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/include/vr/vr_manager/vr_manager.h b/include/vr/vr_manager/vr_manager.h
new file mode 100644
index 0000000..20e4f7c
--- /dev/null
+++ b/include/vr/vr_manager/vr_manager.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VR_MANAGER_H
+#define ANDROID_VR_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// Must be kept in sync with interface defined in IVrStateCallbacks.aidl.
+
+class IVrStateCallbacks : public IInterface {
+public:
+ DECLARE_META_INTERFACE(VrStateCallbacks)
+
+ virtual void onVrStateChanged(bool enabled) = 0;
+};
+
+enum VrStateCallbacksTransaction {
+ ON_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BnVrStateCallbacks : public BnInterface<IVrStateCallbacks> {
+public:
+ status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0) override;
+};
+
+
+// Must be kept in sync with interface defined in IVrManager.aidl.
+
+class IVrManager : public IInterface {
+public:
+ DECLARE_META_INTERFACE(VrManager)
+
+ virtual void registerListener(const sp<IVrStateCallbacks>& cb) = 0;
+ virtual void unregisterListener(const sp<IVrStateCallbacks>& cb) = 0;
+ virtual bool getVrModeState() = 0;
+};
+
+enum VrManagerTransaction {
+ REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
+ UNREGISTER_LISTENER,
+ GET_VR_MODE_STATE,
+};
+
+enum class VrDisplayStateTransaction {
+ ON_DISPLAY_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class IVrDisplayStateService : public IInterface {
+public:
+ DECLARE_META_INTERFACE(VrDisplayStateService)
+
+ virtual void displayAvailable(bool available) = 0;
+};
+
+class BnVrDisplayStateService : public BnInterface<IVrDisplayStateService> {
+public:
+ status_t onTransact(uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0) override;
+};
+
+}; // namespace android
+
+#endif // ANDROID_VR_MANAGER_H
diff --git a/libs/vr/.clang-format b/libs/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/libs/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp
new file mode 100644
index 0000000..e8176cf
--- /dev/null
+++ b/libs/vr/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+ "*",
+]
diff --git a/libs/vr/CPPLINT.cfg b/libs/vr/CPPLINT.cfg
new file mode 100644
index 0000000..87fb641
--- /dev/null
+++ b/libs/vr/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha
diff --git a/libs/vr/libbufferhub/Android.mk b/libs/vr/libbufferhub/Android.mk
new file mode 100644
index 0000000..467f69f
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.mk
@@ -0,0 +1,57 @@
+# 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 := \
+ buffer_hub_client.cpp \
+ buffer_hub_rpc.cpp \
+ ion_buffer.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libchrome \
+ libdvrcommon \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ libhardware \
+ liblog \
+ libui \
+ libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libbufferhub\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libbufferhub
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := bufferhub_tests.cpp
+LOCAL_STATIC_LIBRARIES := libbufferhub $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhub_tests
+include $(BUILD_NATIVE_TEST)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
new file mode 100644
index 0000000..146780e
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,417 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <cutils/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/platform_defines.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace {
+
+constexpr int kUncachedBlobUsageFlags = GRALLOC_USAGE_SW_READ_RARELY |
+ GRALLOC_USAGE_SW_WRITE_RARELY |
+ GRALLOC_USAGE_PRIVATE_UNCACHED;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path)},
+ id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+ Status<LocalChannelHandle> status =
+ InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+ ALOGE_IF(!status,
+ "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+ status.GetErrorMessage().c_str());
+ return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+ ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+ if (!IonBuffer::GetGrallocModule())
+ return -EIO;
+
+ Status<std::vector<NativeBufferHandle<LocalHandle>>> status =
+ InvokeRemoteMethod<BufferHubRPC::GetBuffers>();
+ if (!status) {
+ ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffers: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ } else if (status.get().empty()) {
+ ALOGE(
+ "BufferHubBuffer::ImportBuffer: Expected to receive at least one "
+ "buffer handle but got zero!");
+ return -EIO;
+ }
+
+ auto buffer_handles = status.take();
+
+ // Stash the buffer id to replace the value in id_. All sub-buffers of a
+ // buffer hub buffer have the same id.
+ const int new_id = buffer_handles[0].id();
+
+ // Import all of the buffers.
+ std::vector<IonBuffer> ion_buffers;
+ for (auto& handle : buffer_handles) {
+ const size_t i = &handle - buffer_handles.data();
+ ALOGD_IF(
+ TRACE,
+ "BufferHubBuffer::ImportBuffer: i=%zu id=%d FdCount=%zu IntCount=%zu",
+ i, handle.id(), handle.FdCount(), handle.IntCount());
+
+ IonBuffer buffer;
+ const int ret = handle.Import(&buffer);
+ if (ret < 0)
+ return ret;
+
+ ion_buffers.emplace_back(std::move(buffer));
+ }
+
+ // If all imports succeed, replace the previous buffers and id.
+ slices_ = std::move(ion_buffers);
+ id_ = new_id;
+ return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+ ATRACE_NAME("BufferHubBuffer::Poll");
+ pollfd p = {event_fd(), POLLIN, 0};
+ return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+ void** address, size_t index) {
+ return slices_[index].Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock(size_t index) { return slices_[index].Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+ int width = static_cast<int>(size);
+ int height = 1;
+ constexpr int usage = GRALLOC_USAGE_SW_READ_RARELY |
+ GRALLOC_USAGE_SW_WRITE_RARELY |
+ GRALLOC_USAGE_PRIVATE_UNCACHED;
+ int ret = Lock(usage, 0, 0, width, height, addr);
+ if (ret == 0)
+ Unlock();
+ return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+ int width = static_cast<int>(size);
+ int height = 1;
+ constexpr int usage =
+ GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_PRIVATE_UNCACHED;
+ int ret = Lock(usage, 0, 0, width, height, addr);
+ if (ret == 0)
+ Unlock();
+ return ret;
+}
+
+BufferConsumer::BufferConsumer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+ LocalChannelHandle channel) {
+ ATRACE_NAME("BufferConsumer::Import");
+ ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
+ return BufferConsumer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence) {
+ return Acquire(ready_fence, nullptr, 0);
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
+ size_t meta_size_bytes) {
+ ATRACE_NAME("BufferConsumer::Acquire");
+ LocalFence fence;
+ auto return_value =
+ std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
+ auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
+ &return_value, meta_size_bytes);
+ if (status && ready_fence)
+ *ready_fence = fence.take();
+ return status ? 0 : -status.error();
+}
+
+int BufferConsumer::Release(const LocalHandle& release_fence) {
+ ATRACE_NAME("BufferConsumer::Release");
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+ BorrowedFence(release_fence.Borrow())));
+}
+
+int BufferConsumer::ReleaseAsync() {
+ ATRACE_NAME("BufferConsumer::ReleaseAsync");
+ return ReturnStatusOrError(
+ SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int BufferConsumer::Discard() { return Release(LocalHandle()); }
+
+int BufferConsumer::SetIgnore(bool ignore) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
+}
+
+BufferProducer::BufferProducer(int width, int height, int format, int usage,
+ size_t metadata_size, size_t slice_count)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: fd=%d width=%d height=%d format=%d "
+ "usage=%d, metadata_size=%zu, slice_count=%zu",
+ event_fd(), width, height, format, usage, metadata_size,
+ slice_count);
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, usage, metadata_size, slice_count);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, int width, int height, int format,
+ int usage, size_t meta_size_bytes,
+ size_t slice_count)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
+ "group_id=%d width=%d height=%d format=%d usage=%d, "
+ "meta_size_bytes=%zu, slice_count=%zu",
+ event_fd(), name.c_str(), user_id, group_id, width, height, format,
+ usage, meta_size_bytes, slice_count);
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+ slice_count);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create/get persistent "
+ "buffer \"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(int usage, size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%d size=%zu", usage,
+ size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t meta_size_bytes = 0;
+ const size_t slice_count = 1;
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, usage, meta_size_bytes, slice_count);
+ if (!status) {
+ ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, int usage, size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: name=%s user_id=%d group=%d "
+ "usage=%d size=%zu",
+ name.c_str(), user_id, group_id, usage, size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t meta_size_bytes = 0;
+ const size_t slice_count = 1;
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+ slice_count);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create persistent "
+ "buffer \"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+ "\"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+ size_t meta_size_bytes) {
+ ATRACE_NAME("BufferProducer::Post");
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+ BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+ ATRACE_NAME("BufferProducer::Gain");
+ auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+ if (!status)
+ return -status.error();
+ if (release_fence)
+ *release_fence = status.take().take();
+ return 0;
+}
+
+int BufferProducer::GainAsync() {
+ ATRACE_NAME("BufferProducer::GainAsync");
+ return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+ LocalChannelHandle channel) {
+ ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+ return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+ int group_id) {
+ ATRACE_NAME("BufferProducer::MakePersistent");
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+ group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+ ATRACE_NAME("BufferProducer::RemovePersistence");
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreateUncachedBlob(
+ size_t size) {
+ return BufferProducer::Create(kUncachedBlobUsageFlags, size);
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreatePersistentUncachedBlob(
+ const std::string& name, int user_id, int group_id, size_t size) {
+ return BufferProducer::Create(name, user_id, group_id,
+ kUncachedBlobUsageFlags, size);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000..9a67faa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000..cb45dbe
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,219 @@
+#include <android/native_window.h>
+#include <base/posix/eintr_wrapper.h>
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <mutex>
+#include <thread>
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ // Check that consumers can spawn other consumers.
+ std::unique_ptr<BufferConsumer> c2 =
+ BufferConsumer::Import(c->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+
+ EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+ // Both consumers should be triggered.
+ EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+ EXPECT_LT(0, HANDLE_EINTR(c->Poll(10)));
+ EXPECT_LT(0, HANDLE_EINTR(c2->Poll(10)));
+
+ uint64_t context;
+ LocalHandle fence;
+ EXPECT_LE(0, c->Acquire(&fence, &context));
+ EXPECT_EQ(kContext, context);
+ EXPECT_GE(0, HANDLE_EINTR(c->Poll(0)));
+
+ EXPECT_LE(0, c2->Acquire(&fence, &context));
+ EXPECT_EQ(kContext, context);
+ EXPECT_GE(0, HANDLE_EINTR(c2->Poll(0)));
+
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+ EXPECT_EQ(0, c2->Discard());
+
+ EXPECT_LE(0, HANDLE_EINTR(p->Poll(0)));
+ EXPECT_EQ(0, p->Gain(&fence));
+ EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ Metadata m = {1, 3};
+ EXPECT_EQ(0, p->Post(LocalHandle(), m));
+ EXPECT_LE(0, HANDLE_EINTR(c->Poll(10)));
+
+ LocalHandle fence;
+ Metadata m2 = {};
+ EXPECT_EQ(0, c->Acquire(&fence, &m2));
+ EXPECT_EQ(m.field1, m2.field1);
+ EXPECT_EQ(m.field2, m2.field2);
+
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_LT(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+ EXPECT_GE(0, HANDLE_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ Metadata m = {1, 3};
+ EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+ LocalHandle fence;
+ int64_t sequence;
+ EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+ LocalHandle fence;
+ EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+ std::unique_ptr<BufferProducer> p =
+ BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ LocalHandle fence;
+
+ EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+ EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+ std::unique_ptr<BufferProducer> p =
+ BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ // Record the original buffer id for later comparison.
+ const int buffer_id = p->id();
+
+ auto c = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_NE(nullptr, c);
+
+ EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+ // Close the connection to the producer. This should not affect the consumer.
+ p = nullptr;
+
+ LocalHandle fence;
+ EXPECT_EQ(0, c->Acquire(&fence));
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+
+ // Attempt to reconnect to the persistent buffer.
+ p = BufferProducer::Create("TestPersistentBuffer");
+ ASSERT_NE(nullptr, p);
+ EXPECT_EQ(buffer_id, p->id());
+ EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ // Close the connection to the producer.
+ p = nullptr;
+
+ // Mismatch the params.
+ p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+ kHeight, kFormat, kUsage);
+ ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ LocalHandle fence;
+ auto c = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_NE(nullptr, c);
+ EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+ // Test that removing persistence and closing the producer orphans the
+ // consumer.
+ EXPECT_EQ(0, p->RemovePersistence());
+ p = nullptr;
+
+ EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..b6ff5b6
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,313 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+ using LocalHandle = pdx::LocalHandle;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ template <typename T>
+ using Status = pdx::Status<T>;
+
+ // Create a new consumer channel that is attached to the producer. Returns
+ // a file descriptor for the new channel or a negative error code.
+ Status<LocalChannelHandle> CreateConsumer();
+
+ // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+ int Poll(int timeout_ms);
+
+ // Locks the area specified by (x, y, width, height) for a specific usage. If
+ // the usage is software then |addr| will be updated to point to the address
+ // of the buffer in virtual memory. The caller should only access/modify the
+ // pixels in the specified area. anything else is undefined behavior.
+ int Lock(int usage, int x, int y, int width, int height, void** addr,
+ size_t index);
+
+ // Must be called after Lock() when the caller has finished changing the
+ // buffer.
+ int Unlock(size_t index);
+
+ // Helper for when index is 0.
+ int Lock(int usage, int x, int y, int width, int height, void** addr) {
+ return Lock(usage, x, y, width, height, addr, 0);
+ }
+
+ // Helper for when index is 0.
+ int Unlock() { return Unlock(0); }
+
+ // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadWritePointer(size_t size, void** addr);
+
+ // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+ // Returns a dup'd file descriptor for accessing the blob shared memory. The
+ // caller takes ownership of the file descriptor and must close it or pass on
+ // ownership. Some GPU API extensions can take file descriptors to bind shared
+ // memory gralloc buffers to GPU buffer objects.
+ LocalHandle GetBlobFd() const {
+ // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+ // vendors and this is the wrong fd, late-latching and EDS will very clearly
+ // stop working and we will need to correct this. The alternative is to use
+ // a GL context in the pose service to allocate this buffer or to use the
+ // ION API directly instead of gralloc.
+ return LocalHandle(dup(native_handle()->data[0]));
+ }
+
+ using Client::event_fd;
+ native_handle_t* native_handle() const {
+ return const_cast<native_handle_t*>(slices_[0].handle());
+ }
+ // If index is greater than or equal to slice_count(), the result is
+ // undefined.
+ native_handle_t* native_handle(size_t index) const {
+ return const_cast<native_handle_t*>(slices_[index].handle());
+ }
+
+ IonBuffer* buffer() { return &slices_[0]; }
+ // If index is greater than or equal to slice_count(), the result is
+ // undefined.
+ IonBuffer* slice(size_t index) { return &slices_[index]; }
+
+ int slice_count() const { return static_cast<int>(slices_.size()); }
+ int id() const { return id_; }
+
+ // The following methods return settings of the first buffer. Currently,
+ // it is only possible to create multi-buffer BufferHubBuffers with the same
+ // settings.
+ int width() const { return slices_[0].width(); }
+ int height() const { return slices_[0].height(); }
+ int stride() const { return slices_[0].stride(); }
+ int format() const { return slices_[0].format(); }
+ int usage() const { return slices_[0].usage(); }
+
+ protected:
+ explicit BufferHubBuffer(LocalChannelHandle channel);
+ explicit BufferHubBuffer(const std::string& endpoint_path);
+ virtual ~BufferHubBuffer();
+
+ // Initialization helper.
+ int ImportBuffer();
+
+ private:
+ BufferHubBuffer(const BufferHubBuffer&) = delete;
+ void operator=(const BufferHubBuffer&) = delete;
+
+ // Global id for the buffer that is consistent across processes. It is meant
+ // for logging and debugging purposes only and should not be used for lookup
+ // or any other functional purpose as a security precaution.
+ int id_;
+
+ // A BufferHubBuffer may contain multiple slices of IonBuffers with same
+ // configurations.
+ std::vector<IonBuffer> slices_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+ // Create a buffer designed to hold arbitrary bytes that can be read and
+ // written from CPU, GPU and DSP. The buffer is mapped uncached so that CPU
+ // reads and writes are predictable.
+ static std::unique_ptr<BufferProducer> CreateUncachedBlob(size_t size);
+
+ // Creates a persistent uncached buffer with the given name and access.
+ static std::unique_ptr<BufferProducer> CreatePersistentUncachedBlob(
+ const std::string& name, int user_id, int group_id, size_t size);
+
+ // Imports a bufferhub producer channel, assuming ownership of its handle.
+ static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<BufferProducer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+ // |meta| are passed unaltered to the consumers. The producer must not modify
+ // the buffer until it is re-gained.
+ // This returns zero or a negative unix error code.
+ int Post(const LocalHandle& ready_fence, const void* meta,
+ size_t meta_size_bytes);
+
+ template <typename Meta,
+ typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+ int Post(const LocalHandle& ready_fence) {
+ return Post(ready_fence, nullptr, 0);
+ }
+ template <typename Meta, typename = typename std::enable_if<
+ !std::is_void<Meta>::value>::type>
+ int Post(const LocalHandle& ready_fence, const Meta& meta) {
+ return Post(ready_fence, &meta, sizeof(meta));
+ }
+
+ // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+ // must be waited on before using the buffer. If it is not valid then the
+ // buffer is free for immediate use. This call will only succeed if the buffer
+ // is in the released state.
+ // This returns zero or a negative unix error code.
+ int Gain(LocalHandle* release_fence);
+
+ // Asynchronously marks a released buffer as gained. This method is similar to
+ // the synchronous version above, except that it does not wait for BufferHub
+ // to acknowledge success or failure, nor does it transfer a release fence to
+ // the client. This version may be used in situations where a release fence is
+ // not needed. Because of the asynchronous nature of the underlying message,
+ // no error is returned if this method is called when the buffer is in an
+ // incorrect state. Returns zero if sending the message succeeded, or a
+ // negative errno code otherwise.
+ int GainAsync();
+
+ // Attaches the producer to |name| so that it becomes a persistent buffer that
+ // may be retrieved by name at a later time. This may be used in cases where a
+ // shared memory buffer should persist across the life of the producer process
+ // (i.e. the buffer may be held by clients across a service restart). The
+ // buffer may be associated with a user and/or group id to restrict access to
+ // the buffer. If user_id or group_id is -1 then checks for the respective id
+ // are disabled. If user_id or group_id is 0 then the respective id of the
+ // calling process is used instead.
+ int MakePersistent(const std::string& name, int user_id, int group_id);
+
+ // Removes the persistence of the producer.
+ int RemovePersistence();
+
+ private:
+ friend BASE;
+
+ // Constructors are automatically exposed through BufferProducer::Create(...)
+ // static template methods inherited from ClientBase, which take the same
+ // arguments as the constructors.
+
+ // Constructs a buffer with the given geometry and parameters.
+ BufferProducer(int width, int height, int format, int usage,
+ size_t metadata_size = 0, size_t slice_count = 1);
+
+ // Constructs a persistent buffer with the given geometry and parameters and
+ // binds it to |name| in one shot. If a persistent buffer with the same name
+ // and settings already exists and matches the given geometry and parameters,
+ // that buffer is connected to this client instead of creating a new buffer.
+ // If the name matches but the geometry or settings do not match then
+ // construction fails and BufferProducer::Create() returns nullptr.
+ //
+ // Access to the persistent buffer may be restricted by |user_id| and/or
+ // |group_id|; these settings are established only when the buffer is first
+ // created and cannot be changed. A user or group id of -1 disables checks for
+ // that respective id. A user or group id of 0 is substituted with the
+ // effective user or group id of the calling process.
+ BufferProducer(const std::string& name, int user_id, int group_id, int width,
+ int height, int format, int usage, size_t metadata_size = 0,
+ size_t slice_count = 1);
+
+ // Constructs a blob (flat) buffer with the given usage flags.
+ BufferProducer(int usage, size_t size);
+
+ // Constructs a persistent blob (flat) buffer and binds it to |name|.
+ BufferProducer(const std::string& name, int user_id, int group_id, int usage,
+ size_t size);
+
+ // Constructs a channel to persistent buffer by name only. The buffer must
+ // have been previously created or made persistent.
+ explicit BufferProducer(const std::string& name);
+
+ // Imports the given file handle to a producer channel, taking ownership.
+ explicit BufferProducer(LocalChannelHandle channel);
+};
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
+ public:
+ // This call assumes ownership of |fd|.
+ static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<BufferConsumer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+ // This call will only succeed after the fd is signalled. This call may be
+ // performed as an alternative to the Acquire() with metadata. In such cases
+ // the metadata is not read.
+ //
+ // This returns zero or negative unix error code.
+ int Acquire(LocalHandle* ready_fence);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence signaling that the contents of the buffer
+ // are available. This call will only succeed if the buffer is in the posted
+ // state.
+ // Returns zero on success, or a negative errno code otherwise.
+ int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence to wait on until the buffer is ready. This
+ // call will only succeed after the fd is signaled. This returns zero or a
+ // negative unix error code.
+ template <typename Meta>
+ int Acquire(LocalHandle* ready_fence, Meta* meta) {
+ return Acquire(ready_fence, meta, sizeof(*meta));
+ }
+
+ // This should be called after a successful Acquire call. If the fence is
+ // valid the fence determines the buffer usage, otherwise the buffer is
+ // released immediately.
+ // This returns zero or a negative unix error code.
+ int Release(const LocalHandle& release_fence);
+
+ // Asynchronously releases a buffer. Similar to the synchronous version above,
+ // except that it does not wait for BufferHub to reply with success or error,
+ // nor does it transfer a release fence. This version may be used in
+ // situations where a release fence is not needed. Because of the asynchronous
+ // nature of the underlying message, no error is returned if this method is
+ // called when the buffer is in an incorrect state. Returns zero if sending
+ // the message succeeded, or a negative errno code otherwise.
+ int ReleaseAsync();
+
+ // May be called after or instead of Acquire to indicate that the consumer
+ // does not need to access the buffer this cycle. This returns zero or a
+ // negative unix error code.
+ int Discard();
+
+ // When set, this consumer is no longer notified when this buffer is
+ // available. The system behaves as if Discard() is immediately called
+ // whenever the buffer is posted. If ignore is set to true while a buffer is
+ // pending, it will act as if Discard() was also called.
+ // This returns zero or a negative unix error code.
+ int SetIgnore(bool ignore);
+
+ private:
+ friend BASE;
+
+ explicit BufferConsumer(LocalChannelHandle channel);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
new file mode 100644
index 0000000..ed11551
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,214 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+ NativeBufferHandle() { Clear(); }
+ NativeBufferHandle(const IonBuffer& buffer, int id)
+ : id_(id),
+ stride_(buffer.stride()),
+ width_(buffer.width()),
+ height_(buffer.height()),
+ format_(buffer.format()),
+ usage_(buffer.usage()) {
+ // Populate the fd and int vectors: native_handle->data[] is an array of fds
+ // followed by an array of opaque ints.
+ const int fd_count = buffer.handle()->numFds;
+ const int int_count = buffer.handle()->numInts;
+ for (int i = 0; i < fd_count; i++) {
+ fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i]));
+ }
+ for (int i = 0; i < int_count; i++) {
+ opaque_ints_.push_back(buffer.handle()->data[fd_count + i]);
+ }
+ }
+ NativeBufferHandle(NativeBufferHandle&& other) = default;
+
+ // Imports the native handle into the given IonBuffer instance.
+ int Import(IonBuffer* buffer) {
+ // This is annoying, but we need to convert the vector of FileHandles into a
+ // vector of ints for the Import API.
+ std::vector<int> fd_ints;
+ for (const auto& fd : fds_)
+ fd_ints.push_back(fd.Get());
+
+ const int ret = buffer->Import(fd_ints.data(), fd_ints.size(),
+ opaque_ints_.data(), opaque_ints_.size(),
+ width_, height_, stride_, format_, usage_);
+ if (ret < 0)
+ return ret;
+
+ // Import succeeded, release the file handles which are now owned by the
+ // IonBuffer and clear members.
+ for (auto& fd : fds_)
+ fd.Release();
+ opaque_ints_.clear();
+ Clear();
+
+ return 0;
+ }
+
+ int id() const { return id_; }
+ size_t IntCount() const { return opaque_ints_.size(); }
+ size_t FdCount() const { return fds_.size(); }
+
+ private:
+ int id_;
+ int stride_;
+ int width_;
+ int height_;
+ int format_;
+ int usage_;
+ std::vector<int> opaque_ints_;
+ std::vector<FileHandleType> fds_;
+
+ void Clear() { id_ = stride_ = width_ = height_ = format_ = usage_ = -1; }
+
+ PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+ width_, height_, format_, usage_, opaque_ints_,
+ fds_);
+
+ NativeBufferHandle(const NativeBufferHandle&) = delete;
+ void operator=(const NativeBufferHandle&) = delete;
+};
+
+template <typename FileHandleType>
+class FenceHandle {
+ public:
+ FenceHandle() = default;
+ explicit FenceHandle(int fence) : fence_{fence} {}
+ explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {}
+ FenceHandle(FenceHandle&&) = default;
+ FenceHandle& operator=(FenceHandle&&) = default;
+
+ explicit operator bool() const { return fence_.IsValid(); }
+
+ const FileHandleType& get() const { fence_; }
+ FileHandleType&& take() { return std::move(fence_); }
+
+ int get_fd() const { return fence_.Get(); }
+ void close() { fence_.Close(); }
+
+ FenceHandle<pdx::BorrowedHandle> borrow() const {
+ return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow());
+ }
+
+ private:
+ FileHandleType fence_;
+
+ PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_);
+
+ FenceHandle(const FenceHandle&) = delete;
+ void operator=(const FenceHandle&) = delete;
+};
+
+using LocalFence = FenceHandle<pdx::LocalHandle>;
+using BorrowedFence = FenceHandle<pdx::BorrowedHandle>;
+
+// BufferHub Service RPC interface. Defines the endpoints, op codes, and method
+// type signatures supported by bufferhubd.
+struct BufferHubRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/buffer_hub/client";
+
+ // |BufferHubQueue| will keep track of at most this value of buffers.
+ // Attempts at runtime to increase the number of buffers past this
+ // will fail. Note that the value is in sync with |android::BufferQueue|, so
+ // that slot id can be shared between |android::dvr::BufferHubQueueProducer|
+ // and |android::BufferQueueProducer| which both implements the same
+ // interface: |android::IGraphicBufferProducer|.
+ static constexpr size_t kMaxQueueCapacity =
+ android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ // Op codes.
+ enum {
+ kOpCreateBuffer = 0,
+ kOpCreatePersistentBuffer,
+ kOpGetPersistentBuffer,
+ kOpGetBuffer,
+ kOpGetBuffers,
+ kOpNewConsumer,
+ kOpProducerMakePersistent,
+ kOpProducerRemovePersistence,
+ kOpProducerPost,
+ kOpProducerGain,
+ kOpConsumerAcquire,
+ kOpConsumerRelease,
+ kOpConsumerSetIgnore,
+ kOpCreateProducerQueue,
+ kOpCreateConsumerQueue,
+ kOpProducerQueueAllocateBuffers,
+ kOpProducerQueueDetachBuffer,
+ kOpConsumerQueueImportBuffers,
+ };
+
+ // Aliases.
+ using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using LocalHandle = pdx::LocalHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
+ int(int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count));
+ PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer,
+ int(const std::string& name, int user_id, int group_id,
+ int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count));
+ PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+ int(const std::string& name));
+ PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+ NativeBufferHandle<LocalHandle>(unsigned index));
+ PDX_REMOTE_METHOD(GetBuffers, kOpGetBuffers,
+ std::vector<NativeBufferHandle<LocalHandle>>(Void));
+ PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
+ int(const std::string& name, int user_id, int group_id));
+ PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence,
+ int(Void));
+ PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost,
+ int(LocalFence acquire_fence, MetaData));
+ PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
+ PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
+ std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+ PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
+ int(LocalFence release_fence));
+ PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, int(bool ignore));
+
+ // Buffer Queue Methods.
+ PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
+ int(size_t meta_size_bytes, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask));
+ PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
+ std::pair<LocalChannelHandle, size_t>(Void));
+ PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
+ kOpProducerQueueAllocateBuffers,
+ std::vector<std::pair<LocalChannelHandle, size_t>>(
+ int width, int height, int format, int usage,
+ size_t slice_count, size_t buffer_count));
+ PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer,
+ int(size_t slot));
+ PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
+ std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUB_RPC_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..8125c54
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,108 @@
+#ifndef ANDROID_DVR_ION_BUFFER_H_
+#define ANDROID_DVR_ION_BUFFER_H_
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+ IonBuffer();
+ IonBuffer(int width, int height, int format, int usage);
+ IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage);
+ IonBuffer(buffer_handle_t handle, int width, int height, int layer_count,
+ int stride, int layer_stride, int format, int usage);
+ ~IonBuffer();
+
+ IonBuffer(IonBuffer&& other);
+ IonBuffer& operator=(IonBuffer&& other);
+
+ // Frees the underlying native handle and leaves the instance initialized to
+ // empty.
+ void FreeHandle();
+
+ // Allocates a new native handle with the given parameters, freeing the
+ // previous native handle if necessary. Returns 0 on success or a negative
+ // errno code otherwise. If allocation fails the previous native handle is
+ // left intact.
+ int Alloc(int width, int height, int format, int usage);
+
+ // Resets the underlying native handle and parameters, freeing the previous
+ // native handle if necessary.
+ void Reset(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage);
+
+ // Like Reset but also registers the native handle, which is necessary for
+ // native handles received over IPC. Returns 0 on success or a negative errno
+ // code otherwise. If import fails the previous native handle is left intact.
+ int Import(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage);
+
+ // Like Reset but imports a native handle from raw fd and int arrays. Returns
+ // 0 on success or a negative errno code otherwise. If import fails the
+ // previous native handle is left intact.
+ int Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, int width, int height, int stride, int format,
+ int usage);
+
+ // Duplicates the native handle underlying |other| and then imports it. This
+ // is useful for creating multiple, independent views of the same Ion/Gralloc
+ // buffer. Returns 0 on success or a negative errno code otherwise. If
+ // duplication or import fail the previous native handle is left intact.
+ int Duplicate(const IonBuffer* other);
+
+ int Lock(int usage, int x, int y, int width, int height, void** address);
+ int LockYUV(int usage, int x, int y, int width, int height,
+ struct android_ycbcr* yuv);
+ int Unlock();
+
+ buffer_handle_t handle() const { return handle_; }
+ int width() const { return width_; }
+ int height() const { return height_; }
+ int layer_count() const { return layer_count_; }
+ int stride() const { return stride_; }
+ int layer_stride() const { return layer_stride_; }
+ int format() const { return format_; }
+ int usage() const { return usage_; }
+
+ static gralloc_module_t const* GetGrallocModule() {
+ GrallocInit();
+ return gralloc_module_;
+ }
+
+ static alloc_device_t* GetGrallocDevice() {
+ GrallocInit();
+ return gralloc_device_;
+ }
+
+ private:
+ buffer_handle_t handle_;
+ int width_;
+ int height_;
+ int layer_count_;
+ int stride_;
+ int layer_stride_;
+ int format_;
+ int usage_;
+ bool locked_;
+ bool needs_unregister_;
+
+ void Replace(buffer_handle_t handle, int width, int height, int layer_count,
+ int stride, int layer_stride, int format, int usage,
+ bool needs_unregister);
+
+ static void GrallocInit();
+ static gralloc_module_t const* gralloc_module_;
+ static alloc_device_t* gralloc_device_;
+
+ IonBuffer(const IonBuffer&) = delete;
+ void operator=(const IonBuffer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_ION_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
new file mode 100644
index 0000000..f6c24d9
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,226 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <cutils/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+ : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+ android::LightRefBase<NativeBuffer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+ : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+ ANativeWindowBuffer::width = buffer->width();
+ ANativeWindowBuffer::height = buffer->height();
+ ANativeWindowBuffer::stride = buffer->stride();
+ ANativeWindowBuffer::format = buffer->format();
+ ANativeWindowBuffer::usage = buffer->usage();
+ handle = buffer_->handle();
+ }
+
+ virtual ~NativeBuffer() {}
+
+ std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+ int fence() const { return fence_.Get(); }
+
+ void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+ friend class android::LightRefBase<NativeBuffer>;
+
+ std::shared_ptr<IonBuffer> buffer_;
+ pdx::LocalHandle fence_;
+
+ NativeBuffer(const NativeBuffer&) = delete;
+ void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducerSlice is an implementation of ANativeWindowBuffer backed
+// by a buffer slice of a BufferProducer.
+class NativeBufferProducerSlice
+ : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferProducerSlice,
+ android::LightRefBase<NativeBufferProducerSlice>> {
+ public:
+ NativeBufferProducerSlice(const std::shared_ptr<BufferProducer>& buffer,
+ int buffer_index)
+ : BASE(), buffer_(buffer) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ handle = buffer_->native_handle(buffer_index);
+ }
+
+ virtual ~NativeBufferProducerSlice() {}
+
+ private:
+ friend class android::LightRefBase<NativeBufferProducerSlice>;
+
+ std::shared_ptr<BufferProducer> buffer_;
+
+ NativeBufferProducerSlice(const NativeBufferProducerSlice&) = delete;
+ void operator=(NativeBufferProducerSlice&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferProducer,
+ android::LightRefBase<NativeBufferProducer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+ EGLDisplay display, uint32_t surface_buffer_index)
+ : BASE(),
+ buffer_(buffer),
+ surface_buffer_index_(surface_buffer_index),
+ display_(display) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ handle = buffer_->native_handle();
+ for (int i = 0; i < buffer->slice_count(); ++i) {
+ // display == null means don't create an EGL image. This is used by our
+ // Vulkan code.
+ slices_.push_back(new NativeBufferProducerSlice(buffer, i));
+ if (display_ != nullptr) {
+ egl_images_.push_back(eglCreateImageKHR(
+ display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ static_cast<ANativeWindowBuffer*>(slices_.back().get()), nullptr));
+ if (egl_images_.back() == EGL_NO_IMAGE_KHR) {
+ ALOGE("NativeBufferProducer: eglCreateImageKHR failed");
+ }
+ }
+ }
+ }
+
+ explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+ : NativeBufferProducer(buffer, nullptr, 0) {}
+
+ virtual ~NativeBufferProducer() {
+ for (EGLImageKHR egl_image : egl_images_) {
+ if (egl_image != EGL_NO_IMAGE_KHR)
+ eglDestroyImageKHR(display_, egl_image);
+ }
+ }
+
+ EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+ std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+ int release_fence() const { return release_fence_.Get(); }
+ uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+ // Return the release fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+ // Post the buffer consumer, closing the acquire and release fences.
+ int Post(int acquire_fence, uint64_t sequence) {
+ release_fence_.Close();
+ return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+ }
+
+ // Gain the buffer producer, closing the previous release fence if valid.
+ int Gain() { return buffer_->Gain(&release_fence_); }
+
+ // Asynchronously gain the buffer, closing the previous release fence.
+ int GainAsync() {
+ release_fence_.Close();
+ return buffer_->GainAsync();
+ }
+
+ private:
+ friend class android::LightRefBase<NativeBufferProducer>;
+
+ std::shared_ptr<BufferProducer> buffer_;
+ pdx::LocalHandle release_fence_;
+ std::vector<android::sp<NativeBufferProducerSlice>> slices_;
+ std::vector<EGLImageKHR> egl_images_;
+ uint32_t surface_buffer_index_;
+ EGLDisplay display_;
+
+ NativeBufferProducer(const NativeBufferProducer&) = delete;
+ void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferConsumer,
+ android::LightRefBase<NativeBufferConsumer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer,
+ int index)
+ : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ CHECK(buffer_->slice_count() > index);
+ handle = buffer_->slice(index)->handle();
+ }
+
+ explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+ : NativeBufferConsumer(buffer, 0) {}
+
+ virtual ~NativeBufferConsumer() {}
+
+ std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+ int acquire_fence() const { return acquire_fence_.Get(); }
+ uint64_t sequence() const { return sequence_; }
+
+ // Return the acquire fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+ // Acquire the underlying buffer consumer, closing the previous acquire fence
+ // if valid.
+ int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+ // Release the buffer consumer, closing the acquire and release fences if
+ // valid.
+ int Release(int release_fence) {
+ acquire_fence_.Close();
+ sequence_ = 0;
+ return buffer_->Release(pdx::LocalHandle(release_fence));
+ }
+
+ private:
+ friend class android::LightRefBase<NativeBufferConsumer>;
+
+ std::shared_ptr<BufferConsumer> buffer_;
+ pdx::LocalHandle acquire_fence_;
+ uint64_t sequence_;
+
+ NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+ void operator=(NativeBufferConsumer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..7d20049
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,322 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <cutils/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+namespace android {
+namespace dvr {
+
+gralloc_module_t const* IonBuffer::gralloc_module_ = nullptr;
+alloc_device_t* IonBuffer::gralloc_device_ = nullptr;
+
+IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0, 0) {}
+
+IonBuffer::IonBuffer(int width, int height, int format, int usage)
+ : IonBuffer() {
+ Alloc(width, height, format, usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage)
+ : IonBuffer(handle, width, height, 1, stride, 0, format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height,
+ int layer_count, int stride, int layer_stride, int format,
+ int usage)
+ : handle_(handle),
+ width_(width),
+ height_(height),
+ layer_count_(layer_count),
+ stride_(stride),
+ layer_stride_(layer_stride),
+ format_(format),
+ usage_(usage),
+ locked_(false),
+ needs_unregister_(false) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::IonBuffer: handle=%p width=%d height=%d layer_count=%d "
+ "stride=%d layer stride=%d format=%d usage=%d",
+ handle_, width_, height_, layer_count_, stride_, layer_stride_,
+ format_, usage_);
+ GrallocInit();
+}
+
+IonBuffer::~IonBuffer() {
+ ALOGD_IF(TRACE,
+ "IonBuffer::~IonBuffer: handle=%p width=%d height=%d stride=%d "
+ "format=%d usage=%d",
+ handle_, width_, height_, stride_, format_, usage_);
+
+ FreeHandle();
+}
+
+IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() {
+ *this = std::move(other);
+}
+
+IonBuffer& IonBuffer::operator=(IonBuffer&& other) {
+ ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle_,
+ other.handle_);
+
+ if (this != &other) {
+ Replace(other.handle_, other.width_, other.height_, other.layer_count_,
+ other.stride_, other.layer_stride_, other.format_, other.usage_,
+ other.needs_unregister_);
+ locked_ = other.locked_;
+ other.handle_ = nullptr;
+ other.FreeHandle();
+ }
+
+ return *this;
+}
+
+void IonBuffer::FreeHandle() {
+ if (handle_) {
+ // Lock/Unlock don't need to be balanced, but one Unlock is needed to
+ // clean/unmap the buffer. Warn if this didn't happen before freeing the
+ // native handle.
+ ALOGW_IF(locked_,
+ "IonBuffer::FreeHandle: freeing a locked handle!!! handle=%p",
+ handle_);
+
+ if (needs_unregister_) {
+ int ret = gralloc_module_->unregisterBuffer(gralloc_module_, handle_);
+ ALOGE_IF(ret < 0,
+ "IonBuffer::FreeHandle: Failed to unregister handle: %s",
+ strerror(-ret));
+
+ native_handle_close(const_cast<native_handle_t*>(handle_));
+ native_handle_delete(const_cast<native_handle_t*>(handle_));
+ } else {
+ int ret = gralloc_device_->free(gralloc_device_, handle_);
+ if (ret < 0) {
+ ALOGE("IonBuffer::FreeHandle: failed to free buffer: %s",
+ strerror(-ret));
+
+ // Not sure if this is the right thing to do. Attempting to prevent a
+ // memory leak of the native handle.
+ native_handle_close(const_cast<native_handle_t*>(handle_));
+ native_handle_delete(const_cast<native_handle_t*>(handle_));
+ }
+ }
+ }
+
+ // Always re-initialize these members, even if handle_ was nullptr, in case
+ // someone was dumb enough to pass a nullptr handle to the constructor or
+ // Reset.
+ handle_ = nullptr;
+ width_ = 0;
+ height_ = 0;
+ layer_count_ = 0;
+ stride_ = 0;
+ layer_stride_ = 0;
+ format_ = 0;
+ usage_ = 0;
+ locked_ = false;
+ needs_unregister_ = false;
+}
+
+int IonBuffer::Alloc(int width, int height, int format, int usage) {
+ ATRACE_NAME("IonBuffer::Alloc");
+ ALOGD_IF(TRACE, "IonBuffer::Alloc: width=%d height=%d format=%d usage=%d",
+ width, height, format, usage);
+
+ int stride;
+ buffer_handle_t handle;
+
+ int ret = gralloc_device_->alloc(gralloc_device_, width, height, format,
+ usage, &handle, &stride);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Alloc: failed to allocate gralloc buffer: %s",
+ strerror(-ret));
+ return ret;
+ }
+
+ Replace(handle, width, height, 1, stride, 0, format, usage, false);
+ return 0;
+}
+
+void IonBuffer::Replace(buffer_handle_t handle, int width, int height,
+ int layer_count, int stride, int layer_stride,
+ int format, int usage, bool needs_unregister) {
+ FreeHandle();
+
+ handle_ = handle;
+ width_ = width;
+ height_ = height;
+ layer_count_ = layer_count;
+ stride_ = stride;
+ layer_stride_ = layer_stride;
+ format_ = format;
+ usage_ = usage;
+ needs_unregister_ = needs_unregister;
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::Reset: handle=%p width=%d height=%d stride=%d format=%d "
+ "usage=%d",
+ handle, width, height, stride, format, usage);
+
+ Replace(handle, width, height, 1, stride, 0, format, usage, false);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage) {
+ ATRACE_NAME("IonBuffer::Import1");
+ ALOGD_IF(
+ TRACE,
+ "IonBuffer::Import: handle=%p width=%d height=%d stride=%d format=%d "
+ "usage=%d",
+ handle, width, height, stride, format, usage);
+
+ int ret = gralloc_module_->registerBuffer(gralloc_module_, handle);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Import: failed to import handle: %s", strerror(-ret));
+ return ret;
+ }
+
+ Replace(handle, width, height, 1, stride, 0, format, usage, true);
+ return 0;
+}
+
+int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, int width, int height, int stride,
+ int format, int usage) {
+ ATRACE_NAME("IonBuffer::Import2");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Import: fd_count=%d int_count=%d width=%d height=%d "
+ "stride=%d format=%d usage=%d",
+ fd_count, int_count, width, height, stride, format, usage);
+
+ if (fd_count < 0 || int_count < 0) {
+ ALOGE("IonBuffer::Import: invalid arguments.");
+ return -EINVAL;
+ }
+
+ native_handle_t* handle = native_handle_create(fd_count, int_count);
+ if (!handle) {
+ ALOGE("IonBuffer::Import: failed to create new native handle.");
+ return -ENOMEM;
+ }
+
+ // Copy fd_array into the first part of handle->data and int_array right
+ // after it.
+ memcpy(handle->data, fd_array, sizeof(int) * fd_count);
+ memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count);
+
+ int ret = Import(handle, width, height, stride, format, usage);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+ strerror(-ret));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+
+ return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+ if (!other->handle())
+ return -EINVAL;
+
+ const int fd_count = other->handle()->numFds;
+ const int int_count = other->handle()->numInts;
+
+ if (fd_count < 0 || int_count < 0)
+ return -EINVAL;
+
+ native_handle_t* handle = native_handle_create(fd_count, int_count);
+ if (!handle) {
+ ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+ return -ENOMEM;
+ }
+
+ // Duplicate the file descriptors from the other native handle.
+ for (int i = 0; i < fd_count; i++)
+ handle->data[i] = dup(other->handle()->data[i]);
+
+ // Copy the ints after the file descriptors.
+ memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+ sizeof(int) * int_count);
+
+ const int ret = Import(handle, other->width(), other->height(),
+ other->stride(), other->format(), other->usage());
+ if (ret < 0) {
+ ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s",
+ strerror(-ret));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+
+ return ret;
+}
+
+int IonBuffer::Lock(int usage, int x, int y, int width, int height,
+ void** address) {
+ ATRACE_NAME("IonBuffer::Lock");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d "
+ "address=%p",
+ handle_, usage, x, y, width, height, address);
+
+ // Lock may be called multiple times; but only one Unlock is required.
+ const int err = gralloc_module_->lock(gralloc_module_, handle_, usage, x, y,
+ width, height, address);
+ if (!err)
+ locked_ = true;
+
+ return err;
+}
+
+int IonBuffer::LockYUV(int usage, int x, int y, int width, int height,
+ struct android_ycbcr* yuv) {
+ ATRACE_NAME("IonBuffer::LockYUV");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d",
+ handle_, usage, x, y, width, height);
+ const int err = gralloc_module_->lock_ycbcr(gralloc_module_, handle_, usage,
+ x, y, width, height, yuv);
+ if (!err)
+ locked_ = true;
+
+ return err;
+}
+
+int IonBuffer::Unlock() {
+ ATRACE_NAME("IonBuffer::Unlock");
+ ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle_);
+
+ // Lock may be called multiple times; but only one Unlock is required.
+ const int err = gralloc_module_->unlock(gralloc_module_, handle_);
+ if (!err)
+ locked_ = false;
+
+ return err;
+}
+
+void IonBuffer::GrallocInit() {
+ static std::once_flag gralloc_flag;
+ std::call_once(gralloc_flag, []() {
+ hw_module_t const* module = nullptr;
+ alloc_device_t* device = nullptr;
+
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ ALOGE_IF(err, "IonBuffer::GrallocInit: failed to find the %s module: %s",
+ GRALLOC_HARDWARE_MODULE_ID, strerror(-err));
+
+ err = gralloc_open(module, &device);
+ ALOGE_IF(err, "IonBuffer::GrallocInit: failed to open gralloc device: %s",
+ strerror(-err));
+
+ gralloc_module_ = reinterpret_cast<gralloc_module_t const*>(module);
+ gralloc_device_ = device;
+ });
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..33816fa
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
@@ -0,0 +1,57 @@
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_ // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+// TODO(jwcai) mock not need for now
+class native_handle_t;
+
+namespace android {
+namespace dvr {
+
+// TODO(jwcai) mock not need for now
+class IonBuffer;
+
+class BufferHubBuffer {
+ public:
+ MOCK_METHOD1(Poll, int(int timeout_ms));
+ MOCK_METHOD6(Lock, bool(int usage, int x, int y, int width, int height,
+ void** addr));
+ MOCK_METHOD0(Unlock, int());
+
+ MOCK_METHOD0(native_handle, native_handle_t*());
+ MOCK_METHOD0(buffer, IonBuffer*());
+ MOCK_METHOD0(event_fd, int());
+
+ MOCK_METHOD0(id, int());
+ MOCK_METHOD0(width, int());
+ MOCK_METHOD0(height, int());
+ MOCK_METHOD0(stride, int());
+ MOCK_METHOD0(format, int());
+ MOCK_METHOD0(usage, int());
+};
+
+class BufferProducer : public BufferHubBuffer {
+ public:
+ // Note that static method |CreateBuffer| and |Import| are not mocked
+ // here, they are just implementation details and thus not needed.
+ MOCK_METHOD2(Post, int(int ready_fence, uint64_t sequence));
+ MOCK_METHOD1(Gain, int(int* release_fence));
+
+ static BufferProducer* staticObject;
+};
+
+class BufferConsumer : public BufferHubBuffer {
+ public:
+ MOCK_METHOD2(Acquire, int(int* ready_fence, uint64_t* sequence));
+ MOCK_METHOD1(Release, int(int release_fence));
+ MOCK_METHOD0(Discard, int());
+ MOCK_METHOD3(DoAcquire,
+ int(int* ready_fence, void* meta, size_t meta_size_bytes));
+
+ static BufferConsumer* staticObject;
+};
+
+} // namespace dvr
+} // namespace android
+#endif // LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_ //NOLINT
diff --git a/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
new file mode 100644
index 0000000..9674c7c
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
@@ -0,0 +1,23 @@
+config("gralloc_config") {
+ include_dirs = [ "." ]
+}
+
+static_library("gralloc") {
+ testonly = true
+
+ sources = [
+ "gralloc.cpp",
+ "gralloc.h",
+ ]
+
+ include_dirs = [
+ "//system/core/include",
+ "//hardware/libhardware/include",
+ ]
+
+ public_deps = [
+ "//dreamos/external/gmock",
+ ]
+
+ public_configs = [ ":gralloc_config" ]
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
new file mode 100644
index 0000000..4a923ec
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
@@ -0,0 +1,68 @@
+#include <gralloc_mock.h>
+#include <hardware/gralloc.h>
+
+static alloc_device_t sdevice;
+
+static int local_registerBuffer(struct gralloc_module_t const*,
+ buffer_handle_t handle) {
+ return GrallocMock::staticObject->registerBuffer(handle);
+}
+
+static int local_unregisterBuffer(struct gralloc_module_t const*,
+ buffer_handle_t handle) {
+ return GrallocMock::staticObject->unregisterBuffer(handle);
+}
+
+static int local_unlock(struct gralloc_module_t const*,
+ buffer_handle_t handle) {
+ return GrallocMock::staticObject->unlock(handle);
+}
+
+static int local_lock(struct gralloc_module_t const*, buffer_handle_t handle,
+ int usage, int l, int t, int w, int h, void** vaddr) {
+ return GrallocMock::staticObject->lock(handle, usage, l, t, w, h, vaddr);
+}
+
+static int local_alloc(struct alloc_device_t*, int w, int h, int format,
+ int usage, buffer_handle_t* handle, int* stride) {
+ return GrallocMock::staticObject->alloc(w, h, format, usage, handle, stride);
+}
+
+static int local_free(struct alloc_device_t*, buffer_handle_t handle) {
+ return GrallocMock::staticObject->free(handle);
+}
+
+static int local_open(const struct hw_module_t*, const char*,
+ struct hw_device_t** device) {
+ sdevice.alloc = local_alloc;
+ sdevice.free = local_free;
+ *device = reinterpret_cast<hw_device_t*>(&sdevice);
+ return 0;
+}
+
+static hw_module_methods_t smethods;
+
+static gralloc_module_t smodule;
+
+int hw_get_module(const char*, const struct hw_module_t** module) {
+ smodule.registerBuffer = local_registerBuffer;
+ smodule.unregisterBuffer = local_unregisterBuffer;
+ smodule.lock = local_lock;
+ smodule.unlock = local_unlock;
+ smethods.open = local_open;
+ smodule.common.methods = &smethods;
+ *module = reinterpret_cast<hw_module_t*>(&smodule);
+ return 0;
+}
+
+int native_handle_close(const native_handle_t* handle) {
+ return GrallocMock::staticObject->native_handle_close(handle);
+}
+
+int native_handle_delete(native_handle_t* handle) {
+ return GrallocMock::staticObject->native_handle_delete(handle);
+}
+
+native_handle_t* native_handle_create(int numFds, int numInts) {
+ return GrallocMock::staticObject->native_handle_create(numFds, numInts);
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
new file mode 100644
index 0000000..f62f579
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
@@ -0,0 +1,23 @@
+#ifndef LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#define LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/gralloc.h>
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class GrallocMock {
+ public:
+ // Add methods here.
+ MOCK_METHOD1(native_handle_close, int(const native_handle_t*));
+ MOCK_METHOD1(native_handle_delete, int(native_handle_t*));
+ MOCK_METHOD2(native_handle_create, native_handle_t*(int, int));
+ MOCK_METHOD1(registerBuffer, int(buffer_handle_t));
+ MOCK_METHOD1(unregisterBuffer, int(buffer_handle_t));
+ MOCK_METHOD7(lock, int(buffer_handle_t, int, int, int, int, int, void**));
+ MOCK_METHOD1(unlock, int(buffer_handle_t));
+ MOCK_METHOD6(alloc, int(int, int, int, int, buffer_handle_t*, int*));
+ MOCK_METHOD1(free, int(buffer_handle_t));
+ static GrallocMock* staticObject;
+};
+
+#endif // LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
diff --git a/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..fac6db0
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
@@ -0,0 +1,78 @@
+// This file has a big hack, it "mocks" the actual IonBuffer by redefining
+// it with mock methods and using the same header guard to prevent the original
+// definition from being included in the same context.
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBufferMock {
+ public:
+ IonBufferMock() {}
+ MOCK_METHOD0(GetGrallocModuleImpl, gralloc_module_t const*());
+ MOCK_METHOD6(Import, int(buffer_handle_t handle, int width, int height,
+ int stride, int format, int usage));
+ MOCK_METHOD9(Import, int(const int* fd_array, int fd_count,
+ const int* int_array, int int_count, int width,
+ int height, int stride, int format, int usage));
+ MOCK_METHOD6(Lock, int(int usage, int x, int y, int width, int height,
+ void** address));
+ MOCK_METHOD0(Unlock, int());
+ MOCK_CONST_METHOD0(handle, buffer_handle_t());
+ MOCK_CONST_METHOD0(width, int());
+ MOCK_CONST_METHOD0(height, int());
+ MOCK_CONST_METHOD0(layer_count, int());
+ MOCK_CONST_METHOD0(stride, int());
+ MOCK_CONST_METHOD0(layer_stride, int());
+ MOCK_CONST_METHOD0(format, int());
+ MOCK_CONST_METHOD0(usage, int());
+};
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+ IonBuffer() : mock_(new IonBufferMock) {
+ if (initializer) {
+ initializer(mock_.get());
+ }
+ }
+ IonBuffer(IonBuffer&& other) = default;
+ static gralloc_module_t const* GetGrallocModule() {
+ return staticObject->GetGrallocModuleImpl();
+ }
+ int Import(buffer_handle_t handle, int width, int height, int stride,
+ int format, int usage) {
+ return mock_->Import(handle, width, height, stride, format, usage);
+ }
+ int Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, int width, int height, int stride, int format,
+ int usage) {
+ return mock_->Import(fd_array, fd_count, int_array, int_count, width,
+ height, stride, format, usage);
+ }
+ int Lock(int usage, int x, int y, int width, int height, void** address) {
+ return mock_->Lock(usage, x, y, width, height, address);
+ }
+ int Unlock() { return mock_->Unlock(); }
+ buffer_handle_t handle() const { return mock_->handle(); }
+ int width() const { return mock_->width(); }
+ int height() const { return mock_->height(); }
+ int layer_count() const { return mock_->layer_count(); }
+ int stride() const { return mock_->stride(); }
+ int layer_stride() const { return mock_->layer_stride(); }
+ int format() const { return mock_->format(); }
+ int usage() const { return mock_->usage(); }
+ std::unique_ptr<IonBufferMock> mock_;
+ static IonBufferMock* staticObject;
+ static void (*initializer)(IonBufferMock* target);
+};
+
+} // namespace dvr
+} // namespace android
+#endif // LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ - NOLINT
diff --git a/libs/vr/libbufferhub/tests/Android.mk b/libs/vr/libbufferhub/tests/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/Android.mk b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
new file mode 100644
index 0000000..3bfdb7b
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
@@ -0,0 +1,74 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+COMPONENT_TOP := ${LOCAL_PATH}/../..
+
+LOCAL_SRC_FILES := \
+ ion_buffer-test.cpp \
+ ../../ion_buffer.cpp \
+ ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libc \
+ libcutils \
+ libutils \
+ liblog
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock
+
+LOCAL_C_INCLUDES := \
+ ${COMPONENT_TOP}/mocks/gralloc \
+ ${COMPONENT_TOP}/include \
+ $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0 -g
+
+LOCAL_MODULE := ion_buffer-test
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ ion_buffer-test.cpp \
+ ../../ion_buffer.cpp \
+ ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock_host
+
+LOCAL_C_INCLUDES := \
+ ${COMPONENT_TOP}/mocks/gralloc \
+ ${COMPONENT_TOP}/include \
+ $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0
+
+LOCAL_MODULE := ion_buffer-host_test
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_NATIVE_TEST)
+
+.PHONY: dvr_host_native_unit_tests
+dvr_host_native_unit_tests: ion_buffer-host_test
+ifeq (true,$(NATIVE_COVERAGE))
+ ion_buffer-host_test: llvm-cov
+ ion_buffer-test: llvm-cov
+ # This shouldn't be necessary, but the default build with
+ # NATIVE_COVERAGE=true manages to ion_buffer-test without
+ # building llvm-cov (droid is the default target).
+ droid: llvm-cov
+endif
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
new file mode 100644
index 0000000..68f82d7
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
@@ -0,0 +1,375 @@
+#include <gmock/gmock.h>
+#include <gralloc_mock.h>
+#include <gtest/gtest.h>
+#include <private/dvr/ion_buffer.h>
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using android::dvr::IonBuffer;
+
+GrallocMock* GrallocMock::staticObject = nullptr;
+
+namespace {
+
+const int w1 = 100;
+const int h1 = 200;
+const int d1 = 2;
+const int f1 = 1;
+const int u1 = 3;
+const int stride1 = 8;
+const int layer_stride1 = 8;
+native_handle_t handle1;
+const int w2 = 150;
+const int h2 = 300;
+const int d2 = 4;
+const int f2 = 2;
+const int u2 = 5;
+const int stride2 = 4;
+const int layer_stride2 = 4;
+native_handle_t handle2;
+const int kMaxFd = 10;
+const int kMaxInt = 10;
+char handleData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const dataHandle =
+ reinterpret_cast<native_handle_t*>(handleData);
+char refData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const refHandle = reinterpret_cast<native_handle_t*>(refData);
+
+class IonBufferUnitTest : public ::testing::Test {
+ protected:
+ // You can remove any or all of the following functions if its body
+ // is empty.
+
+ IonBufferUnitTest() {
+ GrallocMock::staticObject = new GrallocMock;
+ // You can do set-up work for each test here.
+ // most ServicefsClients will use this initializer. Use as the
+ // default.
+ }
+
+ virtual ~IonBufferUnitTest() {
+ delete GrallocMock::staticObject;
+ GrallocMock::staticObject = nullptr;
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ void SetUp() override {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ void TearDown() override {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+};
+
+void TestIonBufferState(const IonBuffer& buffer, int w, int h, int d, int f,
+ int u, native_handle_t* handle, int stride,
+ int layer_stride) {
+ EXPECT_EQ(buffer.width(), w);
+ EXPECT_EQ(buffer.height(), h);
+ EXPECT_EQ(buffer.layer_count(), d);
+ EXPECT_EQ(buffer.format(), f);
+ EXPECT_EQ(buffer.usage(), u);
+ EXPECT_EQ(buffer.handle(), handle);
+ EXPECT_EQ(buffer.stride(), stride);
+ EXPECT_EQ(buffer.layer_stride(), layer_stride);
+}
+
+TEST_F(IonBufferUnitTest, TestFreeOnDestruction) {
+ // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+ Return(0)));
+ // Set up |free| to be called once.
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ IonBuffer buffer;
+ // First call to |alloc| with |w1...| set up to succeed.
+ int ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+ // Scoped destructor will be called, calling |free| on |handle1|.
+}
+
+TEST_F(IonBufferUnitTest, TestAlloc) {
+ IonBuffer buffer;
+ // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(2)
+ .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+ Return(0)))
+ .WillRepeatedly(Return(-1));
+
+ // Set up |alloc|(|w2...|) to succeed once and fail on others calls.
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w2, h2, f2, u2, _, _))
+ .Times(2)
+ .WillOnce(DoAll(SetArgPointee<4>(&handle2), SetArgPointee<5>(stride2),
+ Return(0)))
+ .WillRepeatedly(Return(-1));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ // First call to |alloc| with |w1...| set up to succeed.
+ int ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+ // First call to |alloc| with |w2...| set up to succeed, |free| should be
+ // called once on |handle1|.
+ ret = buffer.Alloc(w2, h2, f2, u2);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+ // Second call to |alloc| with |w1| is set up to fail.
+ ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+ // |free| on |handle2| should be called here.
+ buffer.FreeHandle();
+ TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+
+ // |alloc| is set up to fail.
+ ret = buffer.Alloc(w2, h2, f2, u2);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+TEST_F(IonBufferUnitTest, TestReset) {
+ IonBuffer buffer;
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ buffer.Reset(&handle1, w1, h1, stride1, f1, u1);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+ buffer.Reset(&handle2, w2, h2, stride2, f2, u2);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestImport1) {
+ IonBuffer buffer;
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle1))
+ .Times(3)
+ .WillOnce(Return(0))
+ .WillRepeatedly(Return(-1));
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle2))
+ .Times(3)
+ .WillOnce(Return(0))
+ .WillOnce(Return(-1))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle1))
+ .Times(1)
+ .WillOnce(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle1))
+ .Times(1);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle1))
+ .Times(1);
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+ SetArgPointee<5>(stride1), Return(0)));
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle2))
+ .Times(2)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle2))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle2))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+ ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+ ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+ buffer.FreeHandle();
+ ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+native_handle_t* native_handle_create_impl(int nFds, int nInts) {
+ if ((nFds + nInts) > (kMaxFd + kMaxInt))
+ return nullptr;
+ dataHandle->version = sizeof(native_handle_t);
+ dataHandle->numFds = nFds;
+ dataHandle->numInts = nInts;
+ for (int i = 0; i < nFds + nInts; i++)
+ dataHandle->data[i] = 0;
+ return dataHandle;
+}
+
+TEST_F(IonBufferUnitTest, TestImport2) {
+ IonBuffer buffer;
+ int ints[] = {211, 313, 444};
+ int fds[] = {-1, -1};
+ int ni = sizeof(ints) / sizeof(ints[0]);
+ int nfd = sizeof(fds) / sizeof(fds[0]);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+ .Times(3)
+ .WillOnce(Return(nullptr))
+ .WillRepeatedly(Invoke(native_handle_create_impl));
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+ .Times(2)
+ .WillOnce(Return(-1))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Import(fds, -1, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, -1, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+ EXPECT_EQ(dataHandle->numFds, nfd);
+ EXPECT_EQ(dataHandle->numInts, ni);
+ for (int i = 0; i < nfd; i++)
+ EXPECT_EQ(dataHandle->data[i], fds[i]);
+ for (int i = 0; i < ni; i++)
+ EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+ buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestDuplicate) {
+ IonBuffer buffer;
+ IonBuffer ref;
+ int ints[] = {211, 313, 444};
+ int fds[] = {-1, -1};
+ int ni = sizeof(ints) / sizeof(ints[0]);
+ int nfd = sizeof(fds) / sizeof(fds[0]);
+
+ for (int i = 0; i < nfd; i++) {
+ refHandle->data[i] = fds[i];
+ }
+ for (int i = 0; i < ni; i++) {
+ refHandle->data[i + nfd] = ints[i];
+ }
+
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillRepeatedly(DoAll(SetArgPointee<4>(refHandle),
+ SetArgPointee<5>(stride1), Return(0)));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+ .Times(3)
+ .WillOnce(Return(nullptr))
+ .WillRepeatedly(Invoke(native_handle_create_impl));
+ EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+ .Times(2)
+ .WillOnce(Return(-1))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+ .Times(2);
+ EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*GrallocMock::staticObject, free(refHandle))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ ret = ref.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ refHandle->numFds = -1;
+ refHandle->numInts = 0;
+ ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ refHandle->numFds = nfd;
+ refHandle->numInts = ni;
+ ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Duplicate(&ref);
+ EXPECT_LT(ret, 0);
+ ret = buffer.Duplicate(&ref);
+ EXPECT_EQ(ret, 0);
+ TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+ EXPECT_EQ(dataHandle->numFds, nfd);
+ EXPECT_EQ(dataHandle->numInts, ni);
+ for (int i = 0; i < nfd; i++)
+ EXPECT_LT(dataHandle->data[i], 0);
+ for (int i = 0; i < ni; i++)
+ EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+ buffer.FreeHandle();
+ ref.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestLockUnlock) {
+ IonBuffer buffer;
+ const int x = 12;
+ const int y = 24;
+ const int value1 = 17;
+ const int value2 = 25;
+ void* addr1;
+ void** addr = &addr1;
+
+ EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+ .Times(1)
+ .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+ SetArgPointee<5>(stride1), Return(0)));
+ EXPECT_CALL(*GrallocMock::staticObject,
+ lock(&handle1, u2, x, y, w2, h2, addr))
+ .Times(1)
+ .WillRepeatedly(Return(value1));
+ EXPECT_CALL(*GrallocMock::staticObject, unlock(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(value2));
+ EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+ .Times(1)
+ .WillRepeatedly(Return(0));
+
+ int ret = buffer.Alloc(w1, h1, f1, u1);
+ EXPECT_EQ(ret, 0);
+ ret = buffer.Lock(u2, x, y, w2, h2, addr);
+ EXPECT_EQ(ret, value1);
+ ret = buffer.Unlock();
+ EXPECT_EQ(ret, value2);
+ buffer.FreeHandle();
+}
+
+} // namespace
diff --git a/libs/vr/libbufferhubqueue/Android.mk b/libs/vr/libbufferhubqueue/Android.mk
new file mode 100644
index 0000000..46b83e7
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/Android.mk
@@ -0,0 +1,50 @@
+# 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 := \
+ buffer_hub_queue_client.cpp \
+ buffer_hub_queue_core.cpp \
+ buffer_hub_queue_consumer.cpp \
+ buffer_hub_queue_producer.cpp \
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libbufferhub \
+ libchrome \
+ libdvrcommon \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libbase \
+ libbinder \
+ libcutils \
+ libhardware \
+ liblog \
+ libui \
+ libutils \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libbufferhubqueue
+include $(BUILD_STATIC_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
new file mode 100644
index 0000000..4fbfcf6
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -0,0 +1,414 @@
+#include "include/private/dvr/buffer_hub_queue_client.h"
+
+#include <base/logging.h>
+#include <sys/epoll.h>
+
+#include <array>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+
+namespace android {
+namespace dvr {
+
+BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle,
+ size_t meta_size)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ meta_size_(meta_size),
+ meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
+ buffers_(BufferHubQueue::kMaxQueueCapacity),
+ available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+ capacity_(0) {
+ Initialize();
+}
+
+BufferHubQueue::BufferHubQueue(const std::string& endpoint_path,
+ size_t meta_size)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path)},
+ meta_size_(meta_size),
+ meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
+ buffers_(BufferHubQueue::kMaxQueueCapacity),
+ available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+ capacity_(0) {
+ Initialize();
+}
+
+void BufferHubQueue::Initialize() {
+ int ret = epoll_fd_.Create();
+ if (ret < 0) {
+ LOG(ERROR) << "BufferHubQueue::BufferHubQueue: Failed to create epoll fd:"
+ << strerror(-ret);
+ return;
+ }
+
+ epoll_event event = {.events = EPOLLIN | EPOLLET,
+ .data = {.u64 = static_cast<uint64_t>(
+ BufferHubQueue::kEpollQueueEventIndex)}};
+ ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to register ConsumerQueue into epoll event: "
+ << strerror(-ret);
+ }
+}
+
+std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
+ Status<std::pair<LocalChannelHandle, size_t>> status =
+ InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
+
+ if (!status) {
+ LOG(ERROR) << "Cannot create ConsumerQueue: " << status.GetErrorMessage();
+ return nullptr;
+ }
+
+ auto return_value = status.take();
+
+ VLOG(1) << "CreateConsumerQueue: meta_size_bytes=" << return_value.second;
+ return ConsumerQueue::Create(std::move(return_value.first),
+ return_value.second);
+}
+
+bool BufferHubQueue::WaitForBuffers(int timeout) {
+ std::array<epoll_event, kMaxEvents> events;
+
+ while (count() == 0) {
+ int ret = epoll_fd_.Wait(events.data(), events.size(), timeout);
+
+ if (ret == 0) {
+ VLOG(1) << "Wait on epoll returns nothing before timeout.";
+ return false;
+ }
+
+ if (ret < 0 && ret != -EINTR) {
+ LOG(ERROR) << "Failed to wait for buffers:" << strerror(-ret);
+ return false;
+ }
+
+ const int num_events = ret;
+
+ // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
+ // one for each buffer, in the queue and one extra event for the queue
+ // client itself.
+ for (int i = 0; i < num_events; i++) {
+ int64_t index = static_cast<int64_t>(events[i].data.u64);
+
+ VLOG(1) << "New BufferHubQueue event " << i << ": index=" << index;
+
+ if (is_buffer_event_index(index) && (events[i].events & EPOLLIN)) {
+ auto buffer = buffers_[index];
+ ret = OnBufferReady(buffer);
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to set buffer ready:" << strerror(-ret);
+ continue;
+ }
+ Enqueue(buffer, index);
+ } else if (is_buffer_event_index(index) &&
+ (events[i].events & EPOLLHUP)) {
+ // This maybe caused by producer replacing an exising buffer slot.
+ // Currently the epoll FD is cleaned up when the replacement consumer
+ // client is imported.
+ LOG(WARNING) << "Receives EPOLLHUP at slot: " << index;
+ } else if (is_queue_event_index(index) && (events[i].events & EPOLLIN)) {
+ // Note that after buffer imports, if |count()| still returns 0, epoll
+ // wait will be tried again to acquire the newly imported buffer.
+ ret = OnBufferAllocated();
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to import buffer:" << strerror(-ret);
+ continue;
+ }
+ } else {
+ LOG(WARNING) << "Unknown event " << i << ": u64=" << index
+ << ": events=" << events[i].events;
+ }
+ }
+ }
+
+ return true;
+}
+
+int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf,
+ size_t slot) {
+ if (is_full()) {
+ // TODO(jwcai) Move the check into Producer's AllocateBuffer and consumer's
+ // import buffer.
+ LOG(ERROR) << "BufferHubQueue::AddBuffer queue is at maximum capacity: "
+ << capacity_;
+ return -E2BIG;
+ }
+
+ if (buffers_[slot] != nullptr) {
+ // Replace the buffer if the slot is preoccupied. This could happen when the
+ // producer side replaced the slot with a newly allocated buffer. Detach the
+ // buffer and set up with the new one.
+ DetachBuffer(slot);
+ }
+
+ epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}};
+ const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buf->event_fd(), &event);
+ if (ret < 0) {
+ LOG(ERROR)
+ << "BufferHubQueue::AddBuffer: Failed to add buffer to epoll set:"
+ << strerror(-ret);
+ return ret;
+ }
+
+ buffers_[slot] = buf;
+ capacity_++;
+ return 0;
+}
+
+int BufferHubQueue::DetachBuffer(size_t slot) {
+ auto& buf = buffers_[slot];
+ if (buf == nullptr) {
+ LOG(ERROR) << "BufferHubQueue::DetachBuffer: Invalid slot: " << slot;
+ return -EINVAL;
+ }
+
+ const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr);
+ if (ret < 0) {
+ LOG(ERROR) << "BufferHubQueue::DetachBuffer: Failed to detach buffer from "
+ "epoll set:"
+ << strerror(-ret);
+ return ret;
+ }
+
+ buffers_[slot] = nullptr;
+ capacity_--;
+ return 0;
+}
+
+void BufferHubQueue::Enqueue(std::shared_ptr<BufferHubBuffer> buf,
+ size_t slot) {
+ if (count() == capacity_) {
+ LOG(ERROR) << "Buffer queue is full!";
+ return;
+ }
+
+ // Set slot buffer back to vector.
+ // TODO(jwcai) Here have to dynamically allocate BufferInfo::metadata due to
+ // the limitation of the RingBuffer we are using. Would be better to refactor
+ // that.
+ BufferInfo buffer_info(slot, meta_size_);
+ // Swap buffer into vector.
+ std::swap(buffer_info.buffer, buf);
+ // Swap metadata loaded during onBufferReady into vector.
+ std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+ available_buffers_.Append(std::move(buffer_info));
+}
+
+std::shared_ptr<BufferHubBuffer> BufferHubQueue::Dequeue(int timeout,
+ size_t* slot,
+ void* meta) {
+ VLOG(1) << "Dequeue: count=" << count() << ", timeout=" << timeout;
+
+ if (count() == 0 && !WaitForBuffers(timeout))
+ return nullptr;
+
+ std::shared_ptr<BufferHubBuffer> buf;
+ BufferInfo& buffer_info = available_buffers_.Front();
+
+ // Report current pos as the output slot.
+ std::swap(buffer_info.slot, *slot);
+ // Swap buffer from vector to be returned later.
+ std::swap(buffer_info.buffer, buf);
+ // Swap metadata from vector into tmp so that we can write out to |meta|.
+ std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+ available_buffers_.PopFront();
+
+ if (!buf) {
+ LOG(ERROR) << "Dequeue: Buffer to be dequeued is nullptr";
+ return nullptr;
+ }
+
+ if (meta) {
+ std::copy(meta_buffer_tmp_.get(), meta_buffer_tmp_.get() + meta_size_,
+ reinterpret_cast<uint8_t*>(meta));
+ }
+
+ return buf;
+}
+
+ProducerQueue::ProducerQueue(size_t meta_size)
+ : ProducerQueue(meta_size, 0, 0, 0, 0) {}
+
+ProducerQueue::ProducerQueue(LocalChannelHandle handle, size_t meta_size)
+ : BASE(std::move(handle), meta_size) {}
+
+ProducerQueue::ProducerQueue(size_t meta_size, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask)
+ : BASE(BufferHubRPC::kClientPath, meta_size) {
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+ meta_size_, usage_set_mask, usage_clear_mask, usage_deny_set_mask,
+ usage_deny_clear_mask);
+ if (!status) {
+ LOG(ERROR)
+ << "ProducerQueue::ProducerQueue: Failed to create producer queue: %s"
+ << status.GetErrorMessage();
+ Close(-status.error());
+ return;
+ }
+}
+
+int ProducerQueue::AllocateBuffer(int width, int height, int format, int usage,
+ size_t slice_count, size_t* out_slot) {
+ if (out_slot == nullptr) {
+ LOG(ERROR) << "Parameter out_slot cannot be null.";
+ return -EINVAL;
+ }
+
+ if (is_full()) {
+ LOG(ERROR) << "ProducerQueue::AllocateBuffer queue is at maximum capacity: "
+ << capacity();
+ return -E2BIG;
+ }
+
+ const size_t kBufferCount = 1U;
+
+ Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+ InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+ width, height, format, usage, slice_count, kBufferCount);
+ if (!status) {
+ LOG(ERROR) << "ProducerQueue::AllocateBuffer failed to create producer "
+ "buffer through BufferHub.";
+ return -status.error();
+ }
+
+ auto buffer_handle_slots = status.take();
+ CHECK_EQ(buffer_handle_slots.size(), kBufferCount)
+ << "BufferHubRPC::ProducerQueueAllocateBuffers should return one and "
+ "only one buffer handle.";
+
+ // We only allocate one buffer at a time.
+ auto& buffer_handle = buffer_handle_slots[0].first;
+ size_t buffer_slot = buffer_handle_slots[0].second;
+ VLOG(1) << "ProducerQueue::AllocateBuffer, new buffer, channel_handle: "
+ << buffer_handle.value();
+
+ *out_slot = buffer_slot;
+ return AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+ buffer_slot);
+}
+
+int ProducerQueue::AddBuffer(const std::shared_ptr<BufferProducer>& buf,
+ size_t slot) {
+ // For producer buffer, we need to enqueue the newly added buffer
+ // immediately. Producer queue starts with all buffers in available state.
+ const int ret = BufferHubQueue::AddBuffer(buf, slot);
+ if (ret < 0)
+ return ret;
+
+ Enqueue(buf, slot);
+ return 0;
+}
+
+int ProducerQueue::DetachBuffer(size_t slot) {
+ Status<int> status =
+ InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot);
+ if (!status) {
+ LOG(ERROR) << "ProducerQueue::DetachBuffer failed to detach producer "
+ "buffer through BufferHub, error: "
+ << status.GetErrorMessage();
+ return -status.error();
+ }
+
+ return BufferHubQueue::DetachBuffer(slot);
+}
+
+std::shared_ptr<BufferProducer> ProducerQueue::Dequeue(int timeout,
+ size_t* slot) {
+ auto buf = BufferHubQueue::Dequeue(timeout, slot, nullptr);
+ return std::static_pointer_cast<BufferProducer>(buf);
+}
+
+int ProducerQueue::OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) {
+ auto buffer = std::static_pointer_cast<BufferProducer>(buf);
+ return buffer->GainAsync();
+}
+
+ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, size_t meta_size)
+ : BASE(std::move(handle), meta_size) {
+ // TODO(b/34387835) Import consumer queue in case the ProducerQueue we are
+ // based on was not empty.
+}
+
+int ConsumerQueue::ImportBuffers() {
+ Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+ InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
+ if (!status) {
+ LOG(ERROR) << "ConsumerQueue::ImportBuffers failed to import consumer "
+ "buffer through BufferBub, error: "
+ << status.GetErrorMessage();
+ return -status.error();
+ }
+
+ int last_error = 0;
+ int imported_buffers = 0;
+
+ auto buffer_handle_slots = status.take();
+ for (auto& buffer_handle_slot : buffer_handle_slots) {
+ VLOG(1) << "ConsumerQueue::ImportBuffers, new buffer, buffer_handle: "
+ << buffer_handle_slot.first.value();
+
+ std::unique_ptr<BufferConsumer> buffer_consumer =
+ BufferConsumer::Import(std::move(buffer_handle_slot.first));
+ int ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+ if (ret < 0) {
+ LOG(ERROR) << "ConsumerQueue::ImportBuffers failed to add buffer, ret: "
+ << strerror(-ret);
+ last_error = ret;
+ continue;
+ } else {
+ imported_buffers++;
+ }
+ }
+
+ return imported_buffers > 0 ? imported_buffers : last_error;
+}
+
+int ConsumerQueue::AddBuffer(const std::shared_ptr<BufferConsumer>& buf,
+ size_t slot) {
+ // Consumer queue starts with all buffers in unavailable state.
+ return BufferHubQueue::AddBuffer(buf, slot);
+}
+
+std::shared_ptr<BufferConsumer> ConsumerQueue::Dequeue(int timeout,
+ size_t* slot, void* meta,
+ size_t meta_size) {
+ if (meta_size != meta_size_) {
+ LOG(ERROR) << "metadata size (" << meta_size
+ << ") for the dequeuing buffer does not match metadata size ("
+ << meta_size_ << ") for the queue.";
+ return nullptr;
+ }
+ auto buf = BufferHubQueue::Dequeue(timeout, slot, meta);
+ return std::static_pointer_cast<BufferConsumer>(buf);
+}
+
+int ConsumerQueue::OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) {
+ auto buffer = std::static_pointer_cast<BufferConsumer>(buf);
+ LocalHandle fence;
+ return buffer->Acquire(&fence, meta_buffer_tmp_.get(), meta_size_);
+}
+
+int ConsumerQueue::OnBufferAllocated() {
+ const int ret = ImportBuffers();
+ if (ret == 0) {
+ LOG(WARNING) << "No new buffer can be imported on buffer allocated event.";
+ } else if (ret < 0) {
+ LOG(ERROR) << "Failed to import buffers on buffer allocated event.";
+ }
+ VLOG(1) << "Imported " << ret << " consumer buffers.";
+ return ret;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
new file mode 100644
index 0000000..1ea3994
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
@@ -0,0 +1,11 @@
+#include "include/private/dvr/buffer_hub_queue_consumer.h"
+
+namespace android {
+namespace dvr {
+
+BufferHubQueueConsumer::BufferHubQueueConsumer(
+ const std::shared_ptr<BufferHubQueueCore>& core)
+ : core_(core) {}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
new file mode 100644
index 0000000..3fc0600
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -0,0 +1,93 @@
+#include "include/private/dvr/buffer_hub_queue_core.h"
+
+namespace android {
+namespace dvr {
+
+/* static */
+std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create() {
+ auto core = std::shared_ptr<BufferHubQueueCore>(new BufferHubQueueCore());
+ core->producer_ = ProducerQueue::Create<BufferMetadata>();
+ return core;
+}
+
+/* static */
+std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create(
+ const std::shared_ptr<ProducerQueue>& producer) {
+ if (producer->metadata_size() != sizeof(BufferMetadata)) {
+ LOG(ERROR)
+ << "BufferHubQueueCore::Create producer's metadata size is "
+ << "different than the size of BufferHubQueueCore::BufferMetadata";
+ return nullptr;
+ }
+
+ auto core = std::shared_ptr<BufferHubQueueCore>(new BufferHubQueueCore());
+ core->producer_ = producer;
+ return core;
+}
+
+BufferHubQueueCore::BufferHubQueueCore()
+ : generation_number_(0),
+ dequeue_timeout_ms_(BufferHubQueue::kNoTimeOut),
+ unique_id_(getUniqueId()) {}
+
+status_t BufferHubQueueCore::AllocateBuffer(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage,
+ size_t slice_count) {
+ size_t slot;
+
+ // Allocate new buffer through BufferHub and add it into |producer_| queue for
+ // bookkeeping.
+ if (producer_->AllocateBuffer(width, height, format, usage, slice_count,
+ &slot) < 0) {
+ LOG(ERROR) << "Failed to allocate new buffer in BufferHub.";
+ return NO_MEMORY;
+ }
+
+ auto buffer_producer = producer_->GetBuffer(slot);
+
+ CHECK(buffer_producer != nullptr) << "Failed to get buffer producer at slot: "
+ << slot;
+
+ // Allocating a new buffer, |buffers_[slot]| should be in initial state.
+ CHECK(buffers_[slot].mGraphicBuffer == nullptr) << "AllocateBuffer: slot "
+ << slot << " is not empty.";
+
+ // Create new GraphicBuffer based on the newly created |buffer_producer|. Here
+ // we have to cast |buffer_handle_t| to |native_handle_t|, it's OK because
+ // internally, GraphicBuffer is still an |ANativeWindowBuffer| and |handle|
+ // is still type of |buffer_handle_t| and bears const property.
+ sp<GraphicBuffer> graphic_buffer(new GraphicBuffer(
+ buffer_producer->width(), buffer_producer->height(),
+ buffer_producer->format(),
+ 1, /* layer count */
+ buffer_producer->usage(),
+ buffer_producer->stride(),
+ const_cast<native_handle_t*>(buffer_producer->buffer()->handle()),
+ false));
+
+ CHECK_EQ(NO_ERROR, graphic_buffer->initCheck())
+ << "Failed to init GraphicBuffer.";
+ buffers_[slot].mBufferProducer = buffer_producer;
+ buffers_[slot].mGraphicBuffer = graphic_buffer;
+
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueCore::DetachBuffer(size_t slot) {
+ // Detach the buffer producer via BufferHubRPC.
+ int ret = producer_->DetachBuffer(slot);
+ if (ret < 0) {
+ LOG(ERROR) << "BufferHubQueueCore::DetachBuffer failed through RPC, ret="
+ << strerror(-ret);
+ return ret;
+ }
+
+ // Reset in memory objects related the the buffer.
+ buffers_[slot].mBufferProducer = nullptr;
+ buffers_[slot].mGraphicBuffer = nullptr;
+ buffers_[slot].mBufferState.detachProducer();
+ return NO_ERROR;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
new file mode 100644
index 0000000..93d7307
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -0,0 +1,390 @@
+#include "include/private/dvr/buffer_hub_queue_producer.h"
+
+namespace android {
+namespace dvr {
+
+BufferHubQueueProducer::BufferHubQueueProducer(
+ const std::shared_ptr<BufferHubQueueCore>& core)
+ : core_(core), req_buffer_count_(kInvalidBufferCount) {}
+
+status_t BufferHubQueueProducer::requestBuffer(int slot,
+ sp<GraphicBuffer>* buf) {
+ VLOG(1) << "requestBuffer: slot=" << slot;;
+
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+
+ if (slot < 0 || slot >= req_buffer_count_) {
+ LOG(ERROR) << "requestBuffer: slot index " << slot << " out of range [0, "
+ << req_buffer_count_ << ")";
+ return BAD_VALUE;
+ } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+ LOG(ERROR) << "requestBuffer: slot " << slot
+ << " is not owned by the producer (state = "
+ << core_->buffers_[slot].mBufferState.string() << " )";
+ return BAD_VALUE;
+ }
+
+ core_->buffers_[slot].mRequestBufferCalled = true;
+ *buf = core_->buffers_[slot].mGraphicBuffer;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
+ int max_dequeued_buffers) {
+ VLOG(1) << "setMaxDequeuedBufferCount: max_dequeued_buffers="
+ << max_dequeued_buffers;
+
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+
+ if (max_dequeued_buffers <= 0 ||
+ max_dequeued_buffers >
+ static_cast<int>(BufferHubQueue::kMaxQueueCapacity)) {
+ LOG(ERROR) << "setMaxDequeuedBufferCount: " << max_dequeued_buffers
+ << " out of range (0, " << BufferHubQueue::kMaxQueueCapacity
+ << "]";
+ return BAD_VALUE;
+ }
+
+ req_buffer_count_ = max_dequeued_buffers;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAsyncMode(bool /* async */) {
+ LOG(ERROR) << "BufferHubQueueProducer::setAsyncMode not implemented.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::dequeueBuffer(int* out_slot,
+ sp<Fence>* out_fence,
+ uint32_t width, uint32_t height,
+ PixelFormat format,
+ uint32_t usage,
+ FrameEventHistoryDelta* /* outTimestamps */) {
+ VLOG(1) << "dequeueBuffer: w=" << width << ", h=" << height
+ << " format=" << format << ", usage=" << usage;
+
+ status_t ret;
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+
+ if (static_cast<int32_t>(core_->producer_->capacity()) < req_buffer_count_) {
+ // Lazy allocation. When the capacity of |core_->producer_| has not reach
+ // |req_buffer_count_|, allocate new buffer.
+ // TODO(jwcai) To save memory, the really reasonable thing to do is to go
+ // over existing slots and find first existing one to dequeue.
+ ret = core_->AllocateBuffer(width, height, format, usage, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ size_t slot;
+ std::shared_ptr<BufferProducer> buffer_producer;
+
+ for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
+ buffer_producer =
+ core_->producer_->Dequeue(core_->dequeue_timeout_ms_, &slot);
+ if (!buffer_producer)
+ return NO_MEMORY;
+
+ if (static_cast<int>(width) == buffer_producer->width() &&
+ static_cast<int>(height) == buffer_producer->height() &&
+ static_cast<int>(format) == buffer_producer->format()) {
+ // The producer queue returns a buffer producer matches the request.
+ break;
+ }
+
+ // Needs reallocation.
+ // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
+ LOG(INFO) << "dequeueBuffer,: requested buffer (w=" << width
+ << ", h=" << height << ", format=" << format
+ << ") is different from the buffer returned at slot: " << slot
+ << " (w=" << buffer_producer->width()
+ << ", h=" << buffer_producer->height()
+ << ", format=" << buffer_producer->format()
+ << "). Need re-allocattion.";
+ // Mark the slot as reallocating, so that later we can set
+ // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
+ core_->buffers_[slot].mIsReallocating = true;
+
+ // Detach the old buffer once the allocation before allocating its
+ // replacement.
+ core_->DetachBuffer(slot);
+
+ // Allocate a new producer buffer with new buffer configs. Note that if
+ // there are already multiple buffers in the queue, the next one returned
+ // from |core_->producer_->Dequeue| may not be the new buffer we just
+ // reallocated. Retry up to BufferHubQueue::kMaxQueueCapacity times.
+ ret = core_->AllocateBuffer(width, height, format, usage, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ // With the BufferHub backed solution. Buffer slot returned from
+ // |core_->producer_->Dequeue| is guaranteed to avaiable for producer's use.
+ // It's either in free state (if the buffer has never been used before) or
+ // in queued state (if the buffer has been dequeued and queued back to
+ // BufferHubQueue).
+ // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's
+ // model.
+ CHECK(core_->buffers_[slot].mBufferState.isFree() ||
+ core_->buffers_[slot].mBufferState.isQueued())
+ << "dequeueBuffer: slot " << slot << " is not free or queued.";
+
+ core_->buffers_[slot].mBufferState.freeQueued();
+ core_->buffers_[slot].mBufferState.dequeue();
+ VLOG(1) << "dequeueBuffer: slot=" << slot;
+
+ // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
+ // just need to exopose that through |BufferHubQueue| once we need fence.
+ *out_fence = Fence::NO_FENCE;
+ *out_slot = slot;
+ ret = NO_ERROR;
+
+ if (core_->buffers_[slot].mIsReallocating) {
+ ret |= BUFFER_NEEDS_REALLOCATION;
+ core_->buffers_[slot].mIsReallocating = false;
+ }
+
+ return ret;
+}
+
+status_t BufferHubQueueProducer::detachBuffer(int /* slot */) {
+ LOG(ERROR) << "BufferHubQueueProducer::detachBuffer not implemented.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::detachNextBuffer(
+ sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
+ LOG(ERROR) << "BufferHubQueueProducer::detachNextBuffer not implemented.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::attachBuffer(
+ int* /* out_slot */, const sp<GraphicBuffer>& /* buffer */) {
+ // With this BufferHub backed implementation, we assume (for now) all buffers
+ // are allocated and owned by the BufferHub. Thus the attempt of transfering
+ // ownership of a buffer to the buffer queue is intentionally unsupported.
+ LOG(FATAL) << "BufferHubQueueProducer::attachBuffer not supported.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::queueBuffer(int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* /* output */) {
+ VLOG(1) << "queueBuffer: slot " << slot;
+
+ int64_t timestamp;
+ sp<Fence> fence;
+
+ // TODO(jwcai) The following attributes are ignored.
+ bool is_auto_timestamp;
+ android_dataspace data_space;
+ Rect crop(Rect::EMPTY_RECT);
+ int scaling_mode;
+ uint32_t transform;
+
+ input.deflate(×tamp, &is_auto_timestamp, &data_space, &crop,
+ &scaling_mode, &transform, &fence);
+
+ if (fence == nullptr) {
+ LOG(ERROR) << "queueBuffer: fence is NULL";
+ return BAD_VALUE;
+ }
+
+ status_t ret;
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+
+ if (slot < 0 || slot >= req_buffer_count_) {
+ LOG(ERROR) << "queueBuffer: slot index " << slot << " out of range [0, "
+ << req_buffer_count_ << ")";
+ return BAD_VALUE;
+ } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+ LOG(ERROR) << "queueBuffer: slot " << slot
+ << " is not owned by the producer (state = "
+ << core_->buffers_[slot].mBufferState.string() << " )";
+ return BAD_VALUE;
+ }
+
+ // Post the buffer producer with timestamp in the metadata.
+ auto buffer_producer = core_->buffers_[slot].mBufferProducer;
+ LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
+
+ BufferHubQueueCore::BufferMetadata meta_data = {.timestamp = timestamp};
+ buffer_producer->Post(fence_fd, &meta_data, sizeof(meta_data));
+ core_->buffers_[slot].mBufferState.queue();
+
+ // TODO(jwcai) check how to fill in output properly.
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::cancelBuffer(int slot,
+ const sp<Fence>& fence) {
+ VLOG(1) << (__FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+
+ if (slot < 0 || slot >= req_buffer_count_) {
+ LOG(ERROR) << "cancelBuffer: slot index " << slot << " out of range [0, "
+ << req_buffer_count_ << ")";
+ return BAD_VALUE;
+ } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+ LOG(ERROR) << "cancelBuffer: slot " << slot
+ << " is not owned by the producer (state = "
+ << core_->buffers_[slot].mBufferState.string() << " )";
+ return BAD_VALUE;
+ } else if (fence == NULL) {
+ LOG(ERROR) << "cancelBuffer: fence is NULL";
+ return BAD_VALUE;
+ }
+
+ auto buffer_producer = core_->buffers_[slot].mBufferProducer;
+ core_->producer_->Enqueue(buffer_producer, slot);
+ core_->buffers_[slot].mBufferState.cancel();
+ core_->buffers_[slot].mFence = fence;
+ VLOG(1) << "cancelBuffer: slot " << slot;
+
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::query(int what, int* out_value) {
+ VLOG(1) << (__FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+
+ if (out_value == NULL) {
+ LOG(ERROR) << "query: out_value was NULL";
+ return BAD_VALUE;
+ }
+
+ int value = 0;
+ switch (what) {
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = 0;
+ break;
+ case NATIVE_WINDOW_BUFFER_AGE:
+ value = 0;
+ break;
+ // The following queries are currently considered as unsupported.
+ // TODO(jwcai) Need to carefully check the whether they should be
+ // supported after all.
+ case NATIVE_WINDOW_WIDTH:
+ case NATIVE_WINDOW_HEIGHT:
+ case NATIVE_WINDOW_FORMAT:
+ case NATIVE_WINDOW_STICKY_TRANSFORM:
+ case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+ case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+ case NATIVE_WINDOW_DEFAULT_DATASPACE:
+ default:
+ return BAD_VALUE;
+ }
+
+ VLOG(1) << "query: key=" << what << ", v=" << value;
+ *out_value = value;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::connect(
+ const sp<IProducerListener>& /* listener */, int /* api */,
+ bool /* producer_controlled_by_app */, QueueBufferOutput* /* output */) {
+ // Consumer interaction are actually handled by buffer hub, and we need
+ // to maintain consumer operations here. Hence |connect| is a NO-OP.
+ VLOG(1) << (__FUNCTION__);
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::disconnect(int /* api */, DisconnectMode /* mode */) {
+ // Consumer interaction are actually handled by buffer hub, and we need
+ // to maintain consumer operations here. Hence |disconnect| is a NO-OP.
+ VLOG(1) << (__FUNCTION__);
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setSidebandStream(
+ const sp<NativeHandle>& stream) {
+ if (stream != NULL) {
+ // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
+ // metadata.
+ LOG(ERROR) << "SidebandStream is not currently supported.";
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+void BufferHubQueueProducer::allocateBuffers(uint32_t /* width */,
+ uint32_t /* height */,
+ PixelFormat /* format */,
+ uint32_t /* usage */) {
+ // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
+ // of buffers permitted by the current BufferQueue configuration (aka
+ // |req_buffer_count_|).
+ LOG(ERROR) << "BufferHubQueueProducer::allocateBuffers not implemented.";
+}
+
+status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
+ LOG(ERROR) << "BufferHubQueueProducer::allowAllocation not implemented.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setGenerationNumber(
+ uint32_t generation_number) {
+ VLOG(1) << (__FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+ core_->generation_number_ = generation_number;
+ return NO_ERROR;
+}
+
+String8 BufferHubQueueProducer::getConsumerName() const {
+ // BufferHub based implementation could have one to many producer/consumer
+ // relationship, thus |getConsumerName| from the producer side does not
+ // make any sense.
+ LOG(ERROR) << "BufferHubQueueProducer::getConsumerName not supported.";
+ return String8("BufferHubQueue::DummyConsumer");
+}
+
+status_t BufferHubQueueProducer::setSharedBufferMode(
+ bool /* shared_buffer_mode */) {
+ LOG(ERROR) << "BufferHubQueueProducer::setSharedBufferMode not implemented.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setAutoRefresh(bool /* auto_refresh */) {
+ LOG(ERROR) << "BufferHubQueueProducer::setAutoRefresh not implemented.";
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+ VLOG(1) << (__FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(core_->mutex_);
+ core_->dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::getLastQueuedBuffer(
+ sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */,
+ float /*out_transform_matrix*/[16]) {
+ LOG(ERROR) << "BufferHubQueueProducer::getLastQueuedBuffer not implemented.";
+ return INVALID_OPERATION;
+}
+
+void BufferHubQueueProducer::getFrameTimestamps(
+ FrameEventHistoryDelta* /*outDelta*/) {
+ LOG(ERROR) << "BufferHubQueueProducer::getFrameTimestamps not implemented.";
+}
+
+status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
+ VLOG(1) << (__FUNCTION__);
+
+ *out_id = core_->unique_id_;
+ return NO_ERROR;
+}
+
+IBinder* BufferHubQueueProducer::onAsBinder() {
+ // BufferHubQueueProducer is a non-binder implementation of
+ // IGraphicBufferProducer.
+ LOG(WARNING) << "BufferHubQueueProducer::onAsBinder is not supported.";
+ return nullptr;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
new file mode 100644
index 0000000..83e77d4
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -0,0 +1,311 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
+
+#include <gui/BufferQueueDefs.h>
+
+#include <pdx/client.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/epoll_file_descriptor.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+class ConsumerQueue;
+
+// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are
+// automatically re-requeued when released by the remote side.
+class BufferHubQueue : public pdx::Client {
+ public:
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ template <typename T>
+ using Status = pdx::Status<T>;
+
+ virtual ~BufferHubQueue() {}
+ void Initialize();
+
+ // Create a new consumer queue that is attached to the producer. Returns
+ // a new consumer queue client or nullptr on failure.
+ std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
+
+ // Return the number of buffers avaiable for dequeue.
+ size_t count() const { return available_buffers_.GetSize(); }
+
+ // Return the total number of buffers that the queue is tracking.
+ size_t capacity() const { return capacity_; }
+
+ // Return the size of metadata structure associated with this BufferBubQueue.
+ size_t metadata_size() const { return meta_size_; }
+
+ // Return whether the buffer queue is alrady full.
+ bool is_full() const { return available_buffers_.IsFull(); }
+
+ explicit operator bool() const { return epoll_fd_.IsValid(); }
+
+ std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
+ return buffers_[slot];
+ }
+
+ // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer
+ // and |Acquire|'ed for consumer. This is only used for internal bookkeeping.
+ void Enqueue(std::shared_ptr<BufferHubBuffer> buf, size_t slot);
+
+ // |BufferHubQueue| will keep track of at most this value of buffers.
+ static constexpr size_t kMaxQueueCapacity =
+ android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ // Special epoll data field indicating that the epoll event refers to the
+ // queue.
+ static constexpr int64_t kEpollQueueEventIndex = -1;
+
+ // When pass |kNoTimeout| to |Dequeue|, it will block indefinitely without a
+ // timeout.
+ static constexpr int kNoTimeOut = -1;
+
+ protected:
+ BufferHubQueue(LocalChannelHandle channel, size_t meta_size);
+ BufferHubQueue(const std::string& endpoint_path, size_t meta_size);
+
+ // Called by ProducerQueue::AddBuffer and ConsumerQueue::AddBuffer only. to
+ // register a buffer for epoll and internal bookkeeping.
+ int AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot);
+
+ // Called by ProducerQueue::DetachBuffer and ConsumerQueue::DetachBuffer only.
+ // to deregister a buffer for epoll and internal bookkeeping.
+ virtual int DetachBuffer(size_t slot);
+
+ // Dequeue a buffer from the free queue, blocking until one is available. The
+ // timeout argument specifies the number of milliseconds that |Dequeue()| will
+ // block. Specifying a timeout of -1 causes |Dequeue()| to block indefinitely,
+ // while specifying a timeout equal to zero cause |Dequeue()| to return
+ // immediately, even if no buffers are available.
+ std::shared_ptr<BufferHubBuffer> Dequeue(int timeout, size_t* slot,
+ void* meta);
+
+ // Wait for buffers to be released and re-add them to the queue.
+ bool WaitForBuffers(int timeout);
+ virtual int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) = 0;
+
+ // Called when a buffer is allocated remotely.
+ virtual int OnBufferAllocated() = 0;
+
+ // Data members to handle arbitrary metadata passed through BufferHub. It is
+ // fair to enforce that all buffers in the same queue share the same metadata
+ // type. |meta_size_| is used to store the size of metadata on queue creation;
+ // and |meta_buffer_tmp_| is allocated and resized to |meta_size_| on queue
+ // creation to be later used as temporary space so that we can avoid
+ // additional dynamic memory allocation in each |Enqueue| and |Dequeue| call.
+ size_t meta_size_;
+
+ // Here we intentionally choose |unique_ptr<uint8_t[]>| over vector<uint8_t>
+ // to disallow dynamic resizing for stability reasons.
+ std::unique_ptr<uint8_t[]> meta_buffer_tmp_;
+
+ private:
+ static constexpr size_t kMaxEvents = 128;
+
+ // The |u64| data field of an epoll event is interpreted as int64_t:
+ // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
+ // element of |buffers_| as a direct index;
+ static bool is_buffer_event_index(int64_t index) {
+ return index >= 0 &&
+ index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
+ }
+
+ // When |index| == kEpollQueueEventIndex, it refers to the queue itself.
+ static bool is_queue_event_index(int64_t index) {
+ return index == BufferHubQueue::kEpollQueueEventIndex;
+ }
+
+ struct BufferInfo {
+ // A logical slot number that is assigned to a buffer at allocation time.
+ // The slot number remains unchanged during the entire life cycle of the
+ // buffer and should not be confused with the enqueue and dequeue order.
+ size_t slot;
+
+ // A BufferHubBuffer client.
+ std::shared_ptr<BufferHubBuffer> buffer;
+
+ // Metadata associated with the buffer.
+ std::unique_ptr<uint8_t[]> metadata;
+
+ BufferInfo() : BufferInfo(-1, 0) {}
+
+ BufferInfo(size_t slot, size_t metadata_size)
+ : slot(slot),
+ buffer(nullptr),
+ metadata(metadata_size ? new uint8_t[metadata_size] : nullptr) {}
+
+ BufferInfo(BufferInfo&& other)
+ : slot(other.slot),
+ buffer(std::move(other.buffer)),
+ metadata(std::move(other.metadata)) {}
+
+ BufferInfo& operator=(BufferInfo&& other) {
+ slot = other.slot;
+ buffer = std::move(other.buffer);
+ metadata = std::move(other.metadata);
+ return *this;
+ }
+
+ private:
+ BufferInfo(const BufferInfo&) = delete;
+ void operator=(BufferInfo&) = delete;
+ };
+
+ // Buffer queue:
+ // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|.
+ std::vector<std::shared_ptr<BufferHubBuffer>> buffers_;
+
+ // |available_buffers_| uses |dvr::RingBuffer| to implementation queue
+ // sematics. When |Dequeue|, we pop the front element from
+ // |available_buffers_|, and that buffer's reference count will decrease by
+ // one, while another reference in |buffers_| keeps the last reference to
+ // prevent the buffer from being deleted.
+ RingBuffer<BufferInfo> available_buffers_;
+
+ // Keep track with how many buffers have been added into the queue.
+ size_t capacity_;
+
+ // Epoll fd used to wait for BufferHub events.
+ EpollFileDescriptor epoll_fd_;
+
+ BufferHubQueue(const BufferHubQueue&) = delete;
+ void operator=(BufferHubQueue&) = delete;
+};
+
+class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
+ public:
+ template <typename Meta>
+ static std::unique_ptr<ProducerQueue> Create() {
+ return BASE::Create(sizeof(Meta));
+ }
+
+ // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
+ // in |usage_clear_mask| will be automatically masked off. Note that
+ // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
+ // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
+ // allocation through this producer queue shall not have any of the usage bits
+ // in |usage_deny_set_mask| set. Allocation calls violating this will be
+ // rejected. All buffer allocation through this producer queue must have all
+ // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
+ // this will be rejected. Note that |usage_deny_set_mask| and
+ // |usage_deny_clear_mask| shall not conflict with each other. Such
+ // configuration will be treated as invalid input on creation.
+ template <typename Meta>
+ static std::unique_ptr<ProducerQueue> Create(int usage_set_mask,
+ int usage_clear_mask,
+ int usage_deny_set_mask,
+ int usage_deny_clear_mask) {
+ return BASE::Create(sizeof(Meta), usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+ }
+
+ // Import a |ProducerQueue| from a channel handle.
+ template <typename Meta>
+ static std::unique_ptr<ProducerQueue> Import(LocalChannelHandle handle) {
+ return BASE::Create(std::move(handle), sizeof(Meta));
+ }
+
+ // Get a buffer producer. Note that the method doesn't check whether the
+ // buffer slot has a valid buffer that has been allocated already. When no
+ // buffer has been imported before it returns |nullptr|; otherwise it returns
+ // a shared pointer to a |BufferProducer|.
+ std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const {
+ return std::static_pointer_cast<BufferProducer>(
+ BufferHubQueue::GetBuffer(slot));
+ }
+
+ // Allocate producer buffer to populate the queue. Once allocated, a producer
+ // buffer is automatically enqueue'd into the ProducerQueue and available to
+ // use (i.e. in |Gain|'ed mode).
+ // Returns Zero on success and negative error code when buffer allocation
+ // fails.
+ int AllocateBuffer(int width, int height, int format, int usage,
+ size_t buffer_count, size_t* out_slot);
+
+ // Add a producer buffer to populate the queue. Once added, a producer buffer
+ // is available to use (i.e. in |Gain|'ed mode).
+ int AddBuffer(const std::shared_ptr<BufferProducer>& buf, size_t slot);
+
+ // Detach producer buffer from the queue.
+ // Returns Zero on success and negative error code when buffer detach
+ // fails.
+ int DetachBuffer(size_t slot) override;
+
+ // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
+ // and caller should call Post() once it's done writing to release the buffer
+ // to the consumer side.
+ std::shared_ptr<BufferProducer> Dequeue(int timeout, size_t* slot);
+
+ private:
+ friend BASE;
+
+ // Constructors are automatically exposed through ProducerQueue::Create(...)
+ // static template methods inherited from ClientBase, which take the same
+ // arguments as the constructors.
+ explicit ProducerQueue(size_t meta_size);
+ ProducerQueue(LocalChannelHandle handle, size_t meta_size);
+ ProducerQueue(size_t meta_size, int usage_set_mask, int usage_clear_mask,
+ int usage_deny_set_mask, int usage_deny_clear_mask);
+
+ int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) override;
+
+ // Producer buffer is always allocated from the client (i.e. local) side.
+ int OnBufferAllocated() override { return 0; }
+};
+
+class ConsumerQueue : public pdx::ClientBase<ConsumerQueue, BufferHubQueue> {
+ public:
+ // Get a buffer consumer. Note that the method doesn't check whether the
+ // buffer slot has a valid buffer that has been imported already. When no
+ // buffer has been imported before it returns |nullptr|; otherwise it returns
+ // a shared pointer to a |BufferConsumer|.
+ std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const {
+ return std::static_pointer_cast<BufferConsumer>(
+ BufferHubQueue::GetBuffer(slot));
+ }
+
+ // Import newly created buffers from the service side.
+ // Returns number of buffers successfully imported; or negative error code
+ // when buffer import fails.
+ int ImportBuffers();
+
+ // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
+ // mode, and caller should call Releasse() once it's done writing to release
+ // the buffer to the producer side. |meta| is passed along from BufferHub,
+ // The user of BufferProducer is responsible with making sure that the
+ // Dequeue() is done with the corect metadata type and size with those used
+ // when the buffer is orignally created.
+ template <typename Meta>
+ std::shared_ptr<BufferConsumer> Dequeue(int timeout, size_t* slot,
+ Meta* meta) {
+ return Dequeue(timeout, slot, meta, sizeof(*meta));
+ }
+
+ std::shared_ptr<BufferConsumer> Dequeue(int timeout, size_t* slot, void* meta,
+ size_t meta_size);
+
+ private:
+ friend BASE;
+
+ ConsumerQueue(LocalChannelHandle handle, size_t meta_size);
+
+ // Add a consumer buffer to populate the queue. Once added, a consumer buffer
+ // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
+ // will catch the |Post| and |Acquire| the buffer to make it available for
+ // consumer.
+ int AddBuffer(const std::shared_ptr<BufferConsumer>& buf, size_t slot);
+
+ int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) override;
+
+ int OnBufferAllocated() override;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_consumer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_consumer.h
new file mode 100644
index 0000000..8d7bfcc
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_consumer.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CONSUMER_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CONSUMER_H_
+
+#include <private/dvr/buffer_hub_queue_core.h>
+
+#include <gui/IGraphicBufferConsumer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueConsumer : public IGraphicBufferConsumer {
+ public:
+ BufferHubQueueConsumer(const std::shared_ptr<BufferHubQueueCore>& core);
+
+ private:
+ std::shared_ptr<BufferHubQueueCore> core_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CONSUMER_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h
new file mode 100644
index 0000000..ba0c0c5
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h
@@ -0,0 +1,116 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CORE_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CORE_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include <gui/BufferSlot.h>
+#include <utils/Atomic.h>
+#include <utils/String8.h>
+
+#include <mutex>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueCore {
+ private:
+ friend class BufferHubQueueProducer;
+
+ public:
+ // Create a BufferHubQueueCore instance by creating a new producer queue.
+ static std::shared_ptr<BufferHubQueueCore> Create();
+
+ // Create a BufferHubQueueCore instance by importing an existing prodcuer queue.
+ static std::shared_ptr<BufferHubQueueCore> Create(
+ const std::shared_ptr<ProducerQueue>& producer);
+
+ struct BufferMetadata {
+ // Timestamp of the frame.
+ int64_t timestamp;
+ };
+
+ class NativeBuffer
+ : public ANativeObjectBase<ANativeWindowBuffer, NativeBuffer, RefBase> {
+ public:
+ explicit NativeBuffer(const std::shared_ptr<BufferHubBuffer>& buffer)
+ : buffer_(buffer) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ ANativeWindowBuffer::handle = buffer_->buffer()->handle();
+ }
+
+ std::shared_ptr<BufferHubBuffer> buffer() { return buffer_; }
+
+ private:
+ std::shared_ptr<BufferHubBuffer> buffer_;
+ };
+
+ // Get the unique buffer producer queue backing this BufferHubQueue.
+ std::shared_ptr<ProducerQueue> GetProducerQueue() { return producer_; }
+
+ private:
+ using LocalHandle = pdx::LocalHandle;
+
+ struct BufferHubSlot : public BufferSlot {
+ BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
+ // BufferSlot comes from android framework, using m prefix to comply with
+ // the name convention with the reset of data fields from BufferSlot.
+ std::shared_ptr<BufferProducer> mBufferProducer;
+ bool mIsReallocating;
+ };
+
+ static String8 getUniqueName() {
+ static volatile int32_t counter = 0;
+ return String8::format("unnamed-%d-%d", getpid(),
+ android_atomic_inc(&counter));
+ }
+
+ static uint64_t getUniqueId() {
+ static std::atomic<uint32_t> counter{0};
+ static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+ return id | counter++;
+ }
+
+ // Private constructor to force use of |Create|.
+ BufferHubQueueCore();
+
+ // Allocate a new buffer producer through BufferHub.
+ int AllocateBuffer(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t usage, size_t slice_count);
+
+ // Detach a buffer producer through BufferHub.
+ int DetachBuffer(size_t slot);
+
+ // Mutex for thread safety.
+ std::mutex mutex_;
+
+ // |buffers_| stores the buffers that have been dequeued from
+ // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
+ // filled in with the result of |Dequeue|.
+ // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
+ // requested buffer usage or geometry differs from that of the buffer
+ // allocated to a slot.
+ BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity];
+
+ // Concreate implementation backed by BufferHubBuffer.
+ std::shared_ptr<ProducerQueue> producer_;
+
+ // |generation_number_| stores the current generation number of the attached
+ // producer. Any attempt to attach a buffer with a different generation
+ // number will fail.
+ uint32_t generation_number_;
+
+ // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
+ // slot is not yet available. The timeout is stored in milliseconds.
+ int dequeue_timeout_ms_;
+
+ const uint64_t unique_id_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CORE_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
new file mode 100644
index 0000000..5b1a7e0
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
@@ -0,0 +1,118 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+
+#include <private/dvr/buffer_hub_queue_core.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueProducer : public IGraphicBufferProducer {
+ public:
+ BufferHubQueueProducer(const std::shared_ptr<BufferHubQueueCore>& core);
+
+ // See |IGraphicBufferProducer::requestBuffer|
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+
+ // For the BufferHub based implementation. All buffers in the queue are
+ // allowed to be dequeued from the consumer side. It call always returns
+ // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
+ // |max_dequeued_buffers| here can be considered the same as setting queue
+ // capacity.
+ //
+ // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
+ status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
+
+ // See |IGraphicBufferProducer::setAsyncMode|
+ status_t setAsyncMode(bool async) override;
+
+ // See |IGraphicBufferProducer::dequeueBuffer|
+ status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
+ uint32_t height, PixelFormat format,
+ uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override;
+
+ // See |IGraphicBufferProducer::detachBuffer|
+ status_t detachBuffer(int slot) override;
+
+ // See |IGraphicBufferProducer::detachNextBuffer|
+ status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer,
+ sp<Fence>* out_fence) override;
+
+ // See |IGraphicBufferProducer::attachBuffer|
+ status_t attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) override;
+
+ // See |IGraphicBufferProducer::queueBuffer|
+ status_t queueBuffer(int slot, const QueueBufferInput& input,
+ QueueBufferOutput* output) override;
+
+ // See |IGraphicBufferProducer::cancelBuffer|
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+
+ // See |IGraphicBufferProducer::query|
+ status_t query(int what, int* out_value) override;
+
+ // See |IGraphicBufferProducer::connect|
+ status_t connect(const sp<IProducerListener>& listener, int api,
+ bool producer_controlled_by_app,
+ QueueBufferOutput* output) override;
+
+ // See |IGraphicBufferProducer::disconnect|
+ status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override;
+
+ // See |IGraphicBufferProducer::setSidebandStream|
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+
+ // See |IGraphicBufferProducer::allocateBuffers|
+ void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t usage) override;
+
+ // See |IGraphicBufferProducer::allowAllocation|
+ status_t allowAllocation(bool allow) override;
+
+ // See |IGraphicBufferProducer::setGenerationNumber|
+ status_t setGenerationNumber(uint32_t generation_number) override;
+
+ // See |IGraphicBufferProducer::getConsumerName|
+ String8 getConsumerName() const override;
+
+ // See |IGraphicBufferProducer::setSharedBufferMode|
+ status_t setSharedBufferMode(bool shared_buffer_mode) override;
+
+ // See |IGraphicBufferProducer::setAutoRefresh|
+ status_t setAutoRefresh(bool auto_refresh) override;
+
+ // See |IGraphicBufferProducer::setDequeueTimeout|
+ status_t setDequeueTimeout(nsecs_t timeout) override;
+
+ // See |IGraphicBufferProducer::getLastQueuedBuffer|
+ status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer,
+ sp<Fence>* out_fence,
+ float out_transform_matrix[16]) override;
+
+ // See |IGraphicBufferProducer::getFrameTimestamps|
+ void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
+
+ // See |IGraphicBufferProducer::getUniqueId|
+ status_t getUniqueId(uint64_t* out_id) const override;
+
+ protected:
+ IBinder* onAsBinder() override;
+
+ private:
+ using LocalHandle = pdx::LocalHandle;
+
+ static constexpr int kInvalidBufferCount = -1;
+
+ // |core_| holds the actually buffer slots.
+ std::shared_ptr<BufferHubQueueCore> core_;
+
+ // |req_buffer_count_| sets the capacity of the underlying buffer queue.
+ int32_t req_buffer_count_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.mk b/libs/vr/libbufferhubqueue/tests/Android.mk
new file mode 100644
index 0000000..59061e6
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/Android.mk
@@ -0,0 +1,38 @@
+LOCAL_PATH := $(call my-dir)
+
+shared_libraries := \
+ libbase \
+ libbinder \
+ libcutils \
+ libgui \
+ liblog \
+ libhardware \
+ libui \
+ libutils \
+
+static_libraries := \
+ libbufferhubqueue \
+ libbufferhub \
+ libchrome \
+ libdvrcommon \
+ libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := buffer_hub_queue-test.cpp
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+LOCAL_CFLAGS := -DTRACE=0 -O0 -g
+LOCAL_MODULE := buffer_hub_queue-test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := buffer_hub_queue_producer-test.cpp
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+LOCAL_CFLAGS := -DTRACE=0 -O0 -g
+LOCAL_MODULE := buffer_hub_queue_producer-test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
new file mode 100644
index 0000000..841554e
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -0,0 +1,292 @@
+#include <base/logging.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+using pdx::LocalHandle;
+
+namespace {
+
+constexpr int kBufferWidth = 100;
+constexpr int kBufferHeight = 1;
+constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+constexpr int kBufferSliceCount = 1; // number of slices in each buffer
+
+class BufferHubQueueTest : public ::testing::Test {
+ public:
+ template <typename Meta>
+ void CreateQueues(int usage_set_mask = 0, int usage_clear_mask = 0,
+ int usage_deny_set_mask = 0,
+ int usage_deny_clear_mask = 0) {
+ producer_queue_ =
+ ProducerQueue::Create<Meta>(usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+ ASSERT_NE(nullptr, producer_queue_);
+
+ consumer_queue_ = producer_queue_->CreateConsumerQueue();
+ ASSERT_NE(nullptr, consumer_queue_);
+ }
+
+ void AllocateBuffer() {
+ // Create producer buffer.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+ kBufferFormat, kBufferUsage,
+ kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, 0);
+ }
+
+ protected:
+ std::unique_ptr<ProducerQueue> producer_queue_;
+ std::unique_ptr<ConsumerQueue> consumer_queue_;
+};
+
+TEST_F(BufferHubQueueTest, TestDequeue) {
+ const size_t nb_dequeue_times = 16;
+
+ CreateQueues<size_t>();
+
+ // Allocate only one buffer.
+ AllocateBuffer();
+
+ // But dequeue multiple times.
+ for (size_t i = 0; i < nb_dequeue_times; i++) {
+ size_t slot;
+ auto p1 = producer_queue_->Dequeue(0, &slot);
+ ASSERT_NE(nullptr, p1);
+ size_t mi = i;
+ ASSERT_EQ(p1->Post(LocalHandle(), &mi, sizeof(mi)), 0);
+ size_t mo;
+ auto c1 = consumer_queue_->Dequeue(100, &slot, &mo);
+ ASSERT_NE(nullptr, c1);
+ ASSERT_EQ(mi, mo);
+ c1->Release(LocalHandle());
+ }
+}
+
+TEST_F(BufferHubQueueTest, TestProducerConsumer) {
+ const size_t nb_buffer = 16;
+ size_t slot;
+ uint64_t seq;
+
+ CreateQueues<uint64_t>();
+
+ for (size_t i = 0; i < nb_buffer; i++) {
+ AllocateBuffer();
+
+ // Producer queue has all the available buffers on initialize.
+ ASSERT_EQ(producer_queue_->count(), i + 1);
+ ASSERT_EQ(producer_queue_->capacity(), i + 1);
+
+ // Consumer queue has no avaiable buffer on initialize.
+ ASSERT_EQ(consumer_queue_->count(), 0U);
+ // Consumer queue does not import buffers until a dequeue is issued.
+ ASSERT_EQ(consumer_queue_->capacity(), i);
+ // Dequeue returns nullptr since no buffer is ready to consumer, but
+ // this implicitly triggers buffer import and bump up |capacity|.
+ auto consumer_null = consumer_queue_->Dequeue(0, &slot, &seq);
+ ASSERT_EQ(nullptr, consumer_null);
+ ASSERT_EQ(consumer_queue_->capacity(), i + 1);
+ }
+
+ for (size_t i = 0; i < nb_buffer; i++) {
+ // First time, there is no buffer available to dequeue.
+ auto buffer_null = consumer_queue_->Dequeue(0, &slot, &seq);
+ ASSERT_EQ(nullptr, buffer_null);
+
+ // Make sure Producer buffer is Post()'ed so that it's ready to Accquire
+ // in the consumer's Dequeue() function.
+ auto producer = producer_queue_->Dequeue(0, &slot);
+ ASSERT_NE(nullptr, producer);
+
+ uint64_t seq_in = static_cast<uint64_t>(i);
+ ASSERT_EQ(producer->Post({}, &seq_in, sizeof(seq_in)), 0);
+
+ // Second time, the just |Post()|'ed buffer should be dequeued.
+ uint64_t seq_out = 0;
+ auto consumer = consumer_queue_->Dequeue(0, &slot, &seq_out);
+ ASSERT_NE(nullptr, consumer);
+ ASSERT_EQ(seq_in, seq_out);
+ }
+}
+
+struct TestMetadata {
+ char a;
+ int32_t b;
+ int64_t c;
+};
+
+TEST_F(BufferHubQueueTest, TestMetadata) {
+ CreateQueues<TestMetadata>();
+ AllocateBuffer();
+
+ std::vector<TestMetadata> ms = {
+ {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
+
+ for (auto mi : ms) {
+ size_t slot;
+ auto p1 = producer_queue_->Dequeue(0, &slot);
+ ASSERT_NE(nullptr, p1);
+ ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
+ TestMetadata mo;
+ auto c1 = consumer_queue_->Dequeue(0, &slot, &mo);
+ ASSERT_EQ(mi.a, mo.a);
+ ASSERT_EQ(mi.b, mo.b);
+ ASSERT_EQ(mi.c, mo.c);
+ c1->Release(LocalHandle(-1));
+ }
+}
+
+TEST_F(BufferHubQueueTest, TestMetadataMismatch) {
+ CreateQueues<int64_t>();
+ AllocateBuffer();
+
+ int64_t mi = 3;
+ size_t slot;
+ auto p1 = producer_queue_->Dequeue(0, &slot);
+ ASSERT_NE(nullptr, p1);
+ ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
+
+ int32_t mo;
+ // Acquire a buffer with mismatched metadata is not OK.
+ auto c1 = consumer_queue_->Dequeue(0, &slot, &mo);
+ ASSERT_EQ(nullptr, c1);
+}
+
+TEST_F(BufferHubQueueTest, TestEnqueue) {
+ CreateQueues<int64_t>();
+ AllocateBuffer();
+
+ size_t slot;
+ auto p1 = producer_queue_->Dequeue(0, &slot);
+ ASSERT_NE(nullptr, p1);
+
+ int64_t mo;
+ producer_queue_->Enqueue(p1, slot);
+ auto c1 = consumer_queue_->Dequeue(0, &slot, &mo);
+ ASSERT_EQ(nullptr, c1);
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
+ CreateQueues<int64_t>();
+
+ size_t s1;
+ AllocateBuffer();
+ auto p1 = producer_queue_->Dequeue(0, &s1);
+ ASSERT_NE(nullptr, p1);
+
+ // producer queue is exhausted
+ size_t s2;
+ auto p2_null = producer_queue_->Dequeue(0, &s2);
+ ASSERT_EQ(nullptr, p2_null);
+
+ // dynamically add buffer.
+ AllocateBuffer();
+ ASSERT_EQ(producer_queue_->count(), 1U);
+ ASSERT_EQ(producer_queue_->capacity(), 2U);
+
+ // now we can dequeue again
+ auto p2 = producer_queue_->Dequeue(0, &s2);
+ ASSERT_NE(nullptr, p2);
+ ASSERT_EQ(producer_queue_->count(), 0U);
+ // p1 and p2 should have different slot number
+ ASSERT_NE(s1, s2);
+
+ // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
+ // are called. So far consumer_queue_ should be empty.
+ ASSERT_EQ(consumer_queue_->count(), 0U);
+
+ int64_t seq = 1;
+ ASSERT_EQ(p1->Post(LocalHandle(), seq), 0);
+ size_t cs1, cs2;
+ auto c1 = consumer_queue_->Dequeue(0, &cs1, &seq);
+ ASSERT_NE(nullptr, c1);
+ ASSERT_EQ(consumer_queue_->count(), 0U);
+ ASSERT_EQ(consumer_queue_->capacity(), 2U);
+ ASSERT_EQ(cs1, s1);
+
+ ASSERT_EQ(p2->Post(LocalHandle(), seq), 0);
+ auto c2 = consumer_queue_->Dequeue(0, &cs2, &seq);
+ ASSERT_NE(nullptr, c2);
+ ASSERT_EQ(cs2, s2);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageSetMask) {
+ const int set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ CreateQueues<int64_t>(set_mask, 0, 0, 0);
+
+ // When allocation, leave out |set_mask| from usage bits on purpose.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage & ~set_mask,
+ kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, 0);
+
+ auto p1 = producer_queue_->Dequeue(0, &slot);
+ ASSERT_EQ(p1->usage() & set_mask, set_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageClearMask) {
+ const int clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ CreateQueues<int64_t>(0, clear_mask, 0, 0);
+
+ // When allocation, add |clear_mask| into usage bits on purpose.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage | clear_mask,
+ kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, 0);
+
+ auto p1 = producer_queue_->Dequeue(0, &slot);
+ ASSERT_EQ(p1->usage() & clear_mask, 0);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
+ const int deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ CreateQueues<int64_t>(0, 0, deny_set_mask, 0);
+
+ // Now that |deny_set_mask| is illegal, allocation without those bits should
+ // be able to succeed.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage & ~deny_set_mask,
+ kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, 0);
+
+ // While allocation with those bits should fail.
+ ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage | deny_set_mask,
+ kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, -EINVAL);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
+ const int deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ CreateQueues<int64_t>(0, 0, 0, deny_clear_mask);
+
+ // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
+ // mandatory), allocation with those bits should be able to succeed.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferFormat,
+ kBufferUsage | deny_clear_mask, kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, 0);
+
+ // While allocation without those bits should fail.
+ ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferFormat,
+ kBufferUsage & ~deny_clear_mask, kBufferSliceCount, &slot);
+ ASSERT_EQ(ret, -EINVAL);
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
new file mode 100644
index 0000000..5bb121a
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -0,0 +1,23 @@
+#include <private/dvr/buffer_hub_queue_producer.h>
+
+#include <base/logging.h>
+#include <gui/Surface.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class BufferHubQueueProducerTest : public ::testing::Test {};
+
+TEST_F(BufferHubQueueProducerTest, TempTestBufferHubQueueProducer) {
+ auto core = BufferHubQueueCore::Create();
+ sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer(core);
+ sp<Surface> surface = new Surface(producer, true);
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/Android.mk b/libs/vr/libdisplay/Android.mk
new file mode 100644
index 0000000..670bdcd
--- /dev/null
+++ b/libs/vr/libdisplay/Android.mk
@@ -0,0 +1,96 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ native_window.cpp \
+ native_buffer_queue.cpp \
+ display_client.cpp \
+ display_manager_client.cpp \
+ display_manager_client_impl.cpp \
+ display_rpc.cpp \
+ dummy_native_window.cpp \
+ gl_fenced_flush.cpp \
+ graphics.cpp \
+ late_latch.cpp \
+ video_mesh_surface_client.cpp \
+ vsync_client.cpp \
+ vsync_client_api.cpp \
+ screenshot_client.cpp \
+ frame_history.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include \
+ frameworks/native/vulkan/include
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ liblog \
+ libutils \
+ libEGL \
+ libGLESv2 \
+ libvulkan \
+ libui \
+ libgui \
+ libhardware \
+ libsync
+
+staticLibraries := \
+ libchrome \
+ libbufferhub \
+ libbufferhubqueue \
+ libdvrcommon \
+ libdvrgraphics \
+ libsensor \
+ libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+#LOCAL_CPPFLAGS := -UNDEBUG -DDEBUG -O0 -g
+LOCAL_CFLAGS += -DLOG_TAG=\"libdisplay\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdisplay
+include $(BUILD_STATIC_LIBRARY)
+
+
+testFiles := \
+ tests/graphics_app_tests.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := graphics_app_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+ $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+ $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+ libdisplay \
+ $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
new file mode 100644
index 0000000..cfb346d
--- /dev/null
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -0,0 +1,276 @@
+#include "include/private/dvr/display_client.h"
+
+#include <cutils/log.h>
+#include <cutils/native_handle.h>
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/status.h>
+
+#include <mutex>
+
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/native_buffer.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+
+SurfaceClient::SurfaceClient(LocalChannelHandle channel_handle,
+ SurfaceType type)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ type_(type) {}
+
+SurfaceClient::SurfaceClient(const std::string& endpoint_path, SurfaceType type)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path),
+ kInfiniteTimeout},
+ type_(type) {}
+
+int SurfaceClient::GetMetadataBufferFd(LocalHandle* out_fd) {
+ auto buffer_producer = GetMetadataBuffer();
+ if (!buffer_producer)
+ return -ENOMEM;
+
+ *out_fd = buffer_producer->GetBlobFd();
+ return 0;
+}
+
+std::shared_ptr<BufferProducer> SurfaceClient::GetMetadataBuffer() {
+ if (!metadata_buffer_) {
+ auto status = InvokeRemoteMethod<DisplayRPC::GetMetadataBuffer>();
+ if (!status) {
+ ALOGE(
+ "SurfaceClient::AllocateMetadataBuffer: Failed to allocate buffer: "
+ "%s",
+ status.GetErrorMessage().c_str());
+ return nullptr;
+ }
+
+ metadata_buffer_ = BufferProducer::Import(status.take());
+ }
+
+ return metadata_buffer_;
+}
+
+DisplaySurfaceClient::DisplaySurfaceClient(int width, int height, int format,
+ int usage, int flags)
+ : BASE(DisplayRPC::kClientPath, SurfaceTypeEnum::Normal),
+ width_(width),
+ height_(height),
+ format_(format),
+ usage_(usage),
+ flags_(flags),
+ z_order_(0),
+ visible_(true),
+ exclude_from_blur_(false),
+ blur_behind_(true),
+ mapped_metadata_buffer_(nullptr) {
+ auto status = InvokeRemoteMethod<DisplayRPC::CreateSurface>(
+ width, height, format, usage, flags);
+ if (!status) {
+ ALOGE(
+ "DisplaySurfaceClient::DisplaySurfaceClient: Failed to create display "
+ "surface: %s",
+ status.GetErrorMessage().c_str());
+ Close(status.error());
+ }
+}
+
+void DisplaySurfaceClient::SetVisible(bool visible) {
+ SetAttributes({{DisplaySurfaceAttributeEnum::Visible,
+ DisplaySurfaceAttributeValue{visible}}});
+}
+
+void DisplaySurfaceClient::SetZOrder(int z_order) {
+ SetAttributes({{DisplaySurfaceAttributeEnum::ZOrder,
+ DisplaySurfaceAttributeValue{z_order}}});
+}
+
+void DisplaySurfaceClient::SetExcludeFromBlur(bool exclude_from_blur) {
+ SetAttributes({{DisplaySurfaceAttributeEnum::ExcludeFromBlur,
+ DisplaySurfaceAttributeValue{exclude_from_blur}}});
+}
+
+void DisplaySurfaceClient::SetBlurBehind(bool blur_behind) {
+ SetAttributes({{DisplaySurfaceAttributeEnum::BlurBehind,
+ DisplaySurfaceAttributeValue{blur_behind}}});
+}
+
+void DisplaySurfaceClient::SetAttributes(
+ const DisplaySurfaceAttributes& attributes) {
+ Status<int> status =
+ InvokeRemoteMethod<DisplayRPC::SetAttributes>(attributes);
+ if (!status) {
+ ALOGE(
+ "DisplaySurfaceClient::SetAttributes: Failed to set display surface "
+ "attributes: %s",
+ status.GetErrorMessage().c_str());
+ return;
+ }
+
+ // Set the local cached copies of the attributes we care about from the full
+ // set of attributes sent to the display service.
+ for (const auto& attribute : attributes) {
+ const auto& key = attribute.first;
+ const auto* variant = &attribute.second;
+ bool invalid_value = false;
+ switch (key) {
+ case DisplaySurfaceAttributeEnum::Visible:
+ invalid_value =
+ !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
+ break;
+ case DisplaySurfaceAttributeEnum::ZOrder:
+ invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
+ break;
+ case DisplaySurfaceAttributeEnum::ExcludeFromBlur:
+ invalid_value =
+ !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &exclude_from_blur_);
+ break;
+ case DisplaySurfaceAttributeEnum::BlurBehind:
+ invalid_value =
+ !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &blur_behind_);
+ break;
+ }
+
+ if (invalid_value) {
+ ALOGW(
+ "DisplaySurfaceClient::SetAttributes: Failed to set display "
+ "surface attribute '%s' because of incompatible type: %d",
+ DisplaySurfaceAttributeEnum::ToString(key).c_str(), variant->index());
+ }
+ }
+}
+
+std::shared_ptr<BufferProducer> DisplaySurfaceClient::AllocateBuffer(
+ uint32_t* buffer_index) {
+ auto status = InvokeRemoteMethod<DisplayRPC::AllocateBuffer>();
+ if (!status) {
+ ALOGE("DisplaySurfaceClient::AllocateBuffer: Failed to allocate buffer: %s",
+ status.GetErrorMessage().c_str());
+ return nullptr;
+ }
+
+ if (buffer_index)
+ *buffer_index = status.get().first;
+ return BufferProducer::Import(status.take().second);
+}
+
+volatile DisplaySurfaceMetadata* DisplaySurfaceClient::GetMetadataBufferPtr() {
+ if (!mapped_metadata_buffer_) {
+ if (auto buffer_producer = GetMetadataBuffer()) {
+ void* addr = nullptr;
+ const int ret = buffer_producer->GetBlobReadWritePointer(
+ sizeof(DisplaySurfaceMetadata), &addr);
+ if (ret < 0) {
+ ALOGE(
+ "DisplaySurfaceClient::GetMetadataBufferPtr: Failed to map surface "
+ "metadata: %s",
+ strerror(-ret));
+ return nullptr;
+ }
+ mapped_metadata_buffer_ = static_cast<DisplaySurfaceMetadata*>(addr);
+ }
+ }
+
+ return mapped_metadata_buffer_;
+}
+
+LocalChannelHandle DisplaySurfaceClient::CreateVideoMeshSurface() {
+ auto status = InvokeRemoteMethod<DisplayRPC::CreateVideoMeshSurface>();
+ if (!status) {
+ ALOGE(
+ "DisplaySurfaceClient::CreateVideoMeshSurface: Failed to create "
+ "video mesh surface: %s",
+ status.GetErrorMessage().c_str());
+ }
+ return status.take();
+}
+
+DisplayClient::DisplayClient(int* error)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayRPC::kClientPath),
+ kInfiniteTimeout) {
+ if (error)
+ *error = Client::error();
+}
+
+int DisplayClient::GetDisplayMetrics(SystemDisplayMetrics* metrics) {
+ auto status = InvokeRemoteMethod<DisplayRPC::GetMetrics>();
+ if (!status) {
+ ALOGE("DisplayClient::GetDisplayMetrics: Failed to get metrics: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *metrics = status.get();
+ return 0;
+}
+
+pdx::Status<void> DisplayClient::SetViewerParams(const ViewerParams& viewer_params) {
+ auto status = InvokeRemoteMethod<DisplayRPC::SetViewerParams>(viewer_params);
+ if (!status) {
+ ALOGE("DisplayClient::SetViewerParams: Failed to set viewer params: %s",
+ status.GetErrorMessage().c_str());
+ }
+ return status;
+}
+
+int DisplayClient::GetLastFrameEdsTransform(LateLatchOutput* ll_out) {
+ auto status = InvokeRemoteMethod<DisplayRPC::GetEdsCapture>();
+ if (!status) {
+ ALOGE(
+ "DisplayClient::GetLastFrameLateLatch: Failed to get most recent late"
+ " latch: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ if (status.get().size() != sizeof(LateLatchOutput)) {
+ ALOGE(
+ "DisplayClient::GetLastFrameLateLatch: Error expected to receive %zu "
+ "bytes but received %zu",
+ sizeof(LateLatchOutput), status.get().size());
+ return -EIO;
+ }
+
+ *ll_out = *reinterpret_cast<const LateLatchOutput*>(status.get().data());
+ return 0;
+}
+
+int DisplayClient::EnterVrMode() {
+ auto status = InvokeRemoteMethod<DisplayRPC::EnterVrMode>();
+ if (!status) {
+ ALOGE(
+ "DisplayClient::EnterVrMode: Failed to set display service to Vr mode");
+ return -status.error();
+ }
+
+ return 0;
+}
+
+int DisplayClient::ExitVrMode() {
+ auto status = InvokeRemoteMethod<DisplayRPC::ExitVrMode>();
+ if (!status) {
+ ALOGE(
+ "DisplayClient::ExitVrMode: Failed to revert display service from Vr "
+ "mode");
+ return -status.error();
+ }
+
+ return 0;
+}
+
+std::unique_ptr<DisplaySurfaceClient> DisplayClient::CreateDisplaySurface(
+ int width, int height, int format, int usage, int flags) {
+ return DisplaySurfaceClient::Create(width, height, format, usage, flags);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
new file mode 100644
index 0000000..f454b08
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -0,0 +1,109 @@
+#include "include/private/dvr/display_manager_client.h"
+
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_manager_client_impl.h>
+
+using android::dvr::DisplaySurfaceAttributeEnum;
+
+extern "C" {
+
+struct DvrDisplayManagerClient {
+ DvrDisplayManagerClient()
+ : client(android::dvr::DisplayManagerClient::Create()) {}
+ ~DvrDisplayManagerClient() {}
+
+ std::unique_ptr<android::dvr::DisplayManagerClient> client;
+};
+
+struct DvrDisplayManagerClientSurfaceList {
+ DvrDisplayManagerClientSurfaceList(
+ std::vector<android::dvr::DisplaySurfaceInfo> surface_list)
+ : list(std::move(surface_list)) {}
+ ~DvrDisplayManagerClientSurfaceList() {}
+
+ std::vector<android::dvr::DisplaySurfaceInfo> list;
+};
+
+struct DvrDisplayManagerClientSurfaceBuffers {
+ DvrDisplayManagerClientSurfaceBuffers(
+ std::vector<std::unique_ptr<android::dvr::BufferConsumer>> buffer_list)
+ : list(std::move(buffer_list)) {}
+ ~DvrDisplayManagerClientSurfaceBuffers() {}
+
+ std::vector<std::unique_ptr<android::dvr::BufferConsumer>> list;
+};
+
+DvrDisplayManagerClient* dvrDisplayManagerClientCreate() {
+ return new DvrDisplayManagerClient();
+}
+
+void dvrDisplayManagerClientDestroy(DvrDisplayManagerClient* client) {
+ delete client;
+}
+
+int dvrDisplayManagerClientGetSurfaceList(
+ DvrDisplayManagerClient* client,
+ DvrDisplayManagerClientSurfaceList** surface_list) {
+ std::vector<android::dvr::DisplaySurfaceInfo> list;
+ int ret = client->client->GetSurfaceList(&list);
+ if (ret < 0)
+ return ret;
+
+ *surface_list = new DvrDisplayManagerClientSurfaceList(std::move(list));
+ return ret;
+}
+
+void dvrDisplayManagerClientSurfaceListDestroy(
+ DvrDisplayManagerClientSurfaceList* surface_list) {
+ delete surface_list;
+}
+
+size_t dvrDisplayManagerClientSurfaceListGetSize(
+ DvrDisplayManagerClientSurfaceList* surface_list) {
+ return surface_list->list.size();
+}
+
+int dvrDisplayManagerClientSurfaceListGetSurfaceId(
+ DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+ return surface_list->list[index].surface_id;
+}
+
+int dvrDisplayManagerClientSurfaceListGetClientZOrder(
+ DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+ return surface_list->list[index].ClientZOrder();
+}
+
+bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
+ DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+ return surface_list->list[index].IsClientVisible();
+}
+
+int dvrDisplayManagerClientGetSurfaceBuffers(
+ DvrDisplayManagerClient* client, int surface_id,
+ DvrDisplayManagerClientSurfaceBuffers** surface_buffers) {
+ std::vector<std::unique_ptr<android::dvr::BufferConsumer>> buffer_list;
+ int ret = client->client->GetSurfaceBuffers(surface_id, &buffer_list);
+ if (ret < 0)
+ return ret;
+
+ *surface_buffers =
+ new DvrDisplayManagerClientSurfaceBuffers(std::move(buffer_list));
+ return ret;
+}
+
+void dvrDisplayManagerClientSurfaceBuffersDestroy(
+ DvrDisplayManagerClientSurfaceBuffers* surface_buffers) {
+ delete surface_buffers;
+}
+
+size_t dvrDisplayManagerClientSurfaceBuffersGetSize(
+ DvrDisplayManagerClientSurfaceBuffers* surface_buffers) {
+ return surface_buffers->list.size();
+}
+
+int dvrDisplayManagerClientSurfaceBuffersGetFd(
+ DvrDisplayManagerClientSurfaceBuffers* surface_buffers, size_t index) {
+ return surface_buffers->list[index]->event_fd();
+}
+
+} // extern "C"
diff --git a/libs/vr/libdisplay/display_manager_client_impl.cpp b/libs/vr/libdisplay/display_manager_client_impl.cpp
new file mode 100644
index 0000000..82198b9
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client_impl.cpp
@@ -0,0 +1,57 @@
+#include "include/private/dvr/display_manager_client_impl.h"
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_rpc.h>
+#include <utils/Log.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+DisplayManagerClient::DisplayManagerClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayManagerRPC::kClientPath)) {}
+
+DisplayManagerClient::~DisplayManagerClient() {}
+
+int DisplayManagerClient::GetSurfaceList(
+ std::vector<DisplaySurfaceInfo>* surface_list) {
+ auto status = InvokeRemoteMethod<DisplayManagerRPC::GetSurfaceList>();
+ if (!status) {
+ ALOGE(
+ "DisplayManagerClient::GetSurfaceList: Failed to get surface info: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *surface_list = status.take();
+ return 0;
+}
+
+int DisplayManagerClient::GetSurfaceBuffers(
+ int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers) {
+ auto status =
+ InvokeRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(surface_id);
+ if (!status) {
+ ALOGE(
+ "DisplayManagerClient::GetSurfaceBuffers: Failed to get buffers for "
+ "surface_id=%d: %s",
+ surface_id, status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ std::vector<std::unique_ptr<BufferConsumer>> consumer_buffers;
+ std::vector<LocalChannelHandle> channel_handles = status.take();
+ for (auto&& handle : channel_handles) {
+ consumer_buffers.push_back(BufferConsumer::Import(std::move(handle)));
+ }
+
+ *consumers = std::move(consumer_buffers);
+ return 0;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/display_rpc.cpp b/libs/vr/libdisplay/display_rpc.cpp
new file mode 100644
index 0000000..f5693bd
--- /dev/null
+++ b/libs/vr/libdisplay/display_rpc.cpp
@@ -0,0 +1,12 @@
+#include "include/private/dvr/display_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char DisplayRPC::kClientPath[];
+constexpr char DisplayManagerRPC::kClientPath[];
+constexpr char DisplayScreenshotRPC::kClientPath[];
+constexpr char DisplayVSyncRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/dummy_native_window.cpp b/libs/vr/libdisplay/dummy_native_window.cpp
new file mode 100644
index 0000000..5547f53
--- /dev/null
+++ b/libs/vr/libdisplay/dummy_native_window.cpp
@@ -0,0 +1,75 @@
+#include "include/private/dvr/dummy_native_window.h"
+
+#include <utils/Errors.h>
+
+namespace {
+// Dummy functions required for an ANativeWindow Implementation.
+int F1(struct ANativeWindow*, int) { return 0; }
+int F2(struct ANativeWindow*, struct ANativeWindowBuffer**) { return 0; }
+int F3(struct ANativeWindow*, struct ANativeWindowBuffer*) { return 0; }
+int F4(struct ANativeWindow*, struct ANativeWindowBuffer**, int*) { return 0; }
+int F5(struct ANativeWindow*, struct ANativeWindowBuffer*, int) { return 0; }
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+DummyNativeWindow::DummyNativeWindow() {
+ ANativeWindow::setSwapInterval = F1;
+ ANativeWindow::dequeueBuffer = F4;
+ ANativeWindow::cancelBuffer = F5;
+ ANativeWindow::queueBuffer = F5;
+ ANativeWindow::query = Query;
+ ANativeWindow::perform = Perform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = F2;
+ ANativeWindow::cancelBuffer_DEPRECATED = F3;
+ ANativeWindow::lockBuffer_DEPRECATED = F3;
+ ANativeWindow::queueBuffer_DEPRECATED = F3;
+}
+
+int DummyNativeWindow::Query(const ANativeWindow*, int what, int* value) {
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ case NATIVE_WINDOW_HEIGHT:
+ case NATIVE_WINDOW_FORMAT:
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+ case NATIVE_WINDOW_DEFAULT_WIDTH:
+ case NATIVE_WINDOW_DEFAULT_HEIGHT:
+ case NATIVE_WINDOW_TRANSFORM_HINT:
+ *value = 0;
+ return NO_ERROR;
+ }
+
+ *value = 0;
+ return BAD_VALUE;
+}
+
+int DummyNativeWindow::Perform(ANativeWindow*, int operation, ...) {
+ switch (operation) {
+ case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+ case NATIVE_WINDOW_SET_USAGE:
+ case NATIVE_WINDOW_CONNECT:
+ case NATIVE_WINDOW_DISCONNECT:
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ case NATIVE_WINDOW_API_CONNECT:
+ case NATIVE_WINDOW_API_DISCONNECT:
+ case NATIVE_WINDOW_SET_BUFFER_COUNT:
+ case NATIVE_WINDOW_SET_BUFFERS_DATASPACE:
+ case NATIVE_WINDOW_SET_SCALING_MODE:
+ return NO_ERROR;
+ case NATIVE_WINDOW_LOCK:
+ case NATIVE_WINDOW_UNLOCK_AND_POST:
+ case NATIVE_WINDOW_SET_CROP:
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ return INVALID_OPERATION;
+ }
+ return NAME_NOT_FOUND;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/frame_history.cpp b/libs/vr/libdisplay/frame_history.cpp
new file mode 100644
index 0000000..67e4a09
--- /dev/null
+++ b/libs/vr/libdisplay/frame_history.cpp
@@ -0,0 +1,147 @@
+#include <private/dvr/frame_history.h>
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <sync/sync.h>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/sync_util.h>
+
+using android::pdx::LocalHandle;
+
+constexpr int kNumFramesToUseForSchedulePrediction = 10;
+constexpr int kDefaultVsyncIntervalPrediction = 1;
+constexpr int kMaxVsyncIntervalPrediction = 4;
+constexpr int kDefaultPendingFrameBufferSize = 10;
+
+namespace android {
+namespace dvr {
+
+FrameHistory::PendingFrame::PendingFrame()
+ : start_ns(0), scheduled_vsync(0), scheduled_finish_ns(0) {}
+
+FrameHistory::PendingFrame::PendingFrame(int64_t start_ns,
+ uint32_t scheduled_vsync,
+ int64_t scheduled_finish_ns,
+ LocalHandle&& fence)
+ : start_ns(start_ns), scheduled_vsync(scheduled_vsync),
+ scheduled_finish_ns(scheduled_finish_ns), fence(std::move(fence)) {}
+
+FrameHistory::FrameHistory() : FrameHistory(kDefaultPendingFrameBufferSize) {}
+
+FrameHistory::FrameHistory(int pending_frame_buffer_size)
+ : pending_frames_(pending_frame_buffer_size),
+ finished_frames_(pending_frame_buffer_size),
+ frame_duration_history_(kNumFramesToUseForSchedulePrediction) {}
+
+void FrameHistory::Reset(int pending_frame_buffer_size) {
+ pending_frames_.Reset(pending_frame_buffer_size);
+ finished_frames_.Reset(pending_frame_buffer_size);
+ frame_duration_history_.Clear();
+}
+
+void FrameHistory::OnFrameStart(uint32_t scheduled_vsync,
+ int64_t scheduled_finish_ns) {
+ if (!pending_frames_.IsEmpty() && !pending_frames_.Back().fence) {
+ // If we don't have a fence set for the previous frame it's because
+ // OnFrameStart() was called twice in a row with no OnFrameSubmit() call. In
+ // that case throw out the pending frame data for the last frame.
+ pending_frames_.PopBack();
+ }
+
+ if (pending_frames_.IsFull()) {
+ ALOGW("Pending frames buffer is full. Discarding pending frame data.");
+ }
+
+ pending_frames_.Append(PendingFrame(GetSystemClockNs(), scheduled_vsync,
+ scheduled_finish_ns, LocalHandle()));
+}
+
+void FrameHistory::OnFrameSubmit(LocalHandle&& fence) {
+ // Add the fence to the previous frame data in pending_frames so we can
+ // track when it finishes.
+ if (!pending_frames_.IsEmpty() && !pending_frames_.Back().fence) {
+ if (fence && pending_frames_.Back().scheduled_vsync != UINT32_MAX)
+ pending_frames_.Back().fence = std::move(fence);
+ else
+ pending_frames_.PopBack();
+ }
+}
+
+void FrameHistory::CheckForFinishedFrames() {
+ if (pending_frames_.IsEmpty())
+ return;
+
+ android::dvr::FenceInfoBuffer fence_info_buffer;
+ while (!pending_frames_.IsEmpty()) {
+ const auto& pending_frame = pending_frames_.Front();
+ if (!pending_frame.fence) {
+ // The frame hasn't been submitted yet, so there's nothing more to do
+ break;
+ }
+
+ int64_t fence_signaled_time = -1;
+ int fence = pending_frame.fence.Get();
+ int sync_result = sync_wait(fence, 0);
+ if (sync_result == 0) {
+ int fence_signaled_result =
+ GetFenceSignaledTimestamp(fence, &fence_info_buffer,
+ &fence_signaled_time);
+ if (fence_signaled_result < 0) {
+ ALOGE("Failed getting signaled timestamp from fence");
+ } else {
+ // The frame is finished. Record the duration and move the frame data
+ // from pending_frames_ to finished_frames_.
+ DvrFrameScheduleResult schedule_result = {};
+ schedule_result.vsync_count = pending_frame.scheduled_vsync;
+ schedule_result.scheduled_frame_finish_ns =
+ pending_frame.scheduled_finish_ns;
+ schedule_result.frame_finish_offset_ns =
+ fence_signaled_time - pending_frame.scheduled_finish_ns;
+ finished_frames_.Append(schedule_result);
+ frame_duration_history_.Append(
+ fence_signaled_time - pending_frame.start_ns);
+ }
+ pending_frames_.PopFront();
+ } else {
+ if (errno != ETIME) {
+ ALOGE("sync_wait on frame fence failed. fence=%d errno=%d (%s).",
+ fence, errno, strerror(errno));
+ }
+ break;
+ }
+ }
+}
+
+int FrameHistory::PredictNextFrameVsyncInterval(int64_t vsync_period_ns) const {
+ if (frame_duration_history_.IsEmpty())
+ return kDefaultVsyncIntervalPrediction;
+
+ double total = 0;
+ for (size_t i = 0; i < frame_duration_history_.GetSize(); ++i)
+ total += frame_duration_history_.Get(i);
+ double avg_duration = total / frame_duration_history_.GetSize();
+
+ return std::min(kMaxVsyncIntervalPrediction,
+ static_cast<int>(avg_duration / vsync_period_ns) + 1);
+}
+
+int FrameHistory::GetPreviousFrameResults(DvrFrameScheduleResult* results,
+ int in_result_count) {
+ int out_result_count =
+ std::min(in_result_count, static_cast<int>(finished_frames_.GetSize()));
+ for (int i = 0; i < out_result_count; ++i) {
+ results[i] = finished_frames_.Get(0);
+ finished_frames_.PopFront();
+ }
+ return out_result_count;
+}
+
+uint32_t FrameHistory::GetCurrentFrameVsync() const {
+ return pending_frames_.IsEmpty() ?
+ UINT32_MAX : pending_frames_.Back().scheduled_vsync;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/gl_fenced_flush.cpp b/libs/vr/libdisplay/gl_fenced_flush.cpp
new file mode 100644
index 0000000..64b2e99
--- /dev/null
+++ b/libs/vr/libdisplay/gl_fenced_flush.cpp
@@ -0,0 +1,39 @@
+#include "include/private/dvr/gl_fenced_flush.h"
+
+#include <EGL/eglext.h>
+#include <GLES3/gl31.h>
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <base/logging.h>
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+LocalHandle CreateGLSyncAndFlush(EGLDisplay display) {
+ ATRACE_NAME("CreateGLSyncAndFlush");
+
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
+ EGL_NO_NATIVE_FENCE_FD_ANDROID, EGL_NONE};
+ EGLSyncKHR sync_point =
+ eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ glFlush();
+ if (sync_point == EGL_NO_SYNC_KHR) {
+ LOG(ERROR) << "sync_point == EGL_NO_SYNC_KHR";
+ return LocalHandle();
+ }
+ EGLint fence_fd = eglDupNativeFenceFDANDROID(display, sync_point);
+ eglDestroySyncKHR(display, sync_point);
+
+ if (fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ LOG(ERROR) << "fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID";
+ return LocalHandle();
+ }
+ return LocalHandle(fence_fd);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/graphics.cpp b/libs/vr/libdisplay/graphics.cpp
new file mode 100644
index 0000000..d599616
--- /dev/null
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -0,0 +1,1587 @@
+#include <dvr/graphics.h>
+
+#include <sys/timerfd.h>
+#include <array>
+#include <vector>
+
+#include <cutils/log.h>
+#include <utils/Trace.h>
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR 1
+#endif
+#include <vulkan/vulkan.h>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/frame_history.h>
+#include <private/dvr/gl_fenced_flush.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/graphics_private.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/native_buffer_queue.h>
+#include <private/dvr/sensor_constants.h>
+#include <private/dvr/video_mesh_surface_client.h>
+#include <private/dvr/vsync_client.h>
+
+#include <android/native_window.h>
+
+#ifndef EGL_CONTEXT_MAJOR_VERSION
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#endif
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+
+using android::dvr::DisplaySurfaceAttributeEnum;
+using android::dvr::DisplaySurfaceAttributeValue;
+
+namespace {
+
+constexpr int kDefaultDisplaySurfaceUsage =
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+// TODO(alexst): revisit this count when HW encode is available for casting.
+constexpr int kDefaultBufferCount = 4;
+
+// Use with dvrBeginRenderFrame to disable EDS for the current frame.
+constexpr float32x4_t DVR_POSE_NO_EDS = {10.0f, 0.0f, 0.0f, 0.0f};
+
+// Use with dvrBeginRenderFrame to indicate that GPU late-latching is being used
+// for determining the render pose.
+constexpr float32x4_t DVR_POSE_LATE_LATCH = {20.0f, 0.0f, 0.0f, 0.0f};
+
+#ifndef NDEBUG
+
+static const char* GetGlCallbackType(GLenum type) {
+ switch (type) {
+ case GL_DEBUG_TYPE_ERROR_KHR:
+ return "ERROR";
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR:
+ return "DEPRECATED_BEHAVIOR";
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR:
+ return "UNDEFINED_BEHAVIOR";
+ case GL_DEBUG_TYPE_PORTABILITY_KHR:
+ return "PORTABILITY";
+ case GL_DEBUG_TYPE_PERFORMANCE_KHR:
+ return "PERFORMANCE";
+ case GL_DEBUG_TYPE_OTHER_KHR:
+ return "OTHER";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void on_gl_error(GLenum /*source*/, GLenum type, GLuint /*id*/,
+ GLenum severity, GLsizei /*length*/,
+ const char* message, const void* /*user_param*/) {
+ char msg[400];
+ snprintf(msg, sizeof(msg), "[" __FILE__ ":%u] GL %s: %s", __LINE__,
+ GetGlCallbackType(type), message);
+ switch (severity) {
+ case GL_DEBUG_SEVERITY_LOW_KHR:
+ ALOGI("%s", msg);
+ break;
+ case GL_DEBUG_SEVERITY_MEDIUM_KHR:
+ ALOGW("%s", msg);
+ break;
+ case GL_DEBUG_SEVERITY_HIGH_KHR:
+ ALOGE("%s", msg);
+ break;
+ }
+ fprintf(stderr, "%s\n", msg);
+}
+
+#endif
+
+int DvrToHalSurfaceFormat(int dvr_surface_format) {
+ switch (dvr_surface_format) {
+ case DVR_SURFACE_FORMAT_RGBA_8888:
+ return HAL_PIXEL_FORMAT_RGBA_8888;
+ case DVR_SURFACE_FORMAT_RGB_565:
+ return HAL_PIXEL_FORMAT_RGB_565;
+ default:
+ return HAL_PIXEL_FORMAT_RGBA_8888;
+ }
+}
+
+int SelectEGLConfig(EGLDisplay dpy, EGLint* attr, unsigned format,
+ EGLConfig* config) {
+ std::array<EGLint, 4> desired_rgba;
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ desired_rgba = {{8, 8, 8, 8}};
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ desired_rgba = {{5, 6, 5, 0}};
+ break;
+ default:
+ ALOGE("Unsupported framebuffer pixel format %d", format);
+ return -1;
+ }
+
+ EGLint max_configs = 0;
+ if (eglGetConfigs(dpy, NULL, 0, &max_configs) == EGL_FALSE) {
+ ALOGE("No EGL configurations available?!");
+ return -1;
+ }
+
+ std::vector<EGLConfig> configs(max_configs);
+
+ EGLint num_configs;
+ if (eglChooseConfig(dpy, attr, &configs[0], max_configs, &num_configs) ==
+ EGL_FALSE) {
+ ALOGE("eglChooseConfig failed");
+ return -1;
+ }
+
+ std::array<EGLint, 4> config_rgba;
+ for (int i = 0; i < num_configs; i++) {
+ eglGetConfigAttrib(dpy, configs[i], EGL_RED_SIZE, &config_rgba[0]);
+ eglGetConfigAttrib(dpy, configs[i], EGL_GREEN_SIZE, &config_rgba[1]);
+ eglGetConfigAttrib(dpy, configs[i], EGL_BLUE_SIZE, &config_rgba[2]);
+ eglGetConfigAttrib(dpy, configs[i], EGL_ALPHA_SIZE, &config_rgba[3]);
+ if (config_rgba == desired_rgba) {
+ *config = configs[i];
+ return 0;
+ }
+ }
+
+ ALOGE("Cannot find a matching EGL config");
+ return -1;
+}
+
+void DestroyEglContext(EGLDisplay egl_display, EGLContext* egl_context) {
+ if (*egl_context != EGL_NO_CONTEXT) {
+ eglDestroyContext(egl_display, *egl_context);
+ *egl_context = EGL_NO_CONTEXT;
+ }
+}
+
+// Perform internal initialization. A GL context must be bound to the current
+// thread.
+// @param internally_created_context True if we created and own the GL context,
+// false if it was supplied by the application.
+// @return 0 if init was successful, or a negative error code on failure.
+int InitGl(bool internally_created_context) {
+ EGLDisplay egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (egl_display == EGL_NO_DISPLAY) {
+ ALOGE("eglGetDisplay failed");
+ return -EINVAL;
+ }
+
+ EGLContext egl_context = eglGetCurrentContext();
+ if (egl_context == EGL_NO_CONTEXT) {
+ ALOGE("No GL context bound");
+ return -EINVAL;
+ }
+
+ glGetError(); // Clear the error state
+ GLint major_version, minor_version;
+ glGetIntegerv(GL_MAJOR_VERSION, &major_version);
+ glGetIntegerv(GL_MINOR_VERSION, &minor_version);
+ if (glGetError() != GL_NO_ERROR) {
+ // GL_MAJOR_VERSION and GL_MINOR_VERSION were added in GLES 3. If we get an
+ // error querying them it's almost certainly because it's GLES 1 or 2.
+ ALOGE("Error getting GL version. Must be GLES 3.2 or greater.");
+ return -EINVAL;
+ }
+
+ if (major_version < 3 || (major_version == 3 && minor_version < 2)) {
+ ALOGE("Invalid GL version: %d.%d. Must be GLES 3.2 or greater.",
+ major_version, minor_version);
+ return -EINVAL;
+ }
+
+#ifndef NDEBUG
+ if (internally_created_context) {
+ // Enable verbose GL debug output.
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
+ glDebugMessageCallbackKHR(on_gl_error, NULL);
+ GLuint unused_ids = 0;
+ glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0,
+ &unused_ids, GL_TRUE);
+ }
+#else
+ (void)internally_created_context;
+#endif
+
+ load_gl_extensions();
+ return 0;
+}
+
+int CreateEglContext(EGLDisplay egl_display, DvrSurfaceParameter* parameters,
+ EGLContext* egl_context) {
+ *egl_context = EGL_NO_CONTEXT;
+
+ EGLint major, minor;
+ if (!eglInitialize(egl_display, &major, &minor)) {
+ ALOGE("Failed to initialize EGL");
+ return -ENXIO;
+ }
+
+ ALOGI("EGL version: %d.%d\n", major, minor);
+
+ int buffer_format = kDefaultDisplaySurfaceFormat;
+
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_FORMAT_IN:
+ buffer_format = DvrToHalSurfaceFormat(p->value);
+ break;
+ }
+ }
+
+ EGLint config_attrs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
+ EGLConfig config = {0};
+
+ int ret = SelectEGLConfig(egl_display, config_attrs, buffer_format, &config);
+ if (ret < 0)
+ return ret;
+
+ ALOGI("EGL SelectEGLConfig ok.\n");
+
+ EGLint context_attrs[] = {EGL_CONTEXT_MAJOR_VERSION,
+ 3,
+ EGL_CONTEXT_MINOR_VERSION,
+ 2,
+#ifndef NDEBUG
+ EGL_CONTEXT_FLAGS_KHR,
+ EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
+#endif
+ EGL_NONE};
+
+ *egl_context =
+ eglCreateContext(egl_display, config, EGL_NO_CONTEXT, context_attrs);
+ if (*egl_context == EGL_NO_CONTEXT) {
+ ALOGE("eglCreateContext failed");
+ return -ENXIO;
+ }
+
+ ALOGI("eglCreateContext ok.\n");
+
+ if (!eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ *egl_context)) {
+ ALOGE("eglMakeCurrent failed");
+ DestroyEglContext(egl_display, egl_context);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+} // anonymous namespace
+
+// TODO(hendrikw): When we remove the calls to this in native_window.cpp, move
+// this back into the anonymous namespace
+std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient(
+ struct DvrSurfaceParameter* parameters,
+ /*out*/ android::dvr::SystemDisplayMetrics* metrics) {
+ auto client = android::dvr::DisplayClient::Create();
+ if (!client) {
+ ALOGE("Failed to create display client!");
+ return nullptr;
+ }
+
+ const int ret = client->GetDisplayMetrics(metrics);
+ if (ret < 0) {
+ ALOGE("Failed to get display metrics: %s", strerror(-ret));
+ return nullptr;
+ }
+
+ // Parameters that may be modified by the parameters array. Some of these are
+ // here for future expansion.
+ int request_width = -1;
+ int request_height = -1;
+ int request_flags = 0;
+ bool disable_distortion = false;
+ bool disable_stabilization = false;
+ bool disable_cac = false;
+ bool request_visible = true;
+ bool vertical_flip = false;
+ int request_z_order = 0;
+ bool request_exclude_from_blur = false;
+ bool request_blur_behind = true;
+ int request_format = kDefaultDisplaySurfaceFormat;
+ int request_usage = kDefaultDisplaySurfaceUsage;
+ int geometry_type = DVR_SURFACE_GEOMETRY_SINGLE;
+
+ // Handle parameter inputs.
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_WIDTH_IN:
+ request_width = p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_HEIGHT_IN:
+ request_height = p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_DISABLE_DISTORTION_IN:
+ disable_distortion = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_DISABLE_STABILIZATION_IN:
+ disable_stabilization = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_DISABLE_CAC_IN:
+ disable_cac = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_VISIBLE_IN:
+ request_visible = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_Z_ORDER_IN:
+ request_z_order = p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_EXCLUDE_FROM_BLUR_IN:
+ request_exclude_from_blur = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_BLUR_BEHIND_IN:
+ request_blur_behind = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_VERTICAL_FLIP_IN:
+ vertical_flip = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_GEOMETRY_IN:
+ geometry_type = p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_FORMAT_IN:
+ request_format = DvrToHalSurfaceFormat(p->value);
+ break;
+ case DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN:
+ case DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN:
+ case DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT:
+ case DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT:
+ case DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT:
+ case DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT:
+ case DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT:
+ case DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT:
+ case DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT:
+ case DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT:
+ case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT:
+ case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT:
+ case DVR_SURFACE_PARAMETER_GRAPHICS_API_IN:
+ case DVR_SURFACE_PARAMETER_VK_INSTANCE_IN:
+ case DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN:
+ case DVR_SURFACE_PARAMETER_VK_DEVICE_IN:
+ case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN:
+ case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN:
+ case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT:
+ case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT:
+ break;
+ default:
+ ALOGE("Invalid display surface parameter: key=%d value=%ld", p->key,
+ p->value);
+ return nullptr;
+ }
+ }
+
+ request_flags |= disable_distortion
+ ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION
+ : 0;
+ request_flags |=
+ disable_stabilization ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS : 0;
+ request_flags |=
+ disable_cac ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC : 0;
+ request_flags |= vertical_flip ? DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP : 0;
+ request_flags |= (geometry_type == DVR_SURFACE_GEOMETRY_SEPARATE_2)
+ ? DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2
+ : 0;
+
+ if (request_width == -1) {
+ request_width = disable_distortion ? metrics->display_native_width
+ : metrics->distorted_width;
+ if (!disable_distortion &&
+ geometry_type == DVR_SURFACE_GEOMETRY_SEPARATE_2) {
+ // The metrics always return the single wide buffer resolution.
+ // When split between eyes, we need to halve the width of the surface.
+ request_width /= 2;
+ }
+ }
+ if (request_height == -1) {
+ request_height = disable_distortion ? metrics->display_native_height
+ : metrics->distorted_height;
+ }
+
+ std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
+ client->CreateDisplaySurface(request_width, request_height,
+ request_format, request_usage,
+ request_flags);
+ surface->SetAttributes(
+ {{DisplaySurfaceAttributeEnum::Visible,
+ DisplaySurfaceAttributeValue{request_visible}},
+ {DisplaySurfaceAttributeEnum::ZOrder,
+ DisplaySurfaceAttributeValue{request_z_order}},
+ {DisplaySurfaceAttributeEnum::ExcludeFromBlur,
+ DisplaySurfaceAttributeValue{request_exclude_from_blur}},
+ {DisplaySurfaceAttributeEnum::BlurBehind,
+ DisplaySurfaceAttributeValue{request_blur_behind}}});
+
+ // Handle parameter output requests down here so we can return surface info.
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT:
+ *static_cast<int32_t*>(p->value_out) = metrics->display_native_width;
+ break;
+ case DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT:
+ *static_cast<int32_t*>(p->value_out) = metrics->display_native_height;
+ break;
+ case DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT:
+ *static_cast<int32_t*>(p->value_out) = surface->width();
+ break;
+ case DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT:
+ *static_cast<int32_t*>(p->value_out) = surface->height();
+ break;
+ case DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT:
+ *static_cast<float*>(p->value_out) = metrics->inter_lens_distance_m;
+ break;
+ case DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT:
+ for (int i = 0; i < 4; ++i) {
+ float* float_values_out = static_cast<float*>(p->value_out);
+ float_values_out[i] = metrics->left_fov_lrbt[i];
+ }
+ break;
+ case DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT:
+ for (int i = 0; i < 4; ++i) {
+ float* float_values_out = static_cast<float*>(p->value_out);
+ float_values_out[i] = metrics->right_fov_lrbt[i];
+ }
+ break;
+ case DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT:
+ *static_cast<uint64_t*>(p->value_out) = metrics->vsync_period_ns;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return surface;
+}
+
+extern "C" int dvrGetNativeDisplayDimensions(int* native_width,
+ int* native_height) {
+ int error = 0;
+ auto client = android::dvr::DisplayClient::Create(&error);
+ if (!client) {
+ ALOGE("Failed to create display client!");
+ return error;
+ }
+
+ android::dvr::SystemDisplayMetrics metrics;
+ const int ret = client->GetDisplayMetrics(&metrics);
+
+ if (ret != 0) {
+ ALOGE("Failed to get display metrics!");
+ return ret;
+ }
+
+ *native_width = static_cast<int>(metrics.display_native_width);
+ *native_height = static_cast<int>(metrics.display_native_height);
+ return 0;
+}
+
+extern "C" int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width,
+ int* height, int* format) {
+ ANativeWindow* nwin = reinterpret_cast<ANativeWindow*>(win);
+ int w, h, f;
+
+ nwin->query(nwin, NATIVE_WINDOW_DEFAULT_WIDTH, &w);
+ nwin->query(nwin, NATIVE_WINDOW_DEFAULT_HEIGHT, &h);
+ nwin->query(nwin, NATIVE_WINDOW_FORMAT, &f);
+
+ if (width)
+ *width = w;
+ if (height)
+ *height = h;
+ if (format)
+ *format = f;
+
+ return 0;
+}
+
+struct DvrGraphicsContext : public android::ANativeObjectBase<
+ ANativeWindow, DvrGraphicsContext,
+ android::LightRefBase<DvrGraphicsContext>> {
+ public:
+ DvrGraphicsContext();
+ ~DvrGraphicsContext();
+
+ int graphics_api; // DVR_SURFACE_GRAPHICS_API_*
+
+ // GL specific members.
+ struct {
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ bool owns_egl_context;
+ GLuint texture_id[kSurfaceViewMaxCount];
+ int texture_count;
+ GLenum texture_target_type;
+ } gl;
+
+ // VK specific members
+ struct {
+ // These objects are passed in by the application, and are NOT owned
+ // by the context.
+ VkInstance instance;
+ VkPhysicalDevice physical_device;
+ VkDevice device;
+ VkQueue present_queue;
+ uint32_t present_queue_family;
+ const VkAllocationCallbacks* allocation_callbacks;
+ // These objects are owned by the context.
+ ANativeWindow* window;
+ VkSurfaceKHR surface;
+ VkSwapchainKHR swapchain;
+ std::vector<VkImage> swapchain_images;
+ std::vector<VkImageView> swapchain_image_views;
+ } vk;
+
+ // Display surface, metrics, and buffer management members.
+ std::shared_ptr<android::dvr::DisplaySurfaceClient> display_surface;
+ android::dvr::SystemDisplayMetrics display_metrics;
+ std::unique_ptr<android::dvr::NativeBufferQueue> buffer_queue;
+ android::dvr::NativeBufferProducer* current_buffer;
+ bool buffer_already_posted;
+
+ // Synchronization members.
+ std::unique_ptr<android::dvr::VSyncClient> vsync_client;
+ LocalHandle timerfd;
+
+ android::dvr::FrameHistory frame_history;
+
+ // Mapped surface metadata (ie: for pose delivery with presented frames).
+ volatile android::dvr::DisplaySurfaceMetadata* surface_metadata;
+
+ // LateLatch support.
+ std::unique_ptr<android::dvr::LateLatch> late_latch;
+
+ // Video mesh support.
+ std::vector<std::shared_ptr<android::dvr::VideoMeshSurfaceClient>>
+ video_mesh_surfaces;
+
+ private:
+ // ANativeWindow function implementations
+ std::mutex lock_;
+ int Post(android::dvr::NativeBufferProducer* buffer, int fence_fd);
+ static int SetSwapInterval(ANativeWindow* window, int interval);
+ static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fence_fd);
+ static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fence_fd);
+ static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fence_fd);
+ static int Query(const ANativeWindow* window, int what, int* value);
+ static int Perform(ANativeWindow* window, int operation, ...);
+ static int DequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer);
+ static int CancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer);
+ static int QueueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer);
+ static int LockBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer);
+
+ DISALLOW_COPY_AND_ASSIGN(DvrGraphicsContext);
+};
+
+DvrGraphicsContext::DvrGraphicsContext()
+ : graphics_api(DVR_GRAPHICS_API_GLES),
+ gl{},
+ vk{},
+ current_buffer(nullptr),
+ buffer_already_posted(false),
+ surface_metadata(nullptr) {
+ gl.egl_display = EGL_NO_DISPLAY;
+ gl.egl_context = EGL_NO_CONTEXT;
+ gl.owns_egl_context = true;
+ gl.texture_target_type = GL_TEXTURE_2D;
+
+ ANativeWindow::setSwapInterval = SetSwapInterval;
+ ANativeWindow::dequeueBuffer = DequeueBuffer;
+ ANativeWindow::cancelBuffer = CancelBuffer;
+ ANativeWindow::queueBuffer = QueueBuffer;
+ ANativeWindow::query = Query;
+ ANativeWindow::perform = Perform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED;
+ ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED;
+ ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED;
+ ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED;
+}
+
+DvrGraphicsContext::~DvrGraphicsContext() {
+ if (graphics_api == DVR_GRAPHICS_API_GLES) {
+ glDeleteTextures(gl.texture_count, gl.texture_id);
+ if (gl.owns_egl_context)
+ DestroyEglContext(gl.egl_display, &gl.egl_context);
+ } else if (graphics_api == DVR_GRAPHICS_API_VULKAN) {
+ if (vk.swapchain != VK_NULL_HANDLE) {
+ for (auto view : vk.swapchain_image_views) {
+ vkDestroyImageView(vk.device, view, vk.allocation_callbacks);
+ }
+ vkDestroySwapchainKHR(vk.device, vk.swapchain, vk.allocation_callbacks);
+ vkDestroySurfaceKHR(vk.instance, vk.surface, vk.allocation_callbacks);
+ delete vk.window;
+ }
+ }
+}
+
+int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
+ DvrGraphicsContext** return_graphics_context) {
+ std::unique_ptr<DvrGraphicsContext> context(new DvrGraphicsContext);
+
+ // See whether we're using GL or Vulkan
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_GRAPHICS_API_IN:
+ context->graphics_api = p->value;
+ break;
+ }
+ }
+
+ if (context->graphics_api == DVR_GRAPHICS_API_GLES) {
+ context->gl.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (context->gl.egl_display == EGL_NO_DISPLAY) {
+ ALOGE("eglGetDisplay failed");
+ return -ENXIO;
+ }
+
+ // See if we should create a GL context
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN:
+ context->gl.owns_egl_context = p->value != 0;
+ break;
+ }
+ }
+
+ if (context->gl.owns_egl_context) {
+ int ret = CreateEglContext(context->gl.egl_display, parameters,
+ &context->gl.egl_context);
+ if (ret < 0)
+ return ret;
+ } else {
+ context->gl.egl_context = eglGetCurrentContext();
+ }
+
+ int ret = InitGl(context->gl.owns_egl_context);
+ if (ret < 0)
+ return ret;
+ } else if (context->graphics_api == DVR_GRAPHICS_API_VULKAN) {
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_VK_INSTANCE_IN:
+ context->vk.instance = reinterpret_cast<VkInstance>(p->value);
+ break;
+ case DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN:
+ context->vk.physical_device =
+ reinterpret_cast<VkPhysicalDevice>(p->value);
+ break;
+ case DVR_SURFACE_PARAMETER_VK_DEVICE_IN:
+ context->vk.device = reinterpret_cast<VkDevice>(p->value);
+ break;
+ case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN:
+ context->vk.present_queue = reinterpret_cast<VkQueue>(p->value);
+ break;
+ case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN:
+ context->vk.present_queue_family = static_cast<uint32_t>(p->value);
+ break;
+ }
+ }
+ } else {
+ ALOGE("Error: invalid graphics API type");
+ return -EINVAL;
+ }
+
+ context->display_surface =
+ CreateDisplaySurfaceClient(parameters, &context->display_metrics);
+ if (!context->display_surface) {
+ ALOGE("Error: failed to create display surface client");
+ return -ECOMM;
+ }
+
+ context->buffer_queue.reset(new android::dvr::NativeBufferQueue(
+ context->gl.egl_display, context->display_surface, kDefaultBufferCount));
+
+ // The way the call sequence works we need 1 more than the buffer queue
+ // capacity to store data for all pending frames
+ context->frame_history.Reset(context->buffer_queue->GetQueueCapacity() + 1);
+
+ context->vsync_client = android::dvr::VSyncClient::Create();
+ if (!context->vsync_client) {
+ ALOGE("Error: failed to create vsync client");
+ return -ECOMM;
+ }
+
+ context->timerfd.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
+ if (!context->timerfd) {
+ ALOGE("Error: timerfd_create failed because: %s", strerror(errno));
+ return -EPERM;
+ }
+
+ context->surface_metadata = context->display_surface->GetMetadataBufferPtr();
+ if (!context->surface_metadata) {
+ ALOGE("Error: surface metadata allocation failed");
+ return -ENOMEM;
+ }
+
+ ALOGI("buffer: %d x %d\n", context->display_surface->width(),
+ context->display_surface->height());
+
+ if (context->graphics_api == DVR_GRAPHICS_API_GLES) {
+ context->gl.texture_count = (context->display_surface->flags() &
+ DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2)
+ ? 2
+ : 1;
+
+ // Create the GL textures.
+ glGenTextures(context->gl.texture_count, context->gl.texture_id);
+
+ // We must make sure that we have at least one buffer allocated at this time
+ // so that anyone who tries to bind an FBO to context->texture_id
+ // will not get an incomplete buffer.
+ context->current_buffer = context->buffer_queue->Dequeue();
+ CHECK(context->gl.texture_count ==
+ context->current_buffer->buffer()->slice_count());
+ for (int i = 0; i < context->gl.texture_count; ++i) {
+ glBindTexture(context->gl.texture_target_type, context->gl.texture_id[i]);
+ glEGLImageTargetTexture2DOES(context->gl.texture_target_type,
+ context->current_buffer->image_khr(i));
+ }
+ glBindTexture(context->gl.texture_target_type, 0);
+ CHECK_GL();
+
+ bool is_late_latch = false;
+
+ // Pass back the texture target type and id.
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN:
+ is_late_latch = !!p->value;
+ break;
+ case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT:
+ *static_cast<GLenum*>(p->value_out) = context->gl.texture_target_type;
+ break;
+ case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT:
+ for (int i = 0; i < context->gl.texture_count; ++i) {
+ *(static_cast<GLuint*>(p->value_out) + i) =
+ context->gl.texture_id[i];
+ }
+ break;
+ }
+ }
+
+ // Initialize late latch.
+ if (is_late_latch) {
+ LocalHandle fd;
+ int ret = context->display_surface->GetMetadataBufferFd(&fd);
+ if (ret == 0) {
+ context->late_latch.reset(
+ new android::dvr::LateLatch(true, std::move(fd)));
+ } else {
+ ALOGE("Error: failed to get surface metadata buffer fd for late latch");
+ }
+ }
+ } else if (context->graphics_api == DVR_GRAPHICS_API_VULKAN) {
+ VkResult result = VK_SUCCESS;
+ // Create a VkSurfaceKHR from the ANativeWindow.
+ VkAndroidSurfaceCreateInfoKHR android_surface_ci = {};
+ android_surface_ci.sType =
+ VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ android_surface_ci.window = context.get();
+ result = vkCreateAndroidSurfaceKHR(
+ context->vk.instance, &android_surface_ci,
+ context->vk.allocation_callbacks, &context->vk.surface);
+ CHECK_EQ(result, VK_SUCCESS);
+ VkBool32 surface_supports_present = VK_FALSE;
+ result = vkGetPhysicalDeviceSurfaceSupportKHR(
+ context->vk.physical_device, context->vk.present_queue_family,
+ context->vk.surface, &surface_supports_present);
+ CHECK_EQ(result, VK_SUCCESS);
+ if (!surface_supports_present) {
+ ALOGE("Error: provided queue family (%u) does not support presentation",
+ context->vk.present_queue_family);
+ return -EPERM;
+ }
+ VkSurfaceCapabilitiesKHR surface_capabilities = {};
+ result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+ context->vk.physical_device, context->vk.surface,
+ &surface_capabilities);
+ CHECK_EQ(result, VK_SUCCESS);
+ // Determine the swapchain image format.
+ uint32_t device_surface_format_count = 0;
+ result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+ context->vk.physical_device, context->vk.surface,
+ &device_surface_format_count, nullptr);
+ CHECK_EQ(result, VK_SUCCESS);
+ std::vector<VkSurfaceFormatKHR> device_surface_formats(
+ device_surface_format_count);
+ result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+ context->vk.physical_device, context->vk.surface,
+ &device_surface_format_count, device_surface_formats.data());
+ CHECK_EQ(result, VK_SUCCESS);
+ CHECK_GT(device_surface_format_count, 0U);
+ CHECK_NE(device_surface_formats[0].format, VK_FORMAT_UNDEFINED);
+ VkSurfaceFormatKHR present_surface_format = device_surface_formats[0];
+ // Determine the swapchain present mode.
+ // TODO(cort): query device_present_modes to make sure MAILBOX is supported.
+ // But according to libvulkan, it is.
+ uint32_t device_present_mode_count = 0;
+ result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+ context->vk.physical_device, context->vk.surface,
+ &device_present_mode_count, nullptr);
+ CHECK_EQ(result, VK_SUCCESS);
+ std::vector<VkPresentModeKHR> device_present_modes(
+ device_present_mode_count);
+ result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+ context->vk.physical_device, context->vk.surface,
+ &device_present_mode_count, device_present_modes.data());
+ CHECK_EQ(result, VK_SUCCESS);
+ VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
+ // Extract presentation surface extents, image count, transform, usages,
+ // etc.
+ LOG_ASSERT(
+ static_cast<int>(surface_capabilities.currentExtent.width) != -1 &&
+ static_cast<int>(surface_capabilities.currentExtent.height) != -1);
+ VkExtent2D swapchain_extent = surface_capabilities.currentExtent;
+
+ uint32_t desired_image_count = surface_capabilities.minImageCount;
+ if (surface_capabilities.maxImageCount > 0 &&
+ desired_image_count > surface_capabilities.maxImageCount) {
+ desired_image_count = surface_capabilities.maxImageCount;
+ }
+ VkSurfaceTransformFlagBitsKHR surface_transform =
+ surface_capabilities.currentTransform;
+ VkImageUsageFlags image_usage_flags =
+ surface_capabilities.supportedUsageFlags;
+ CHECK_NE(surface_capabilities.supportedCompositeAlpha,
+ static_cast<VkFlags>(0));
+ VkCompositeAlphaFlagBitsKHR composite_alpha =
+ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ if (!(surface_capabilities.supportedCompositeAlpha &
+ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) {
+ composite_alpha = VkCompositeAlphaFlagBitsKHR(
+ static_cast<int>(surface_capabilities.supportedCompositeAlpha) &
+ -static_cast<int>(surface_capabilities.supportedCompositeAlpha));
+ }
+ // Create VkSwapchainKHR
+ VkSwapchainCreateInfoKHR swapchain_ci = {};
+ swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchain_ci.pNext = nullptr;
+ swapchain_ci.surface = context->vk.surface;
+ swapchain_ci.minImageCount = desired_image_count;
+ swapchain_ci.imageFormat = present_surface_format.format;
+ swapchain_ci.imageColorSpace = present_surface_format.colorSpace;
+ swapchain_ci.imageExtent.width = swapchain_extent.width;
+ swapchain_ci.imageExtent.height = swapchain_extent.height;
+ swapchain_ci.imageUsage = image_usage_flags;
+ swapchain_ci.preTransform = surface_transform;
+ swapchain_ci.compositeAlpha = composite_alpha;
+ swapchain_ci.imageArrayLayers = 1;
+ swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchain_ci.queueFamilyIndexCount = 0;
+ swapchain_ci.pQueueFamilyIndices = nullptr;
+ swapchain_ci.presentMode = present_mode;
+ swapchain_ci.clipped = VK_TRUE;
+ swapchain_ci.oldSwapchain = VK_NULL_HANDLE;
+ result = vkCreateSwapchainKHR(context->vk.device, &swapchain_ci,
+ context->vk.allocation_callbacks,
+ &context->vk.swapchain);
+ CHECK_EQ(result, VK_SUCCESS);
+ // Create swapchain image views
+ uint32_t image_count = 0;
+ result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+ &image_count, nullptr);
+ CHECK_EQ(result, VK_SUCCESS);
+ CHECK_GT(image_count, 0U);
+ context->vk.swapchain_images.resize(image_count);
+ result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+ &image_count,
+ context->vk.swapchain_images.data());
+ CHECK_EQ(result, VK_SUCCESS);
+ context->vk.swapchain_image_views.resize(image_count);
+ VkImageViewCreateInfo image_view_ci = {};
+ image_view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ image_view_ci.pNext = nullptr;
+ image_view_ci.flags = 0;
+ image_view_ci.format = swapchain_ci.imageFormat;
+ image_view_ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ image_view_ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ image_view_ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ image_view_ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+ image_view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ image_view_ci.subresourceRange.baseMipLevel = 0;
+ image_view_ci.subresourceRange.levelCount = 1;
+ image_view_ci.subresourceRange.baseArrayLayer = 0;
+ image_view_ci.subresourceRange.layerCount = 1;
+ image_view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ image_view_ci.image = VK_NULL_HANDLE; // filled in below
+ for (uint32_t i = 0; i < image_count; ++i) {
+ image_view_ci.image = context->vk.swapchain_images[i];
+ result = vkCreateImageView(context->vk.device, &image_view_ci,
+ context->vk.allocation_callbacks,
+ &context->vk.swapchain_image_views[i]);
+ CHECK_EQ(result, VK_SUCCESS);
+ }
+ // Fill in any requested output parameters.
+ for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+ switch (p->key) {
+ case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT:
+ *static_cast<uint32_t*>(p->value_out) = image_count;
+ break;
+ case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT:
+ *static_cast<VkFormat*>(p->value_out) = swapchain_ci.imageFormat;
+ break;
+ }
+ }
+ }
+
+ *return_graphics_context = context.release();
+ return 0;
+}
+
+void dvrGraphicsContextDestroy(DvrGraphicsContext* graphics_context) {
+ delete graphics_context;
+}
+
+// ANativeWindow function implementations. These should only be used
+// by the Vulkan path.
+int DvrGraphicsContext::Post(android::dvr::NativeBufferProducer* buffer,
+ int fence_fd) {
+ LOG_ASSERT(graphics_api == DVR_GRAPHICS_API_VULKAN);
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+ ALOGI_IF(TRACE, "DvrGraphicsContext::Post: buffer_id=%d, fence_fd=%d",
+ buffer->buffer()->id(), fence_fd);
+ ALOGW_IF(!display_surface->visible(),
+ "DvrGraphicsContext::Post: Posting buffer on invisible surface!!!");
+ // The NativeBufferProducer closes the fence fd, so dup it for tracking in the
+ // frame history.
+ frame_history.OnFrameSubmit(LocalHandle::AsDuplicate(fence_fd));
+ int result = buffer->Post(fence_fd, 0);
+ return result;
+}
+
+int DvrGraphicsContext::SetSwapInterval(ANativeWindow* window, int interval) {
+ ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
+ DvrGraphicsContext* self = getSelf(window);
+ (void)self;
+ LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+ return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::DequeueBuffer(ANativeWindow* window,
+ ANativeWindowBuffer** buffer,
+ int* fence_fd) {
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+
+ DvrGraphicsContext* self = getSelf(window);
+ LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ if (!self->current_buffer) {
+ self->current_buffer = self->buffer_queue.get()->Dequeue();
+ }
+ ATRACE_ASYNC_BEGIN("BufferDraw", self->current_buffer->buffer()->id());
+ *fence_fd = self->current_buffer->ClaimReleaseFence().Release();
+ *buffer = self->current_buffer;
+
+ ALOGI_IF(TRACE, "DvrGraphicsContext::DequeueBuffer: fence_fd=%d", *fence_fd);
+ return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::QueueBuffer(ANativeWindow* window,
+ ANativeWindowBuffer* buffer, int fence_fd) {
+ ATRACE_NAME("NativeWindow::QueueBuffer");
+ ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
+
+ DvrGraphicsContext* self = getSelf(window);
+ LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ android::dvr::NativeBufferProducer* native_buffer =
+ static_cast<android::dvr::NativeBufferProducer*>(buffer);
+ ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+ bool do_post = true;
+ if (self->buffer_already_posted) {
+ // Check that the buffer is the one we expect, but handle it if this happens
+ // in production by allowing this buffer to post on top of the previous one.
+ DCHECK(native_buffer == self->current_buffer);
+ if (native_buffer == self->current_buffer) {
+ do_post = false;
+ if (fence_fd >= 0)
+ close(fence_fd);
+ }
+ }
+ if (do_post) {
+ ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id());
+ self->Post(native_buffer, fence_fd);
+ }
+ self->buffer_already_posted = false;
+ self->current_buffer = nullptr;
+
+ return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::CancelBuffer(ANativeWindow* window,
+ ANativeWindowBuffer* buffer,
+ int fence_fd) {
+ ATRACE_NAME("DvrGraphicsContext::CancelBuffer");
+ ALOGI_IF(TRACE, "DvrGraphicsContext::CancelBuffer: fence_fd: %d", fence_fd);
+
+ DvrGraphicsContext* self = getSelf(window);
+ LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ android::dvr::NativeBufferProducer* native_buffer =
+ static_cast<android::dvr::NativeBufferProducer*>(buffer);
+ ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+ ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
+ bool do_enqueue = true;
+ if (self->buffer_already_posted) {
+ // Check that the buffer is the one we expect, but handle it if this happens
+ // in production by returning this buffer to the buffer queue.
+ DCHECK(native_buffer == self->current_buffer);
+ if (native_buffer == self->current_buffer) {
+ do_enqueue = false;
+ }
+ }
+ if (do_enqueue) {
+ self->buffer_queue.get()->Enqueue(native_buffer);
+ }
+ if (fence_fd >= 0)
+ close(fence_fd);
+ self->buffer_already_posted = false;
+ self->current_buffer = nullptr;
+
+ return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::Query(const ANativeWindow* window, int what,
+ int* value) {
+ DvrGraphicsContext* self = getSelf(const_cast<ANativeWindow*>(window));
+ LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ *value = self->display_surface->width();
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = self->display_surface->height();
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_FORMAT:
+ *value = self->display_surface->format();
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ *value = 1;
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ *value = NATIVE_WINDOW_SURFACE;
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+ *value = 1;
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_DEFAULT_WIDTH:
+ *value = self->display_surface->width();
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_DEFAULT_HEIGHT:
+ *value = self->display_surface->height();
+ return android::NO_ERROR;
+ case NATIVE_WINDOW_TRANSFORM_HINT:
+ *value = 0;
+ return android::NO_ERROR;
+ }
+
+ *value = 0;
+ return android::BAD_VALUE;
+}
+
+int DvrGraphicsContext::Perform(ANativeWindow* window, int operation, ...) {
+ DvrGraphicsContext* self = getSelf(window);
+ LOG_ASSERT(self->graphics_api == DVR_GRAPHICS_API_VULKAN);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ va_list args;
+ va_start(args, operation);
+
+ // TODO(eieio): The following operations are not used at this time. They are
+ // included here to help document which operations may be useful and what
+ // parameters they take.
+ switch (operation) {
+ case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h);
+ return android::NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+ int format = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format);
+ return android::NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: {
+ int transform = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d",
+ transform);
+ return android::NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_SET_USAGE: {
+ int usage = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
+ return android::NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_CONNECT:
+ case NATIVE_WINDOW_DISCONNECT:
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ case NATIVE_WINDOW_API_CONNECT:
+ case NATIVE_WINDOW_API_DISCONNECT:
+ // TODO(eieio): we should implement these
+ return android::NO_ERROR;
+
+ case NATIVE_WINDOW_SET_BUFFER_COUNT: {
+ int buffer_count = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d",
+ buffer_count);
+ return android::NO_ERROR;
+ }
+ case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+ android_dataspace_t data_space =
+ static_cast<android_dataspace_t>(va_arg(args, int));
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d",
+ data_space);
+ return android::NO_ERROR;
+ }
+ case NATIVE_WINDOW_SET_SCALING_MODE: {
+ int mode = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode);
+ return android::NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_LOCK:
+ case NATIVE_WINDOW_UNLOCK_AND_POST:
+ case NATIVE_WINDOW_SET_CROP:
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ return android::INVALID_OPERATION;
+ }
+
+ return android::NAME_NOT_FOUND;
+}
+
+int DvrGraphicsContext::DequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer) {
+ int fence_fd = -1;
+ int ret = DequeueBuffer(window, buffer, &fence_fd);
+
+ // wait for fence
+ if (ret == android::NO_ERROR && fence_fd != -1)
+ close(fence_fd);
+
+ return ret;
+}
+
+int DvrGraphicsContext::CancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return CancelBuffer(window, buffer, -1);
+}
+
+int DvrGraphicsContext::QueueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return QueueBuffer(window, buffer, -1);
+}
+
+int DvrGraphicsContext::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
+ ANativeWindowBuffer* /*buffer*/) {
+ return android::NO_ERROR;
+}
+// End ANativeWindow implementation
+
+int dvrSetEdsPose(DvrGraphicsContext* graphics_context,
+ float32x4_t render_pose_orientation,
+ float32x4_t render_pose_translation) {
+ ATRACE_NAME("dvrSetEdsPose");
+ if (!graphics_context->current_buffer) {
+ ALOGE("dvrBeginRenderFrame must be called before dvrSetEdsPose");
+ return -EPERM;
+ }
+
+ // When late-latching is enabled, the pose buffer is written by the GPU, so
+ // we don't touch it here.
+ float32x4_t is_late_latch = DVR_POSE_LATE_LATCH;
+ if (render_pose_orientation[0] != is_late_latch[0]) {
+ volatile android::dvr::DisplaySurfaceMetadata* data =
+ graphics_context->surface_metadata;
+ uint32_t buffer_index =
+ graphics_context->current_buffer->surface_buffer_index();
+ ALOGE_IF(TRACE, "write pose index %d %f %f", buffer_index,
+ render_pose_orientation[0], render_pose_orientation[1]);
+ data->orientation[buffer_index] = render_pose_orientation;
+ data->translation[buffer_index] = render_pose_translation;
+ }
+
+ return 0;
+}
+
+int dvrBeginRenderFrameEds(DvrGraphicsContext* graphics_context,
+ float32x4_t render_pose_orientation,
+ float32x4_t render_pose_translation) {
+ ATRACE_NAME("dvrBeginRenderFrameEds");
+ LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_GLES);
+ CHECK_GL();
+ // Grab a buffer from the queue and set its pose.
+ if (!graphics_context->current_buffer) {
+ graphics_context->current_buffer =
+ graphics_context->buffer_queue->Dequeue();
+ }
+
+ int ret = dvrSetEdsPose(graphics_context, render_pose_orientation,
+ render_pose_translation);
+ if (ret < 0)
+ return ret;
+
+ ATRACE_ASYNC_BEGIN("BufferDraw",
+ graphics_context->current_buffer->buffer()->id());
+
+ {
+ ATRACE_NAME("glEGLImageTargetTexture2DOES");
+ // Bind the texture to the latest buffer in the queue.
+ for (int i = 0; i < graphics_context->gl.texture_count; ++i) {
+ glBindTexture(graphics_context->gl.texture_target_type,
+ graphics_context->gl.texture_id[i]);
+ glEGLImageTargetTexture2DOES(
+ graphics_context->gl.texture_target_type,
+ graphics_context->current_buffer->image_khr(i));
+ }
+ glBindTexture(graphics_context->gl.texture_target_type, 0);
+ }
+ CHECK_GL();
+ return 0;
+}
+int dvrBeginRenderFrameEdsVk(DvrGraphicsContext* graphics_context,
+ float32x4_t render_pose_orientation,
+ float32x4_t render_pose_translation,
+ VkSemaphore acquire_semaphore,
+ VkFence acquire_fence,
+ uint32_t* swapchain_image_index,
+ VkImageView* swapchain_image_view) {
+ ATRACE_NAME("dvrBeginRenderFrameEds");
+ LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_VULKAN);
+
+ // Acquire a swapchain image. This calls Dequeue() internally.
+ VkResult result = vkAcquireNextImageKHR(
+ graphics_context->vk.device, graphics_context->vk.swapchain, UINT64_MAX,
+ acquire_semaphore, acquire_fence, swapchain_image_index);
+ if (result != VK_SUCCESS)
+ return -EINVAL;
+
+ // Set the pose pose.
+ int ret = dvrSetEdsPose(graphics_context, render_pose_orientation,
+ render_pose_translation);
+ if (ret < 0)
+ return ret;
+ *swapchain_image_view =
+ graphics_context->vk.swapchain_image_views[*swapchain_image_index];
+ return 0;
+}
+
+int dvrBeginRenderFrame(DvrGraphicsContext* graphics_context) {
+ return dvrBeginRenderFrameEds(graphics_context, DVR_POSE_NO_EDS,
+ DVR_POSE_NO_EDS);
+}
+int dvrBeginRenderFrameVk(DvrGraphicsContext* graphics_context,
+ VkSemaphore acquire_semaphore, VkFence acquire_fence,
+ uint32_t* swapchain_image_index,
+ VkImageView* swapchain_image_view) {
+ return dvrBeginRenderFrameEdsVk(
+ graphics_context, DVR_POSE_NO_EDS, DVR_POSE_NO_EDS, acquire_semaphore,
+ acquire_fence, swapchain_image_index, swapchain_image_view);
+}
+
+int dvrBeginRenderFrameLateLatch(DvrGraphicsContext* graphics_context,
+ uint32_t /*flags*/,
+ uint32_t target_vsync_count, int num_views,
+ const float** projection_matrices,
+ const float** eye_from_head_matrices,
+ const float** pose_offset_matrices,
+ uint32_t* out_late_latch_buffer_id) {
+ if (!graphics_context->late_latch) {
+ return -EPERM;
+ }
+ if (num_views > DVR_GRAPHICS_SURFACE_MAX_VIEWS) {
+ LOG(ERROR) << "dvrBeginRenderFrameLateLatch called with too many views.";
+ return -EINVAL;
+ }
+ dvrBeginRenderFrameEds(graphics_context, DVR_POSE_LATE_LATCH,
+ DVR_POSE_LATE_LATCH);
+ auto& ll = graphics_context->late_latch;
+ // TODO(jbates) Need to change this shader so that it dumps the single
+ // captured pose for both eyes into the display surface metadata buffer at
+ // the right index.
+ android::dvr::LateLatchInput input;
+ memset(&input, 0, sizeof(input));
+ for (int i = 0; i < num_views; ++i) {
+ memcpy(input.proj_mat + i, *(projection_matrices + i), 16 * sizeof(float));
+ memcpy(input.eye_from_head_mat + i, *(eye_from_head_matrices + i),
+ 16 * sizeof(float));
+ memcpy(input.pose_offset + i, *(pose_offset_matrices + i),
+ 16 * sizeof(float));
+ }
+ input.pose_index =
+ target_vsync_count & android::dvr::kPoseAsyncBufferIndexMask;
+ input.render_pose_index =
+ graphics_context->current_buffer->surface_buffer_index();
+ ll->AddLateLatch(input);
+ *out_late_latch_buffer_id = ll->output_buffer_id();
+ return 0;
+}
+
+extern "C" int dvrGraphicsWaitNextFrame(
+ DvrGraphicsContext* graphics_context, int64_t start_delay_ns,
+ DvrFrameSchedule* out_next_frame_schedule) {
+ start_delay_ns = std::max(start_delay_ns, static_cast<int64_t>(0));
+
+ // We only do one-shot timers:
+ int64_t wake_time_ns = 0;
+
+ uint32_t current_frame_vsync;
+ int64_t current_frame_scheduled_finish_ns;
+ int64_t vsync_period_ns;
+
+ int fetch_schedule_result = graphics_context->vsync_client->GetSchedInfo(
+ &vsync_period_ns, ¤t_frame_scheduled_finish_ns,
+ ¤t_frame_vsync);
+ if (fetch_schedule_result == 0) {
+ wake_time_ns = current_frame_scheduled_finish_ns + start_delay_ns;
+ // If the last wakeup time is still in the future, use it instead to avoid
+ // major schedule jumps when applications call WaitNextFrame with
+ // aggressive offsets.
+ int64_t now = android::dvr::GetSystemClockNs();
+ if (android::dvr::TimestampGT(wake_time_ns - vsync_period_ns, now)) {
+ wake_time_ns -= vsync_period_ns;
+ --current_frame_vsync;
+ }
+ // If the next wakeup time is in the past, add a vsync period to keep the
+ // application on schedule.
+ if (android::dvr::TimestampLT(wake_time_ns, now)) {
+ wake_time_ns += vsync_period_ns;
+ ++current_frame_vsync;
+ }
+ } else {
+ ALOGE("Error getting frame schedule because: %s",
+ strerror(-fetch_schedule_result));
+ // Sleep for a vsync period to avoid cascading failure.
+ wake_time_ns = android::dvr::GetSystemClockNs() +
+ graphics_context->display_metrics.vsync_period_ns;
+ }
+
+ // Adjust nsec to [0..999,999,999].
+ struct itimerspec wake_time;
+ wake_time.it_interval.tv_sec = 0;
+ wake_time.it_interval.tv_nsec = 0;
+ wake_time.it_value = android::dvr::NsToTimespec(wake_time_ns);
+ bool sleep_result =
+ timerfd_settime(graphics_context->timerfd.Get(), TFD_TIMER_ABSTIME,
+ &wake_time, nullptr) == 0;
+ if (sleep_result) {
+ ATRACE_NAME("sleep");
+ uint64_t expirations = 0;
+ sleep_result = read(graphics_context->timerfd.Get(), &expirations,
+ sizeof(uint64_t)) == sizeof(uint64_t);
+ if (!sleep_result) {
+ ALOGE("Error: timerfd read failed");
+ }
+ } else {
+ ALOGE("Error: timerfd_settime failed because: %s", strerror(errno));
+ }
+
+ auto& frame_history = graphics_context->frame_history;
+ frame_history.CheckForFinishedFrames();
+ if (fetch_schedule_result == 0) {
+ uint32_t next_frame_vsync =
+ current_frame_vsync +
+ frame_history.PredictNextFrameVsyncInterval(vsync_period_ns);
+ int64_t next_frame_scheduled_finish =
+ (wake_time_ns - start_delay_ns) + vsync_period_ns;
+ frame_history.OnFrameStart(next_frame_vsync, next_frame_scheduled_finish);
+ if (out_next_frame_schedule) {
+ out_next_frame_schedule->vsync_count = next_frame_vsync;
+ out_next_frame_schedule->scheduled_frame_finish_ns =
+ next_frame_scheduled_finish;
+ }
+ } else {
+ frame_history.OnFrameStart(UINT32_MAX, -1);
+ }
+
+ return (fetch_schedule_result == 0 && sleep_result) ? 0 : -1;
+}
+
+extern "C" void dvrGraphicsPostEarly(DvrGraphicsContext* graphics_context) {
+ ATRACE_NAME("dvrGraphicsPostEarly");
+ ALOGI_IF(TRACE, "dvrGraphicsPostEarly");
+
+ LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_GLES);
+
+ // Note that this function can be called before or after
+ // dvrBeginRenderFrame.
+ if (!graphics_context->buffer_already_posted) {
+ graphics_context->buffer_already_posted = true;
+
+ if (!graphics_context->current_buffer) {
+ graphics_context->current_buffer =
+ graphics_context->buffer_queue->Dequeue();
+ }
+
+ auto buffer = graphics_context->current_buffer->buffer().get();
+ ATRACE_ASYNC_BEGIN("BufferPost", buffer->id());
+ int result = buffer->Post<uint64_t>(LocalHandle(), 0);
+ if (result < 0)
+ ALOGE("Buffer post failed: %d (%s)", result, strerror(-result));
+ }
+}
+
+int dvrPresent(DvrGraphicsContext* graphics_context) {
+ LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_GLES);
+
+ std::array<char, 128> buf;
+ snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
+ graphics_context->frame_history.GetCurrentFrameVsync());
+ ATRACE_NAME(buf.data());
+
+ if (!graphics_context->current_buffer) {
+ ALOGE("Error: dvrPresent called without dvrBeginRenderFrame");
+ return -EPERM;
+ }
+
+ LocalHandle fence_fd =
+ android::dvr::CreateGLSyncAndFlush(graphics_context->gl.egl_display);
+
+ ALOGI_IF(TRACE, "PostBuffer: buffer_id=%d, fence_fd=%d",
+ graphics_context->current_buffer->buffer()->id(), fence_fd.Get());
+ ALOGW_IF(!graphics_context->display_surface->visible(),
+ "PostBuffer: Posting buffer on invisible surface!!!");
+
+ auto buffer = graphics_context->current_buffer->buffer().get();
+ ATRACE_ASYNC_END("BufferDraw", buffer->id());
+ if (!graphics_context->buffer_already_posted) {
+ ATRACE_ASYNC_BEGIN("BufferPost", buffer->id());
+ int result = buffer->Post<uint64_t>(fence_fd, 0);
+ if (result < 0)
+ ALOGE("Buffer post failed: %d (%s)", result, strerror(-result));
+ }
+
+ graphics_context->frame_history.OnFrameSubmit(std::move(fence_fd));
+ graphics_context->buffer_already_posted = false;
+ graphics_context->current_buffer = nullptr;
+ return 0;
+}
+
+int dvrPresentVk(DvrGraphicsContext* graphics_context,
+ VkSemaphore submit_semaphore, uint32_t swapchain_image_index) {
+ LOG_ASSERT(graphics_context->graphics_api == DVR_GRAPHICS_API_VULKAN);
+
+ std::array<char, 128> buf;
+ snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
+ graphics_context->frame_history.GetCurrentFrameVsync());
+ ATRACE_NAME(buf.data());
+
+ if (!graphics_context->current_buffer) {
+ ALOGE("Error: dvrPresentVk called without dvrBeginRenderFrameVk");
+ return -EPERM;
+ }
+
+ // Present the specified image. Internally, this gets a fence from the
+ // Vulkan driver and passes it to DvrGraphicsContext::Post(),
+ // which in turn passes it to buffer->Post() and adds it to frame_history.
+ VkPresentInfoKHR present_info = {};
+ present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ present_info.swapchainCount = 1;
+ present_info.pSwapchains = &graphics_context->vk.swapchain;
+ present_info.pImageIndices = &swapchain_image_index;
+ present_info.waitSemaphoreCount =
+ (submit_semaphore != VK_NULL_HANDLE) ? 1 : 0;
+ present_info.pWaitSemaphores = &submit_semaphore;
+ VkResult result =
+ vkQueuePresentKHR(graphics_context->vk.present_queue, &present_info);
+ if (result != VK_SUCCESS) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+extern "C" int dvrGetFrameScheduleResults(DvrGraphicsContext* context,
+ DvrFrameScheduleResult* results,
+ int in_result_count) {
+ if (!context || !results)
+ return -EINVAL;
+
+ return context->frame_history.GetPreviousFrameResults(results,
+ in_result_count);
+}
+
+extern "C" void dvrGraphicsSurfaceSetVisible(
+ DvrGraphicsContext* graphics_context, int visible) {
+ graphics_context->display_surface->SetVisible(visible);
+}
+
+extern "C" int dvrGraphicsSurfaceGetVisible(
+ DvrGraphicsContext* graphics_context) {
+ return graphics_context->display_surface->visible() ? 1 : 0;
+}
+
+extern "C" void dvrGraphicsSurfaceSetZOrder(
+ DvrGraphicsContext* graphics_context, int z_order) {
+ graphics_context->display_surface->SetZOrder(z_order);
+}
+
+extern "C" int dvrGraphicsSurfaceGetZOrder(
+ DvrGraphicsContext* graphics_context) {
+ return graphics_context->display_surface->z_order();
+}
+
+extern "C" DvrVideoMeshSurface* dvrGraphicsVideoMeshSurfaceCreate(
+ DvrGraphicsContext* graphics_context) {
+ auto display_surface = graphics_context->display_surface;
+ // A DisplaySurface must be created prior to the creation of a
+ // VideoMeshSurface.
+ LOG_ASSERT(display_surface != nullptr);
+
+ LocalChannelHandle surface_handle = display_surface->CreateVideoMeshSurface();
+ if (!surface_handle.valid()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<DvrVideoMeshSurface> surface(new DvrVideoMeshSurface);
+ surface->client =
+ android::dvr::VideoMeshSurfaceClient::Import(std::move(surface_handle));
+
+ // TODO(jwcai) The next line is not needed...
+ auto producer_queue = surface->client->GetProducerQueue();
+ return surface.release();
+}
+
+extern "C" void dvrGraphicsVideoMeshSurfaceDestroy(
+ DvrVideoMeshSurface* surface) {
+ delete surface;
+}
+
+extern "C" void dvrGraphicsVideoMeshSurfacePresent(
+ DvrGraphicsContext* graphics_context, DvrVideoMeshSurface* surface,
+ const int eye, const float* transform) {
+ volatile android::dvr::VideoMeshSurfaceMetadata* metadata =
+ surface->client->GetMetadataBufferPtr();
+
+ const uint32_t graphics_buffer_index =
+ graphics_context->current_buffer->surface_buffer_index();
+
+ for (int i = 0; i < 4; ++i) {
+ metadata->transform[graphics_buffer_index][eye].val[i] = {
+ transform[i + 0], transform[i + 4], transform[i + 8], transform[i + 12],
+ };
+ }
+}
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdisplay/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/dvr/graphics.h b/libs/vr/libdisplay/include/dvr/graphics.h
new file mode 100644
index 0000000..50d2754
--- /dev/null
+++ b/libs/vr/libdisplay/include/dvr/graphics.h
@@ -0,0 +1,475 @@
+#ifndef DVR_GRAPHICS_H_
+#define DVR_GRAPHICS_H_
+
+#include <EGL/egl.h>
+#include <sys/cdefs.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
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR 1
+#endif
+#include <vulkan/vulkan.h>
+
+__BEGIN_DECLS
+
+// Create a stereo surface that will be lens-warped by the system.
+EGLNativeWindowType dvrCreateWarpedDisplaySurface(int* display_width,
+ int* display_height);
+EGLNativeWindowType dvrCreateDisplaySurface(void);
+
+// Display surface parameters used to specify display surface options.
+enum {
+ DVR_SURFACE_PARAMETER_NONE = 0,
+ // WIDTH
+ DVR_SURFACE_PARAMETER_WIDTH_IN,
+ // HEIGHT
+ DVR_SURFACE_PARAMETER_HEIGHT_IN,
+ // DISABLE_DISTORTION
+ DVR_SURFACE_PARAMETER_DISABLE_DISTORTION_IN,
+ // DISABLE_STABILIZATION
+ DVR_SURFACE_PARAMETER_DISABLE_STABILIZATION_IN,
+ // Disable chromatic aberration correction
+ DVR_SURFACE_PARAMETER_DISABLE_CAC_IN,
+ // ENABLE_LATE_LATCH: Enable late latching of pose data for application
+ // GPU shaders.
+ DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN,
+ // VISIBLE
+ DVR_SURFACE_PARAMETER_VISIBLE_IN,
+ // Z_ORDER
+ DVR_SURFACE_PARAMETER_Z_ORDER_IN,
+ // EXCLUDE_FROM_BLUR
+ DVR_SURFACE_PARAMETER_EXCLUDE_FROM_BLUR_IN,
+ // BLUR_BEHIND
+ DVR_SURFACE_PARAMETER_BLUR_BEHIND_IN,
+ // DISPLAY_WIDTH
+ DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT,
+ // DISPLAY_HEIGHT
+ DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT,
+ // SURFACE_WIDTH: Returns width of allocated surface buffer.
+ DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT,
+ // SURFACE_HEIGHT: Returns height of allocated surface buffer.
+ DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT,
+ // INTER_LENS_METERS: Returns float value in meters, the distance between
+ // lenses.
+ DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT,
+ // LEFT_FOV_LRBT: Return storage must have room for array of 4 floats (in
+ // radians). The layout is left, right, bottom, top as indicated by LRBT.
+ DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT,
+ // RIGHT_FOV_LRBT: Return storage must have room for array of 4 floats (in
+ // radians). The layout is left, right, bottom, top as indicated by LRBT.
+ DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT,
+ // VSYNC_PERIOD: Returns the period of the display refresh (in
+ // nanoseconds per refresh), as a 64-bit unsigned integer.
+ DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT,
+ // SURFACE_TEXTURE_TARGET_TYPE: Returns the type of texture used as the render
+ // target.
+ DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT,
+ // SURFACE_TEXTURE_TARGET_ID: Returns the texture ID used as the render
+ // target.
+ DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT,
+ // Whether the surface needs to be flipped vertically before display. Default
+ // is 0.
+ DVR_SURFACE_PARAMETER_VERTICAL_FLIP_IN,
+ // A bool indicating whether or not to create a GL context for the surface.
+ // 0: don't create a context
+ // Non-zero: create a context.
+ // Default is 1.
+ // If this value is 0, there must be a GLES 3.2 or greater context bound on
+ // the current thread at the time dvrGraphicsContextCreate is called.
+ DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN,
+ // Specify one of DVR_SURFACE_GEOMETRY_*.
+ DVR_SURFACE_PARAMETER_GEOMETRY_IN,
+ // FORMAT: One of DVR_SURFACE_FORMAT_RGBA_8888 or DVR_SURFACE_FORMAT_RGB_565.
+ // Default is DVR_SURFACE_FORMAT_RGBA_8888.
+ DVR_SURFACE_PARAMETER_FORMAT_IN,
+ // GRAPHICS_API: One of DVR_SURFACE_GRAPHICS_API_GLES or
+ // DVR_SURFACE_GRAPHICS_API_VULKAN. Default is GLES.
+ DVR_SURFACE_PARAMETER_GRAPHICS_API_IN,
+ // VK_INSTANCE: In Vulkan mode, the application creates a VkInstance and
+ // passes it in.
+ DVR_SURFACE_PARAMETER_VK_INSTANCE_IN,
+ // VK_PHYSICAL_DEVICE: In Vulkan mode, the application passes in the
+ // PhysicalDevice handle corresponding to the logical device passed to
+ // VK_DEVICE.
+ DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN,
+ // VK_DEVICE: In Vulkan mode, the application creates a VkDevice and
+ // passes it in.
+ DVR_SURFACE_PARAMETER_VK_DEVICE_IN,
+ // VK_PRESENT_QUEUE: In Vulkan mode, the application selects a
+ // presentation-compatible VkQueue and passes it in.
+ DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN,
+ // VK_PRESENT_QUEUE_FAMILY: In Vulkan mode, the application passes in the
+ // index of the queue family containing the VkQueue passed to
+ // VK_PRESENT_QUEUE.
+ DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN,
+ // VK_SWAPCHAIN_IMAGE_COUNT: In Vulkan mode, the number of swapchain images
+ // will be returned here.
+ DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT,
+ // VK_SWAPCHAIN_IMAGE_FORMAT: In Vulkan mode, the VkFormat of the swapchain
+ // images will be returned here.
+ DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT,
+};
+
+enum {
+ // Default surface type. One wide buffer with the left eye view in the left
+ // half and the right eye view in the right half.
+ DVR_SURFACE_GEOMETRY_SINGLE,
+ // Separate buffers, one per eye. The width parameters still refer to the
+ // total width (2 * eye view width).
+ DVR_SURFACE_GEOMETRY_SEPARATE_2,
+};
+
+// Surface format. Gvr only supports RGBA_8888 and RGB_565 for now, so those are
+// the only formats we provide here.
+enum {
+ DVR_SURFACE_FORMAT_RGBA_8888,
+ DVR_SURFACE_FORMAT_RGB_565,
+};
+
+enum {
+ // Graphics contexts are created for OpenGL ES client applications by default.
+ DVR_GRAPHICS_API_GLES,
+ // Create the graphics context for Vulkan client applications.
+ DVR_GRAPHICS_API_VULKAN,
+};
+
+#define DVR_SURFACE_PARAMETER_IN(name, value) \
+ { DVR_SURFACE_PARAMETER_##name##_IN, (value), NULL }
+#define DVR_SURFACE_PARAMETER_OUT(name, value) \
+ { DVR_SURFACE_PARAMETER_##name##_OUT, 0, (value) }
+#define DVR_SURFACE_PARAMETER_LIST_END \
+ { DVR_SURFACE_PARAMETER_NONE, 0, NULL }
+
+struct DvrSurfaceParameter {
+ int32_t key;
+ int64_t value;
+ void* value_out;
+};
+
+// This is a convenience struct to hold the relevant information of the HMD
+// lenses.
+struct DvrLensInfo {
+ float inter_lens_meters;
+ float left_fov[4];
+ float right_fov[4];
+};
+
+// Creates a display surface with the given parameters. The list of parameters
+// is terminated with an entry where key == DVR_SURFACE_PARAMETER_NONE.
+// For example, the parameters array could be built as follows:
+// 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,
+// };
+EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
+ struct DvrSurfaceParameter* parameters);
+
+int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
+
+int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height,
+ int* format);
+
+// NOTE: Only call the functions below on windows created with the API above.
+
+// Sets the display surface visible based on the boolean evaluation of
+// |visible|.
+void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible);
+
+// Sets the application z-order of the display surface. Higher values display on
+// top of lower values.
+void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order);
+
+// Post the next buffer early. This allows the application to race with either
+// the async EDS process or the scanline for applications that are not using
+// system distortion. When this is called, the next buffer in the queue is
+// posted for display. It is up to the application to kick its GPU rendering
+// work in time. If the rendering is incomplete there will be significant,
+// undesirable tearing artifacts.
+// It is not recommended to use this feature with system distortion.
+void dvrDisplayPostEarly(EGLNativeWindowType window);
+
+// Opaque struct that represents a graphics context, the texture swap chain,
+// and surfaces.
+typedef struct DvrGraphicsContext DvrGraphicsContext;
+
+// Create the graphics context.
+int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
+ DvrGraphicsContext** return_graphics_context);
+
+// Destroy the graphics context.
+void dvrGraphicsContextDestroy(DvrGraphicsContext* graphics_context);
+
+// For every frame a schedule is decided by the system compositor. A sample
+// schedule for two frames is shown below.
+//
+// | | |
+// |-----------------|------|-----------------|------|
+// | | |
+// V0 A1 V1 A2 V2
+//
+// V0, V1, and V2 are display vsync events. Vsync events are uniquely identified
+// throughout the DVR system by a vsync count maintained by the system
+// compositor.
+//
+// A1 and A2 indicate when the application should finish rendering its frame,
+// including all GPU work. Under normal circumstances the scheduled finish
+// finish time will be set a few milliseconds before the vsync time, to give the
+// compositor time to perform distortion and EDS on the app's buffer. For apps
+// that don't use system distortion the scheduled frame finish time will be
+// closer to the vsync time. Other factors can also effect the scheduled frame
+// finish time, e.g. whether or not the System UI is being displayed.
+typedef struct DvrFrameSchedule {
+ // vsync_count is used as a frame identifier.
+ uint32_t vsync_count;
+
+ // The time when the app should finish rendering its frame, including all GPU
+ // work.
+ int64_t scheduled_frame_finish_ns;
+} DvrFrameSchedule;
+
+// Sleep until it's time to render the next frame. This should be the first
+// function called as part of an app's render loop, which normally looks like
+// this:
+//
+// while (1) {
+// DvrFrameSchedule schedule;
+// dvrGraphicsWaitNextFrame(..., &schedule); // Sleep until it's time to
+// // render the next frame
+// pose = dvrPoseGet(schedule.vsync_count);
+// dvrBeginRenderFrame(...);
+// <render a frame using the pose>
+// dvrPresent(...); // Post the buffer
+// }
+//
+// |start_delay_ns| adjusts how long this function blocks the app from starting
+// its next frame. If |start_delay_ns| is 0, the function waits until the
+// scheduled frame finish time for the current frame, which gives the app one
+// full vsync period to render the next frame. If the app needs less than a full
+// vysnc period to render the frame, pass in a non-zero |start_delay_ns| to
+// delay the start of frame rendering further. For example, if the vsync period
+// is 11.1ms and the app takes 6ms to render a frame, consider setting this to
+// 5ms (note that the value is in nanoseconds, so 5,000,000ns) so that the app
+// finishes the frame closer to the scheduled frame finish time. Delaying the
+// start of rendering allows the app to use a more up-to-date pose for
+// rendering.
+// |start_delay_ns| must be a positive value or 0. If you're unsure what to set
+// for |start_delay_ns|, use 0.
+//
+// |out_next_frame_schedule| is an output parameter that will contain the
+// schedule for the next frame. It can be null. This function returns a negative
+// error code on failure.
+int dvrGraphicsWaitNextFrame(DvrGraphicsContext* graphics_context,
+ int64_t start_delay_ns,
+ DvrFrameSchedule* out_next_frame_schedule);
+
+// Prepares the graphics context's texture for rendering. This function should
+// be called once for each frame, ideally immediately before the first GL call
+// on the framebuffer which wraps the surface texture.
+//
+// For GL contexts, GL states are modified as follows by this function:
+// glBindTexture(GL_TEXTURE_2D, 0);
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] render_pose_orientation Head pose orientation that rendering for
+// this frame will be based off of. This must be an unmodified value
+// from DvrPoseAsync, returned by dvrPoseGet.
+// @param[in] render_pose_translation Head pose translation that rendering for
+// this frame will be based off of. This must be an unmodified value
+// from DvrPoseAsync, returned by dvrPoseGet.
+// @return 0 on success or a negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrBeginRenderFrameEds(DvrGraphicsContext* graphics_context,
+ float32x4_t render_pose_orientation,
+ float32x4_t render_pose_translation);
+int dvrBeginRenderFrameEdsVk(DvrGraphicsContext* graphics_context,
+ float32x4_t render_pose_orientation,
+ float32x4_t render_pose_translation,
+ VkSemaphore acquire_semaphore,
+ VkFence acquire_fence,
+ uint32_t* swapchain_image_index,
+ VkImageView* swapchain_image_view);
+// Same as dvrBeginRenderFrameEds, but with no EDS (asynchronous reprojection).
+//
+// For GL contexts, GL states are modified as follows by this function:
+// glBindTexture(GL_TEXTURE_2D, 0);
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @return 0 on success or a negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrBeginRenderFrame(DvrGraphicsContext* graphics_context);
+int dvrBeginRenderFrameVk(DvrGraphicsContext* graphics_context,
+ VkSemaphore acquire_semaphore, VkFence acquire_fence,
+ uint32_t* swapchain_image_index,
+ VkImageView* swapchain_image_view);
+
+// Maximum number of views per surface buffer (for multiview, multi-eye, etc).
+#define DVR_GRAPHICS_SURFACE_MAX_VIEWS 4
+
+// Output data format of late latch shader. The application can bind all or part
+// of this data with the buffer ID returned by dvrBeginRenderFrameLateLatch.
+// This struct is compatible with std140 layout for use from shaders.
+struct __attribute__((__packed__)) DvrGraphicsLateLatchData {
+ // Column-major order.
+ float view_proj_matrix[DVR_GRAPHICS_SURFACE_MAX_VIEWS][16];
+ // Column-major order.
+ float view_matrix[DVR_GRAPHICS_SURFACE_MAX_VIEWS][16];
+ // Quaternion for pose orientation from start space.
+ float pose_orientation[4];
+ // Pose translation from start space.
+ float pose_translation[4];
+};
+
+// Begin render frame with late latching of pose data. This kicks off a compute
+// shader that will read the latest head pose and then compute and output
+// matrices that can be used by application shaders.
+//
+// Matrices are computed with the following pseudo code.
+// Pose pose = getLateLatchPose();
+// out.pose_orientation = pose.orientation;
+// out.pose_translation = pose.translation;
+// mat4 head_from_center = ComputeInverseMatrix(pose);
+// for each view:
+// out.viewMatrix[view] =
+// eye_from_head_matrices[view] * head_from_center *
+// pose_offset_matrices[view];
+// out.viewProjMatrix[view] =
+// projection_matrices[view] * out.viewMatrix[view];
+//
+// For GL contexts, GL states are modified as follows by this function:
+// glBindTexture(GL_TEXTURE_2D, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0);
+// glUseProgram(0);
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] flags Specify 0.
+// @param[in] target_vsync_count The target vsync count that this frame will
+// display at. This is used for pose prediction.
+// @param[in] num_views Number of matrices in each of the following matrix array
+// parameters. Typically 2 for left and right eye views. Maximum is
+// DVR_GRAPHICS_SURFACE_MAX_VIEWS.
+// @param[in] projection_matrices Array of pointers to |num_views| matrices with
+// column-major layout. These are the application projection
+// matrices.
+// @param[in] eye_from_head_matrices Array of pointers to |num_views| matrices
+// with column-major layout. See pseudo code for how these are used.
+// @param[in] pose_offset_matrices Array of pointers to |num_views| matrices
+// with column-major layout. See pseudo code for how these are used.
+// @param[out] out_late_latch_buffer_id The GL buffer ID of the output buffer of
+// of type DvrGraphicsLateLatchData.
+// @return 0 on success or a negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrBeginRenderFrameLateLatch(DvrGraphicsContext* graphics_context,
+ uint32_t flags, uint32_t target_vsync_count,
+ int num_views,
+ const float** projection_matrices,
+ const float** eye_from_head_matrices,
+ const float** pose_offset_matrices,
+ uint32_t* out_late_latch_buffer_id);
+
+// Present a frame for display.
+// This call is normally non-blocking, unless the internal buffer queue is full.
+// @return 0 on success or a negative error code on failure.
+int dvrPresent(DvrGraphicsContext* graphics_context);
+int dvrPresentVk(DvrGraphicsContext* graphics_context,
+ VkSemaphore submit_semaphore, uint32_t swapchain_image_index);
+
+// Post the next buffer early. This allows the application to race with either
+// the async EDS process or the scanline for applications that are not using
+// system distortion. When this is called, the next buffer in the queue is
+// posted for display. It is up to the application to kick its GPU rendering
+// work in time. If the rendering is incomplete there will be significant,
+// undesirable tearing artifacts.
+// It is not recommended to use this feature with system distortion.
+void dvrGraphicsPostEarly(DvrGraphicsContext* graphics_context);
+
+// Used to retrieve frame measurement timings from dvrGetFrameScheduleResults().
+typedef struct DvrFrameScheduleResult {
+ // vsync_count is used as a frame identifier.
+ uint32_t vsync_count;
+
+ // The app's scheduled frame finish time.
+ int64_t scheduled_frame_finish_ns;
+
+ // The difference (in nanoseconds) between the scheduled finish time and the
+ // actual finish time.
+ //
+ // A value of +2ms for frame_finish_offset_ns indicates the app's frame was
+ // late and may have been skipped by the compositor for that vsync. A value of
+ // -1ms indicates the app's frame finished just ahead of schedule, as
+ // desired. A value of -6ms indicates the app's frame finished well ahead of
+ // schedule for that vsync. In that case the app may have unnecessary visual
+ // latency. Consider using the start_delay_ns parameter in
+ // dvrGraphicsWaitNextFrame() to align the app's frame finish time closer to
+ // the scheduled finish time.
+ int64_t frame_finish_offset_ns;
+} DvrFrameScheduleResult;
+
+// Retrieve the latest frame schedule results for the app. To collect all the
+// results this should be called each frame. The results for each frame are
+// returned only once.
+// The number of results written to |results| is returned on success, or a
+// negative error code on failure.
+// |graphics_context| is the context to retrieve frame schedule results for.
+// |results| is an array that will contain the frame schedule results.
+// |result_count| is the size of the |results| array. It's recommended to pass
+// in an array with 2 elements to ensure results for all frames are collected.
+int dvrGetFrameScheduleResults(DvrGraphicsContext* graphics_context,
+ DvrFrameScheduleResult* results,
+ int result_count);
+
+// Make the surface visible or hidden based on |visible|.
+// 0: hidden, Non-zero: visible.
+void dvrGraphicsSurfaceSetVisible(DvrGraphicsContext* graphics_context,
+ int visible);
+
+// Returns surface visilibity last requested by the client.
+int dvrGraphicsSurfaceGetVisible(DvrGraphicsContext* graphics_context);
+
+// Returns surface z order last requested by the client.
+int dvrGraphicsSurfaceGetZOrder(DvrGraphicsContext* graphics_context);
+
+// Sets the compositor z-order of the surface. Higher values display on
+// top of lower values.
+void dvrGraphicsSurfaceSetZOrder(DvrGraphicsContext* graphics_context,
+ int z_order);
+
+typedef struct DvrVideoMeshSurface DvrVideoMeshSurface;
+
+DvrVideoMeshSurface* dvrGraphicsVideoMeshSurfaceCreate(
+ DvrGraphicsContext* graphics_context);
+void dvrGraphicsVideoMeshSurfaceDestroy(DvrVideoMeshSurface* surface);
+
+// Present a VideoMeshSurface with the current video mesh transfromation matrix.
+void dvrGraphicsVideoMeshSurfacePresent(DvrGraphicsContext* graphics_context,
+ DvrVideoMeshSurface* surface,
+ const int eye,
+ const float* transform);
+
+__END_DECLS
+
+#endif // DVR_GRAPHICS_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
new file mode 100644
index 0000000..034b7b4
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -0,0 +1,126 @@
+#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
+#define ANDROID_DVR_DISPLAY_CLIENT_H_
+
+#include <hardware/hwcomposer.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+struct LateLatchOutput;
+
+// Abstract base class for all surface types maintained in DVR's display
+// service.
+// TODO(jwcai) Explain more, surface is a channel...
+class SurfaceClient : public pdx::Client {
+ public:
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ SurfaceType type() const { return type_; }
+
+ // Get the shared memory metadata buffer fd for this display surface. If it is
+ // not yet allocated, this will allocate it.
+ int GetMetadataBufferFd(pdx::LocalHandle* out_fd);
+
+ // Allocate the single metadata buffer for providing metadata associated with
+ // posted buffers for this surface. This can be used to provide rendered poses
+ // for EDS, for example. The buffer format is defined by the struct
+ // DisplaySurfaceMetadata.
+ // The first call to this method will allocate the buffer in via IPC to the
+ // display surface.
+ std::shared_ptr<BufferProducer> GetMetadataBuffer();
+
+ protected:
+ SurfaceClient(LocalChannelHandle channel_handle, SurfaceType type);
+ SurfaceClient(const std::string& endpoint_path, SurfaceType type);
+
+ private:
+ SurfaceType type_;
+ std::shared_ptr<BufferProducer> metadata_buffer_;
+};
+
+// DisplaySurfaceClient represents the client interface to a displayd display
+// surface.
+class DisplaySurfaceClient
+ : public pdx::ClientBase<DisplaySurfaceClient, SurfaceClient> {
+ public:
+ using LocalHandle = pdx::LocalHandle;
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+ int format() const { return format_; }
+ int usage() const { return usage_; }
+ int flags() const { return flags_; }
+ int z_order() const { return z_order_; }
+ bool visible() const { return visible_; }
+
+ void SetVisible(bool visible);
+ void SetZOrder(int z_order);
+ void SetExcludeFromBlur(bool exclude_from_blur);
+ void SetBlurBehind(bool blur_behind);
+ void SetAttributes(const DisplaySurfaceAttributes& attributes);
+
+ // |out_buffer_index| will receive a unique index for this buffer within the
+ // surface. The first buffer gets 0, second gets 1, and so on. This index
+ // can be used to deliver metadata for buffers that are queued for display.
+ std::shared_ptr<BufferProducer> AllocateBuffer(uint32_t* out_buffer_index);
+ std::shared_ptr<BufferProducer> AllocateBuffer() {
+ return AllocateBuffer(nullptr);
+ }
+
+ // Get the shared memory metadata buffer for this display surface. If it is
+ // not yet allocated, this will allocate it.
+ volatile DisplaySurfaceMetadata* GetMetadataBufferPtr();
+
+ // Create a VideoMeshSurface that is attached to the display sruface.
+ LocalChannelHandle CreateVideoMeshSurface();
+
+ private:
+ friend BASE;
+
+ DisplaySurfaceClient(int width, int height, int format, int usage, int flags);
+
+ int width_;
+ int height_;
+ int format_;
+ int usage_;
+ int flags_;
+ int z_order_;
+ bool visible_;
+ bool exclude_from_blur_;
+ bool blur_behind_;
+ DisplaySurfaceMetadata* mapped_metadata_buffer_;
+
+ DisplaySurfaceClient(const DisplaySurfaceClient&) = delete;
+ void operator=(const DisplaySurfaceClient&) = delete;
+};
+
+class DisplayClient : public pdx::ClientBase<DisplayClient> {
+ public:
+ int GetDisplayMetrics(SystemDisplayMetrics* metrics);
+ pdx::Status<void> SetViewerParams(const ViewerParams& viewer_params);
+
+ // Pull the latest eds pose data from the display service renderer
+ int GetLastFrameEdsTransform(LateLatchOutput* ll_out);
+
+ int EnterVrMode();
+ int ExitVrMode();
+
+ std::unique_ptr<DisplaySurfaceClient> CreateDisplaySurface(
+ int width, int height, int format, int usage, int flags);
+
+ private:
+ friend BASE;
+
+ explicit DisplayClient(int* error = nullptr);
+
+ DisplayClient(const DisplayClient&) = delete;
+ void operator=(const DisplayClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
new file mode 100644
index 0000000..f28c1e4
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -0,0 +1,73 @@
+#ifndef DVR_DISPLAY_MANAGER_CLIENT_H_
+#define DVR_DISPLAY_MANAGER_CLIENT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrDisplayManagerClient DvrDisplayManagerClient;
+typedef struct DvrDisplayManagerClientSurfaceList
+ DvrDisplayManagerClientSurfaceList;
+typedef struct DvrDisplayManagerClientSurfaceBuffers
+ DvrDisplayManagerClientSurfaceBuffers;
+
+DvrDisplayManagerClient* dvrDisplayManagerClientCreate();
+
+void dvrDisplayManagerClientDestroy(DvrDisplayManagerClient* client);
+
+// If successful, populates |surface_list| with a list of application
+// surfaces the display is currently using.
+//
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrDisplayManagerClientGetSurfaceList(
+ DvrDisplayManagerClient* client,
+ DvrDisplayManagerClientSurfaceList** surface_list);
+
+void dvrDisplayManagerClientSurfaceListDestroy(
+ DvrDisplayManagerClientSurfaceList* surface_list);
+
+// @return Returns the number of surfaces in the list.
+size_t dvrDisplayManagerClientSurfaceListGetSize(
+ DvrDisplayManagerClientSurfaceList* surface_list);
+
+// @return Return a unique identifier for a client surface. The identifier can
+// be used to query for other surface properties.
+int dvrDisplayManagerClientSurfaceListGetSurfaceId(
+ DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// @return Returns the stacking order of the client surface at |index|.
+int dvrDisplayManagerClientSurfaceListGetClientZOrder(
+ DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// @return Returns true if the client surface is visible, false otherwise.
+bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
+ DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// Populates |surface_buffers| with the list of buffers for |surface_id|.
+// |surface_id| should be a valid ID from the list of surfaces.
+//
+// @return Returns 0 on success. Otherwise it returns a negative error value.
+int dvrDisplayManagerClientGetSurfaceBuffers(
+ DvrDisplayManagerClient* client, int surface_id,
+ DvrDisplayManagerClientSurfaceBuffers** surface_buffers);
+
+void dvrDisplayManagerClientSurfaceBuffersDestroy(
+ DvrDisplayManagerClientSurfaceBuffers* surface_buffers);
+
+// @return Returns the number of buffers.
+size_t dvrDisplayManagerClientSurfaceBuffersGetSize(
+ DvrDisplayManagerClientSurfaceBuffers* surface_buffers);
+
+// @return Returns the file descriptor for the buffer consumer at |index|.
+int dvrDisplayManagerClientSurfaceBuffersGetFd(
+ DvrDisplayManagerClientSurfaceBuffers* surface_buffers, size_t index);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
new file mode 100644
index 0000000..645ccce
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
+#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
+
+#include <vector>
+
+#include <pdx/client.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class BufferConsumer;
+
+class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
+ public:
+ ~DisplayManagerClient() override;
+
+ int GetSurfaceList(std::vector<DisplaySurfaceInfo>* surface_list);
+
+ int GetSurfaceBuffers(
+ int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers);
+
+ private:
+ friend BASE;
+
+ DisplayManagerClient();
+
+ DisplayManagerClient(const DisplayManagerClient&) = delete;
+ void operator=(const DisplayManagerClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_rpc.h b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
new file mode 100644
index 0000000..6150b35
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
@@ -0,0 +1,342 @@
+#ifndef ANDROID_DVR_DISPLAY_RPC_H_
+#define ANDROID_DVR_DISPLAY_RPC_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <map>
+
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/variant.h>
+#include <private/dvr/display_types.h>
+
+namespace android {
+namespace dvr {
+
+struct SystemDisplayMetrics {
+ uint32_t display_native_width;
+ uint32_t display_native_height;
+ uint32_t display_x_dpi;
+ uint32_t display_y_dpi;
+ uint32_t distorted_width;
+ uint32_t distorted_height;
+ uint32_t vsync_period_ns;
+ uint32_t hmd_ipd_mm;
+ float inter_lens_distance_m;
+ std::array<float, 4> left_fov_lrbt;
+ std::array<float, 4> right_fov_lrbt;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(SystemDisplayMetrics, display_native_width,
+ display_native_height, display_x_dpi, display_y_dpi,
+ distorted_width, distorted_height, vsync_period_ns,
+ hmd_ipd_mm, inter_lens_distance_m, left_fov_lrbt,
+ right_fov_lrbt);
+};
+
+using SurfaceType = uint32_t;
+struct SurfaceTypeEnum {
+ enum : SurfaceType {
+ Normal = DVR_SURFACE_TYPE_NORMAL,
+ VideoMesh = DVR_SURFACE_TYPE_VIDEO_MESH,
+ Overlay = DVR_SURFACE_TYPE_OVERLAY,
+ };
+};
+
+using DisplaySurfaceFlags = uint32_t;
+enum class DisplaySurfaceFlagsEnum : DisplaySurfaceFlags {
+ DisableSystemEds = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS,
+ DisableSystemDistortion = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION,
+ VerticalFlip = DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP,
+ SeparateGeometry = DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2,
+ DisableSystemCac = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC,
+};
+
+using DisplaySurfaceInfoFlags = uint32_t;
+enum class DisplaySurfaceInfoFlagsEnum : DisplaySurfaceInfoFlags {
+ BuffersChanged = DVR_DISPLAY_SURFACE_ITEM_FLAGS_BUFFERS_CHANGED,
+};
+
+using DisplaySurfaceAttributeValue =
+ pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
+ std::array<float, 3>, std::array<float, 4>,
+ std::array<float, 16>>;
+using DisplaySurfaceAttribute = uint32_t;
+struct DisplaySurfaceAttributeEnum {
+ enum : DisplaySurfaceAttribute {
+ ZOrder = DVR_DISPLAY_SURFACE_ATTRIBUTE_Z_ORDER,
+ Visible = DVR_DISPLAY_SURFACE_ATTRIBUTE_VISIBLE,
+ // Manager only.
+ Blur = DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR,
+ // Client only.
+ ExcludeFromBlur = DVR_DISPLAY_SURFACE_ATTRIBUTE_EXCLUDE_FROM_BLUR,
+ BlurBehind = DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR_BEHIND,
+ };
+
+ static std::string ToString(DisplaySurfaceAttribute attribute) {
+ switch (attribute) {
+ case ZOrder:
+ return "z-order";
+ case Visible:
+ return "visible";
+ case Blur:
+ return "blur";
+ case ExcludeFromBlur:
+ return "exclude-from-blur";
+ case BlurBehind:
+ return "blur-behind";
+ default:
+ return "unknown";
+ }
+ }
+};
+
+using DisplaySurfaceAttributes =
+ std::map<DisplaySurfaceAttribute, DisplaySurfaceAttributeValue>;
+
+struct DisplaySurfaceInfo {
+ int surface_id;
+ int process_id;
+ SurfaceType type;
+ DisplaySurfaceFlags flags;
+ DisplaySurfaceInfoFlags info_flags;
+ DisplaySurfaceAttributes client_attributes;
+ DisplaySurfaceAttributes manager_attributes;
+
+ // Convenience accessors.
+ bool IsClientVisible() const {
+ const auto* variant =
+ FindClientAttribute(DisplaySurfaceAttributeEnum::Visible);
+ bool bool_value;
+ if (variant && pdx::rpc::IfAnyOf<int32_t, int64_t, bool, float>::Get(
+ variant, &bool_value))
+ return bool_value;
+ else
+ return false;
+ }
+
+ int ClientZOrder() const {
+ const auto* variant =
+ FindClientAttribute(DisplaySurfaceAttributeEnum::ZOrder);
+ int int_value;
+ if (variant &&
+ pdx::rpc::IfAnyOf<int32_t, int64_t, float>::Get(variant, &int_value))
+ return int_value;
+ else
+ return 0;
+ }
+
+ private:
+ const DisplaySurfaceAttributeValue* FindClientAttribute(
+ DisplaySurfaceAttribute key) const {
+ auto search = client_attributes.find(key);
+ return (search != client_attributes.end()) ? &search->second : nullptr;
+ }
+
+ PDX_SERIALIZABLE_MEMBERS(DisplaySurfaceInfo, surface_id, process_id, type,
+ flags, info_flags, client_attributes,
+ manager_attributes);
+};
+
+struct VideoMeshSurfaceBufferMetadata {
+ int64_t timestamp_ns;
+};
+
+struct AlignmentMarker {
+ public:
+ float horizontal;
+ float vertical;
+
+ PDX_SERIALIZABLE_MEMBERS(AlignmentMarker, horizontal, vertical);
+};
+
+struct DaydreamInternalParams {
+ public:
+ int32_t version;
+ std::vector<AlignmentMarker> alignment_markers;
+
+ PDX_SERIALIZABLE_MEMBERS(DaydreamInternalParams, version, alignment_markers);
+};
+
+struct ViewerParams {
+ public:
+ // TODO(hendrikw): Do we need viewer_vendor_name and viewer_model_name?
+ float screen_to_lens_distance;
+ float inter_lens_distance;
+ float screen_center_to_lens_distance;
+ std::vector<float> left_eye_field_of_view_angles;
+
+ enum VerticalAlignmentType : int32_t {
+ BOTTOM = 0, // phone rests against a fixed bottom tray
+ CENTER = 1, // phone screen assumed to be centered w.r.t. lenses
+ TOP = 2 // phone rests against a fixed top tray
+ };
+
+ enum EyeOrientation : int32_t {
+ kCCW0Degrees = 0,
+ kCCW90Degrees = 1,
+ kCCW180Degrees = 2,
+ kCCW270Degrees = 3,
+ kCCW0DegreesMirrored = 4,
+ kCCW90DegreesMirrored = 5,
+ kCCW180DegreesMirrored = 6,
+ kCCW270DegreesMirrored = 7
+ };
+
+ VerticalAlignmentType vertical_alignment;
+ std::vector<EyeOrientation> eye_orientations;
+
+ float tray_to_lens_distance;
+
+ std::vector<float> distortion_coefficients_r;
+ std::vector<float> distortion_coefficients_g;
+ std::vector<float> distortion_coefficients_b;
+
+ DaydreamInternalParams daydream_internal;
+
+ PDX_SERIALIZABLE_MEMBERS(ViewerParams, screen_to_lens_distance,
+ inter_lens_distance, screen_center_to_lens_distance,
+ left_eye_field_of_view_angles, vertical_alignment,
+ eye_orientations, tray_to_lens_distance,
+ distortion_coefficients_r, distortion_coefficients_g,
+ distortion_coefficients_b, daydream_internal);
+};
+
+struct DisplayRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/display/client";
+
+ // Op codes.
+ enum {
+ kOpGetMetrics = 0,
+ kOpGetEdsCapture,
+ kOpCreateSurface,
+ kOpAllocateBuffer,
+ kOpSetAttributes,
+ kOpGetMetadataBuffer,
+ kOpCreateVideoMeshSurface,
+ kOpVideoMeshSurfaceCreateProducerQueue,
+ kOpEnterVrMode,
+ kOpExitVrMode,
+ kOpSetViewerParams
+ };
+
+ // Aliases.
+ using ByteBuffer = pdx::rpc::BufferWrapper<std::vector<uint8_t>>;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, SystemDisplayMetrics(Void));
+ PDX_REMOTE_METHOD(GetEdsCapture, kOpGetEdsCapture, ByteBuffer(Void));
+ PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
+ int(int width, int height, int format, int usage,
+ DisplaySurfaceFlags flags));
+ PDX_REMOTE_METHOD(AllocateBuffer, kOpAllocateBuffer,
+ std::pair<std::uint32_t, LocalChannelHandle>(Void));
+ PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
+ int(const DisplaySurfaceAttributes& attributes));
+ PDX_REMOTE_METHOD(GetMetadataBuffer, kOpGetMetadataBuffer,
+ LocalChannelHandle(Void));
+ // VideoMeshSurface methods
+ PDX_REMOTE_METHOD(CreateVideoMeshSurface, kOpCreateVideoMeshSurface,
+ LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(VideoMeshSurfaceCreateProducerQueue,
+ kOpVideoMeshSurfaceCreateProducerQueue,
+ LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(EnterVrMode, kOpEnterVrMode, int(Void));
+ PDX_REMOTE_METHOD(ExitVrMode, kOpExitVrMode, int(Void));
+ PDX_REMOTE_METHOD(SetViewerParams, kOpSetViewerParams,
+ void(const ViewerParams& viewer_params));
+};
+
+struct DisplayManagerRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/display/manager";
+
+ // Op codes.
+ enum {
+ kOpGetSurfaceList = 0,
+ kOpGetSurfaceBuffers,
+ kOpUpdateSurfaces,
+ };
+
+ // Aliases.
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(GetSurfaceList, kOpGetSurfaceList,
+ std::vector<DisplaySurfaceInfo>(Void));
+ PDX_REMOTE_METHOD(GetSurfaceBuffers, kOpGetSurfaceBuffers,
+ std::vector<LocalChannelHandle>(int surface_id));
+ PDX_REMOTE_METHOD(
+ UpdateSurfaces, kOpUpdateSurfaces,
+ int(const std::map<int, DisplaySurfaceAttributes>& updates));
+};
+
+struct ScreenshotData {
+ int width;
+ int height;
+ std::vector<uint8_t> buffer;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(ScreenshotData, width, height, buffer);
+};
+
+struct DisplayScreenshotRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/display/screenshot";
+
+ // Op codes.
+ enum {
+ kOpGetFormat = 0,
+ kOpTakeScreenshot,
+ };
+
+ using Void = pdx::rpc::Void;
+
+ PDX_REMOTE_METHOD(GetFormat, kOpGetFormat, int(Void));
+ PDX_REMOTE_METHOD(TakeScreenshot, kOpTakeScreenshot,
+ ScreenshotData(int layer_index));
+};
+
+struct VSyncSchedInfo {
+ int64_t vsync_period_ns;
+ int64_t timestamp_ns;
+ uint32_t next_vsync_count;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
+ next_vsync_count);
+};
+
+struct DisplayVSyncRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/display/vsync";
+
+ // Op codes.
+ enum {
+ kOpWait = 0,
+ kOpAck,
+ kOpGetLastTimestamp,
+ kOpGetSchedInfo,
+ kOpAcknowledge,
+ };
+
+ // Aliases.
+ using Void = pdx::rpc::Void;
+ using Timestamp = int64_t;
+
+ // Methods.
+ PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
+ PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
+ PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
+ PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, int(Void));
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_RPC_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_types.h b/libs/vr/libdisplay/include/private/dvr/display_types.h
new file mode 100644
index 0000000..2bd02bd
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_types.h
@@ -0,0 +1,83 @@
+#ifndef ANDROID_DVR_DISPLAY_TYPES_H_
+#define ANDROID_DVR_DISPLAY_TYPES_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 <cutils/native_handle.h>
+
+// DVR display-related data types.
+
+enum dvr_display_surface_type {
+ // Normal display surface meant to be used by applications' GL context to
+ // render into.
+ DVR_SURFACE_TYPE_NORMAL = 0,
+
+ // VideoMeshSurface is used to composite video frames into the 3D world.
+ DVR_SURFACE_TYPE_VIDEO_MESH,
+
+ // System overlay surface type. This is not currently in use.
+ DVR_SURFACE_TYPE_OVERLAY,
+};
+
+enum dvr_display_surface_flags {
+ DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS = (1 << 0),
+ DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION = (1 << 1),
+ DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP = (1 << 2),
+ DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2 = (1 << 3),
+ DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC = (1 << 4),
+};
+
+enum dvr_display_surface_item_flags {
+ DVR_DISPLAY_SURFACE_ITEM_FLAGS_BUFFERS_CHANGED = (1 << 0),
+};
+
+enum dvr_display_surface_attribute {
+ DVR_DISPLAY_SURFACE_ATTRIBUTE_Z_ORDER = (1<<0),
+ DVR_DISPLAY_SURFACE_ATTRIBUTE_VISIBLE = (1<<1),
+ DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR = (1<<2),
+ DVR_DISPLAY_SURFACE_ATTRIBUTE_EXCLUDE_FROM_BLUR = (1<<3),
+ DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR_BEHIND = (1<<4),
+};
+
+// Maximum number of buffers for a surface. Each buffer represents a single
+// frame and may actually be a buffer array if multiview rendering is in use.
+// Define so that it can be used in shader code.
+#define kSurfaceBufferMaxCount 4
+
+// Maximum number of views per surface. Each eye is a view, for example.
+#define kSurfaceViewMaxCount 4
+
+namespace android {
+namespace dvr {
+
+struct __attribute__((packed, aligned(16))) DisplaySurfaceMetadata {
+ // Array of orientations and translations corresponding with surface buffers.
+ // The index is associated with each allocated buffer by DisplaySurface and
+ // communicated to clients.
+ // The maximum number of buffers is hard coded here as 4 so that we can bind
+ // this data structure in GPU shaders.
+ float32x4_t orientation[kSurfaceBufferMaxCount];
+ float32x4_t translation[kSurfaceBufferMaxCount];
+};
+
+struct __attribute__((packed, aligned(16))) VideoMeshSurfaceMetadata {
+ // Array of transform matrices corresponding with surface buffers.
+ // Note that The index is associated with each allocated buffer by
+ // DisplaySurface instead of VideoMeshSurface due to the fact that the
+ // metadata here is interpreted as video mesh's transformation in each
+ // application's rendering frame.
+ float32x4x4_t transform[4][2];
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_TYPES_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h b/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h
new file mode 100644
index 0000000..b03eeaa
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
+#define ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
+
+#include <android/native_window.h>
+#include <ui/ANativeObjectBase.h>
+
+namespace android {
+namespace dvr {
+
+// DummyNativeWindow is an implementation of ANativeWindow that is
+// essentially empty and is used as a surface placeholder during context
+// creation for contexts that we don't intend to call eglSwapBuffers on.
+class DummyNativeWindow
+ : public ANativeObjectBase<ANativeWindow, DummyNativeWindow,
+ LightRefBase<DummyNativeWindow> > {
+ public:
+ DummyNativeWindow();
+
+ private:
+ static int Query(const ANativeWindow* window, int what, int* value);
+ static int Perform(ANativeWindow* window, int operation, ...);
+
+ DummyNativeWindow(const DummyNativeWindow&) = delete;
+ void operator=(DummyNativeWindow&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/frame_history.h b/libs/vr/libdisplay/include/private/dvr/frame_history.h
new file mode 100644
index 0000000..53e0717
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/frame_history.h
@@ -0,0 +1,71 @@
+#ifndef ANDROID_DVR_FRAME_HISTORY_H_
+#define ANDROID_DVR_FRAME_HISTORY_H_
+
+#include <dvr/graphics.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/ring_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// FrameHistory tracks frame times from the start of rendering commands to when
+// the buffer is ready.
+class FrameHistory {
+ public:
+ FrameHistory();
+ explicit FrameHistory(int pending_frame_buffer_size);
+
+ void Reset(int pending_frame_buffer_size);
+
+ // Call when starting rendering commands (i.e. dvrBeginRenderFrame).
+ void OnFrameStart(uint32_t scheduled_vsync, int64_t scheduled_finish_ns);
+
+ // Call when rendering commands are finished (i.e. dvrPresent).
+ void OnFrameSubmit(android::pdx::LocalHandle&& fence);
+
+ // Call once per frame to see if any pending frames have finished.
+ void CheckForFinishedFrames();
+
+ // Uses the recently completed frame render times to predict how long the next
+ // frame will take, in vsync intervals. For example if the predicted frame
+ // time is 10ms and the vsync interval is 11ms, this will return 1. If the
+ // predicted frame time is 12ms and the vsync interval is 11ms, this will
+ // return 2.
+ int PredictNextFrameVsyncInterval(int64_t vsync_period_ns) const;
+
+ // Returns results for recently completed frames. Each frame's result is
+ // returned only once.
+ int GetPreviousFrameResults(DvrFrameScheduleResult* results,
+ int result_count);
+
+ // Gets the vsync count for the most recently started frame. If there are no
+ // started frames this will return UINT32_MAX.
+ uint32_t GetCurrentFrameVsync() const;
+
+ private:
+ struct PendingFrame {
+ int64_t start_ns;
+ uint32_t scheduled_vsync;
+ int64_t scheduled_finish_ns;
+ android::pdx::LocalHandle fence;
+
+ PendingFrame();
+ PendingFrame(int64_t start_ns, uint32_t scheduled_vsync,
+ int64_t scheduled_finish_ns,
+ android::pdx::LocalHandle&& fence);
+
+ PendingFrame(PendingFrame&&) = default;
+ PendingFrame& operator=(PendingFrame&&) = default;
+ PendingFrame(const PendingFrame&) = delete;
+ PendingFrame& operator=(const PendingFrame&) = delete;
+ };
+
+ RingBuffer<PendingFrame> pending_frames_;
+ RingBuffer<DvrFrameScheduleResult> finished_frames_;
+ RingBuffer<int64_t> frame_duration_history_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_FRAME_HISTORY_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h b/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h
new file mode 100644
index 0000000..1d75335
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_GL_FENCED_FLUSH_H_
+#define ANDROID_DVR_GL_FENCED_FLUSH_H_
+
+#include <EGL/egl.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace dvr {
+
+// Creates a EGL_SYNC_NATIVE_FENCE_ANDROID and flushes. Returns the fence as a
+// file descriptor.
+pdx::LocalHandle CreateGLSyncAndFlush(EGLDisplay display);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GL_FENCED_FLUSH_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/graphics_private.h b/libs/vr/libdisplay/include/private/dvr/graphics_private.h
new file mode 100644
index 0000000..57c99da
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/graphics_private.h
@@ -0,0 +1,39 @@
+#ifndef ANDROID_DVR_GRAPHICS_PRIVATE_H_
+#define ANDROID_DVR_GRAPHICS_PRIVATE_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 <sys/cdefs.h>
+
+#include <dvr/graphics.h>
+
+__BEGIN_DECLS
+
+// Sets the pose used by the system for EDS. If dvrBeginRenderFrameEds() or
+// dvrBeginRenderFrameLateLatch() are called instead of dvrBeginRenderFrame()
+// it's not necessary to call this function. If this function is used, the call
+// must be made after dvrBeginRenderFrame() and before dvrPresent().
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] render_pose_orientation Head pose orientation that rendering for
+// this frame will be based off of. This must be an unmodified value
+// from DvrPoseAsync, returned by dvrPoseGet.
+// @param[in] render_pose_translation Head pose translation that rendering for
+// this frame will be based off of. This must be an unmodified value
+// from DvrPoseAsync, returned by dvrPoseGet.
+// @return 0 on success or a negative error code on failure.
+int dvrSetEdsPose(DvrGraphicsContext* graphics_context,
+ float32x4_t render_pose_orientation,
+ float32x4_t render_pose_translation);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_GRAPHICS_PRIVATE_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/late_latch.h b/libs/vr/libdisplay/include/private/dvr/late_latch.h
new file mode 100644
index 0000000..d0eff51
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/late_latch.h
@@ -0,0 +1,186 @@
+#ifndef ANDROID_DVR_LATE_LATCH_H_
+#define ANDROID_DVR_LATE_LATCH_H_
+
+#include <atomic>
+#include <thread>
+#include <vector>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/types.h>
+
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+// Input data for late latch compute shader.
+struct LateLatchInput {
+ // For app late latch:
+ mat4 eye_from_head_mat[kSurfaceViewMaxCount];
+ mat4 proj_mat[kSurfaceViewMaxCount];
+ mat4 pose_offset[kSurfaceViewMaxCount];
+ // For EDS late latch only:
+ mat4 eds_mat1[kSurfaceViewMaxCount];
+ mat4 eds_mat2[kSurfaceViewMaxCount];
+ // For both app and EDS late latch:
+ uint32_t pose_index;
+ uint32_t render_pose_index;
+};
+
+// Output data for late latch shader. The application can use all or part of
+// this data by calling LateLatch::BindUniformBuffer.
+// This struct matches the layout of DvrGraphicsLateLatchData.
+struct LateLatchOutput {
+ mat4 view_proj_matrix[kSurfaceViewMaxCount];
+ mat4 view_matrix[kSurfaceViewMaxCount];
+ vec4 pose_quaternion;
+ vec4 pose_translation;
+};
+
+// LateLatch provides a facility for GL workloads to acquire a late-adjusted
+// model-view projection matrix, adjusted based on the position/quaternion pose
+// read from a buffer that is being written to asynchronously. The adjusted
+// MVP matrix is written to a GL buffer object via GL transform feedback.
+class LateLatch {
+ public:
+ enum BufferType {
+ kViewProjMatrix,
+ kViewMatrix,
+ kPoseQuaternion,
+ kPoseTranslation,
+ // Max transform feedback count is 4, so no more buffers can go here.
+ kNumBuffers,
+ };
+
+ static size_t GetBufferSize(BufferType type) {
+ switch (type) {
+ default:
+ case kViewProjMatrix:
+ case kViewMatrix:
+ return 4 * 4 * sizeof(float);
+ case kPoseQuaternion:
+ case kPoseTranslation:
+ return 4 * sizeof(float);
+ }
+ }
+
+ static size_t GetBufferOffset(BufferType type, int view) {
+ switch (type) {
+ default:
+ case kViewProjMatrix:
+ return offsetof(LateLatchOutput, view_proj_matrix) +
+ GetBufferSize(type) * view;
+ case kViewMatrix:
+ return offsetof(LateLatchOutput, view_matrix) +
+ GetBufferSize(type) * view;
+ case kPoseQuaternion:
+ return offsetof(LateLatchOutput, pose_quaternion);
+ case kPoseTranslation:
+ return offsetof(LateLatchOutput, pose_translation);
+ }
+ }
+
+ explicit LateLatch(bool is_app_late_latch);
+ LateLatch(bool is_app_late_latch, pdx::LocalHandle&& surface_metadata_fd);
+ ~LateLatch();
+
+ // Bind the late-latch output data as a GL_UNIFORM_BUFFER. For example,
+ // to bind just the view_matrix from the output:
+ // BindUniformBuffer(BINDING, offsetof(LateLatchOutput, view_matrix),
+ // sizeof(mat4));
+ // buffer_index is the index of one of the output buffers if more than 1 were
+ // requested in the constructor.
+ void BindUniformBuffer(GLuint ubo_binding, size_t offset, size_t size) const {
+ glBindBufferRange(GL_UNIFORM_BUFFER, ubo_binding, output_buffer_id_, offset,
+ size);
+ }
+
+ void BindUniformBuffer(GLuint ubo_binding, BufferType type, int view) const {
+ glBindBufferRange(GL_UNIFORM_BUFFER, ubo_binding, output_buffer_id_,
+ GetBufferOffset(type, view), GetBufferSize(type));
+ }
+
+ GLuint output_buffer_id() const { return output_buffer_id_; }
+
+ void UnbindUniformBuffer(GLuint ubo_binding) const {
+ glBindBufferBase(GL_UNIFORM_BUFFER, ubo_binding, 0);
+ }
+
+ void CaptureOutputData(LateLatchOutput* data) const;
+
+ // Add the late latch GL commands for this frame. This should be done just
+ // before the first application draw calls that are dependent on the head
+ // latest head pose.
+ //
+ // For efficiency, the application projection and eye_from_head matrices are
+ // passed through the late latch shader and output in various combinations to
+ // allow for both simple application vertex shaders that can take the view-
+ // projection matrix as-is and shaders that need to access the view matrix
+ // separately.
+ //
+ // GL state must be reset to default for this call.
+ void AddLateLatch(const LateLatchInput& data) const;
+
+ // After calling AddEdsLateLatch one or more times, this method must be called
+ // to add the necessary GL memory barrier to ensure late latch outputs are
+ // written before the EDS and warp shaders read them.
+ void PostEdsLateLatchBarrier() const {
+ // The transform feedback buffer is going to be read as a uniform by EDS,
+ // so we need a uniform memory barrier.
+ glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+ }
+
+ // Typically not for use by application code. This method adds the EDS late
+ // latch that will adjust the application framebuffer with the latest head
+ // pose.
+ // buffer_index is the index of one of the output buffers if more than 1 were
+ // requested in the constructor.
+ void AddEdsLateLatch(const LateLatchInput& data,
+ GLuint render_pose_buffer_object) const;
+
+ // For debugging purposes, capture the output during the next call to
+ // AddLateLatch. Set to NULL to reset.
+ void SetLateLatchDataCapture(LateLatchOutput* app_late_latch) {
+ app_late_latch_output_ = app_late_latch;
+ }
+
+ // For debugging purposes, capture the output during the next call to
+ // AddEdsLateLatch. Set to NULL to reset.
+ void SetEdsLateLatchDataCapture(LateLatchOutput* eds_late_latch) {
+ eds_late_latch_output_ = eds_late_latch;
+ }
+
+ private:
+ LateLatch(const LateLatch&) = delete;
+ LateLatch& operator=(const LateLatch&) = delete;
+
+ void LoadLateLatchShader();
+
+ // Late latch shader.
+ ShaderProgram late_latch_program_;
+
+ // Async pose ring buffer object.
+ GLuint pose_buffer_object_;
+
+ GLuint metadata_buffer_id_;
+
+ // Pose matrix buffers
+ GLuint input_buffer_id_;
+ GLuint output_buffer_id_;
+
+ bool is_app_late_latch_;
+ // During development, these can be used to capture the pose output data.
+ LateLatchOutput* app_late_latch_output_;
+ LateLatchOutput* eds_late_latch_output_;
+
+ DvrPose* pose_client_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LATE_LATCH_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h b/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h
new file mode 100644
index 0000000..87e9c9f
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_QUEUE_H_
+#define ANDROID_DVR_NATIVE_BUFFER_QUEUE_H_
+
+#include <semaphore.h>
+
+#include <mutex>
+#include <vector>
+
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/ring_buffer.h>
+
+#include "display_client.h"
+
+namespace android {
+namespace dvr {
+
+// NativeBufferQueue manages a queue of NativeBufferProducers allocated from a
+// DisplaySurfaceClient. Buffers are automatically re-enqueued when released by
+// the consumer side.
+class NativeBufferQueue {
+ public:
+ // Create a queue with the given number of free buffers.
+ NativeBufferQueue(const std::shared_ptr<DisplaySurfaceClient>& surface,
+ size_t capacity);
+ NativeBufferQueue(EGLDisplay display,
+ const std::shared_ptr<DisplaySurfaceClient>& surface,
+ size_t capacity);
+ ~NativeBufferQueue();
+
+ std::shared_ptr<DisplaySurfaceClient> surface() const { return surface_; }
+
+ // Dequeue a buffer from the free queue, blocking until one is available.
+ NativeBufferProducer* Dequeue();
+
+ // Enqueue a buffer at the end of the free queue.
+ void Enqueue(NativeBufferProducer* buf);
+
+ // Get the number of free buffers in the queue.
+ size_t GetFreeBufferCount() const;
+
+ // Get the total number of buffers managed by this queue.
+ size_t GetQueueCapacity() const;
+
+ // Accessors for display surface buffer attributes.
+ int width() const { return surface_->width(); }
+ int height() const { return surface_->height(); }
+ int format() const { return surface_->format(); }
+ int usage() const { return surface_->usage(); }
+
+ private:
+ // Wait for buffers to be released and enqueue them.
+ bool WaitForBuffers();
+
+ std::shared_ptr<DisplaySurfaceClient> surface_;
+
+ // A list of strong pointers to the buffers, used for managing buffer
+ // lifetime.
+ std::vector<android::sp<NativeBufferProducer>> buffers_;
+
+ // Used to implement queue semantics.
+ RingBuffer<NativeBufferProducer*> buffer_queue_;
+
+ // Epoll fd used to wait for BufferHub events.
+ int epoll_fd_;
+
+ NativeBufferQueue(const NativeBufferQueue&) = delete;
+ void operator=(NativeBufferQueue&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NATIVE_BUFFER_QUEUE_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/screenshot_client.h b/libs/vr/libdisplay/include/private/dvr/screenshot_client.h
new file mode 100644
index 0000000..b6fc859
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/screenshot_client.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_DVR_SCREENSHOT_CLIENT_H_
+#define ANDROID_DVR_SCREENSHOT_CLIENT_H_
+
+#include <memory>
+#include <vector>
+
+#include <pdx/client.h>
+#include <private/dvr/display_rpc.h>
+#include <system/graphics.h>
+
+namespace android {
+namespace dvr {
+
+// Represents a connection to the screenshot service, which allows capturing an
+// upcoming frame as it is being rendered to the display.
+class ScreenshotClient : public pdx::ClientBase<ScreenshotClient> {
+ public:
+ int format() const { return format_; }
+
+ // Attempts to take a screenshot. If successful, sets *data to the contents
+ // of the screenshot and returns zero. Otherwise, returns a negative error
+ // code.
+ // |index| is used to match the requested buffer with various buffer layers.
+ int Take(std::vector<uint8_t>* data, int index, int* return_width,
+ int* return_height);
+
+ private:
+ friend BASE;
+
+ ScreenshotClient();
+
+ // Layout information for screenshots.
+ int format_;
+
+ ScreenshotClient(const ScreenshotClient&) = delete;
+ void operator=(const ScreenshotClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SCREENSHOT_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
new file mode 100644
index 0000000..a2659a6
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+#define ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+
+#include <base/macros.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_client.h>
+
+namespace android {
+namespace dvr {
+
+class VideoMeshSurfaceClient
+ : pdx::ClientBase<VideoMeshSurfaceClient, SurfaceClient> {
+ public:
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+
+ // This call assumes ownership of |handle|.
+ static std::unique_ptr<VideoMeshSurfaceClient> Import(
+ LocalChannelHandle handle);
+
+ std::shared_ptr<ProducerQueue> GetProducerQueue();
+
+ // Get the shared memory metadata buffer for this video mesh surface. If it is
+ // not yet allocated, this will allocate it.
+ volatile VideoMeshSurfaceMetadata* GetMetadataBufferPtr();
+
+ private:
+ friend BASE;
+
+ std::shared_ptr<android::dvr::ProducerQueue> producer_queue_;
+ VideoMeshSurfaceMetadata* mapped_metadata_buffer_;
+
+ explicit VideoMeshSurfaceClient(LocalChannelHandle handle);
+};
+
+} // namespace dvr
+} // namespace android
+
+struct DvrVideoMeshSurface {
+ std::shared_ptr<android::dvr::VideoMeshSurfaceClient> client;
+};
+
+#endif // ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
new file mode 100644
index 0000000..32fa40f
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
@@ -0,0 +1,70 @@
+#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
+#define ANDROID_DVR_VSYNC_CLIENT_H_
+
+#include <stdint.h>
+
+#include <pdx/client.h>
+#include <private/dvr/vsync_client_api.h>
+
+struct dvr_vsync_client {};
+
+namespace android {
+namespace dvr {
+
+/*
+ * VSyncClient is a remote interface to the vsync service in displayd.
+ * This class is used to wait for and retrieve information about the
+ * display vsync.
+ */
+class VSyncClient : public pdx::ClientBase<VSyncClient>,
+ public dvr_vsync_client {
+ public:
+ /*
+ * Wait for the next vsync signal.
+ * The timestamp (in ns) is written into *ts when ts is non-NULL.
+ */
+ int Wait(int64_t* timestamp_ns);
+
+ /*
+ * Returns the file descriptor used to communicate with the vsync system
+ * service or -1 on error.
+ */
+ int GetFd();
+
+ /*
+ * Clears the select/poll/epoll event so that subsequent calls to
+ * these will not signal until the next vsync.
+ */
+ int Acknowledge();
+
+ /*
+ * Get the timestamp of the last vsync event in ns. This call has
+ * the same side effect on events as Acknowledge(), which saves
+ * an IPC message.
+ */
+ int GetLastTimestamp(int64_t* timestamp_ns);
+
+ /*
+ * Get vsync scheduling info.
+ * Get the estimated timestamp of the next GPU lens warp preemption event in
+ * ns. Also returns the corresponding vsync count that the next lens warp
+ * operation will target. This call has the same side effect on events as
+ * Acknowledge(), which saves an IPC message.
+ */
+ int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns,
+ uint32_t* next_vsync_count);
+
+ private:
+ friend BASE;
+
+ VSyncClient();
+ explicit VSyncClient(long timeout_ms);
+
+ VSyncClient(const VSyncClient&) = delete;
+ void operator=(const VSyncClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VSYNC_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h b/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
new file mode 100644
index 0000000..4cdbc71
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
@@ -0,0 +1,44 @@
+#ifndef ANDROID_DVR_VSYNC_CLIENT_API_H_
+#define ANDROID_DVR_VSYNC_CLIENT_API_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// A client of the vsync service.
+//
+// The "dvr_vsync_client" structure wraps a client connection to the
+// system vsync service. It is used to synchronize application drawing
+// with the scanout of the display.
+typedef struct dvr_vsync_client dreamos_vsync_client;
+
+// Creates a new client to the system vsync service.
+dvr_vsync_client* dvr_vsync_client_create();
+
+// Destroys the vsync client.
+void dvr_vsync_client_destroy(dvr_vsync_client* client);
+
+// Blocks until the next vsync signal.
+// The timestamp (in ns) is written into |*timestamp_ns| when it is non-NULL.
+// Returns 0 upon success, or -errno.
+int dvr_vsync_client_wait(dvr_vsync_client* client, int64_t* timestamp_ns);
+
+// Returns the file descriptor used to communicate with the vsync service.
+int dvr_vsync_client_get_fd(dvr_vsync_client* client);
+
+// Clears the select/poll/epoll event so that subsequent calls to these
+// will not signal until the next vsync.
+int dvr_vsync_client_acknowledge(dvr_vsync_client* client);
+
+// Gets the timestamp of the last vsync signal in ns. This call has the
+// same side effects on events as acknowledge.
+int dvr_vsync_client_get_last_timestamp(dvr_vsync_client* client,
+ int64_t* timestamp_ns);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_VSYNC_CLIENT_API_H_
diff --git a/libs/vr/libdisplay/late_latch.cpp b/libs/vr/libdisplay/late_latch.cpp
new file mode 100644
index 0000000..3681e10
--- /dev/null
+++ b/libs/vr/libdisplay/late_latch.cpp
@@ -0,0 +1,461 @@
+#include "include/private/dvr/late_latch.h"
+
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <base/logging.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+#include <private/dvr/types.h>
+
+#define PRINT_MATRIX 0
+
+#if PRINT_MATRIX
+#ifndef LOG_TAG
+#define LOG_TAG "latelatch"
+#endif
+#include <cutils/log.h>
+
+#define PE(str, ...) \
+ fprintf(stderr, "[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__); \
+ ALOGI("[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define PV4(v) PE(#v "=%f,%f,%f,%f\n", v[0], v[1], v[2], v[3]);
+#define PM4(m) \
+ PE(#m ":\n %f,%f,%f,%f\n %f,%f,%f,%f\n %f,%f,%f,%f\n %f,%f,%f,%f\n", \
+ m(0, 0), m(0, 1), m(0, 2), m(0, 3), m(1, 0), m(1, 1), m(1, 2), m(1, 3), \
+ m(2, 0), m(2, 1), m(2, 2), m(2, 3), m(3, 0), m(3, 1), m(3, 2), m(3, 3))
+#endif // PRINT_MATRIX
+
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+
+// Compute shader bindings.
+// GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS must be at least 8 for GLES 3.1.
+#define POSE_BINDING 0
+#define RENDER_POSE_BINDING 1
+#define INPUT_BINDING 2
+#define OUTPUT_BINDING 3
+
+using android::pdx::LocalHandle;
+
+namespace {
+
+static const std::string kShaderLateLatch = R"( // NOLINT
+ struct Pose {
+ vec4 quat;
+ vec3 pos;
+ };
+
+ // Must match DvrPoseAsync C struct.
+ struct DvrPoseAsync {
+ vec4 orientation;
+ vec4 translation;
+ vec4 right_orientation;
+ vec4 right_translation;
+ vec4 angular_velocity;
+ vec4 velocity;
+ vec4 reserved[2];
+ };
+
+ // Must match LateLatchInputData C struct.
+ layout(binding = INPUT_BINDING, std140)
+ buffer InputData {
+ mat4 uEyeFromHeadMat[kSurfaceViewMaxCount];
+ mat4 uProjMat[kSurfaceViewMaxCount];
+ mat4 uPoseOffset[kSurfaceViewMaxCount];
+ mat4 uEdsMat1[kSurfaceViewMaxCount];
+ mat4 uEdsMat2[kSurfaceViewMaxCount];
+ uint uPoseIndex;
+ uint uRenderPoseIndex;
+ } bIn;
+
+ // std140 is to layout the structure in a consistent, standard way so we
+ // can access it from C++.
+ // This structure exactly matches the pose ring buffer in pose_client.h.
+ layout(binding = POSE_BINDING, std140)
+ buffer PoseBuffer {
+ DvrPoseAsync data[kPoseAsyncBufferTotalCount];
+ } bPose;
+
+ // Must stay in sync with DisplaySurfaceMetadata C struct.
+ // GPU thread 0 will exclusively read in a pose and capture it
+ // into this array.
+ layout(binding = RENDER_POSE_BINDING, std140)
+ buffer DisplaySurfaceMetadata {
+ vec4 orientation[kSurfaceBufferMaxCount];
+ vec4 translation[kSurfaceBufferMaxCount];
+ } bSurfaceData;
+
+ // Must stay in sync with DisplaySurfaceMetadata C struct.
+ // Each thread writes to a vertic
+ layout(binding = OUTPUT_BINDING, std140)
+ buffer Output {
+ mat4 viewProjMatrix[kSurfaceViewMaxCount];
+ mat4 viewMatrix[kSurfaceViewMaxCount];
+ vec4 quaternion;
+ vec4 translation;
+ } bOut;
+
+ // Thread 0 will also store the single quat/pos pair in shared variables
+ // for the other threads to use (left and right eye in this array).
+ shared Pose sharedPose[2];
+
+ // Rotate v1 by the given quaternion. This is based on mathfu's
+ // Quaternion::Rotate function. It is the typical implementation of this
+ // operation. Eigen has a similar method (Quaternion::_transformVector) that
+ // supposedly requires fewer operations, but I am skeptical of optimizing
+ // shader code without proper profiling first.
+ vec3 rotate(vec4 quat, vec3 v1) {
+ float ss = 2.0 * quat.w;
+ vec3 v = quat.xyz;
+ return ss * cross(v, v1) + (ss * quat.w - 1.0) * v1 +
+ 2.0 * dot(v, v1) * v;
+ }
+
+ // See Eigen Quaternion::conjugate;
+ // Note that this isn't a true multiplicative inverse unless you can guarantee
+ // quat is also normalized, but that typically isn't an issue for our
+ // purposes.
+ vec4 quatInvert(vec4 quat) {
+ return vec4(-quat.xyz, quat.w);
+ }
+
+ // This is based on mathfu's Quaternion::operator*(Quaternion)
+ // Eigen's version is mathematically equivalent, just notationally different.
+ vec4 quatMul(vec4 q1, vec4 q2) {
+ return vec4(q1.w * q2.xyz + q2.w * q1.xyz + cross(q1.xyz, q2.xyz),
+ q1.w * q2.w - dot(q1.xyz, q2.xyz));
+ }
+
+ // Equivalent to pose.h GetObjectFromReferenceMatrix.
+ mat4 getInverseMatrix(Pose pose) {
+ // Invert quaternion and store fields the way Eigen does so we can
+ // keep in sync with Eigen methods easier.
+ vec4 quatInv = quatInvert(pose.quat);
+ vec3 v = quatInv.xyz;
+ float s = quatInv.w;
+ // Convert quaternion to matrix. See Eigen Quaternion::toRotationMatrix()
+ float x2 = v.x * v.x, y2 = v.y * v.y, z2 = v.z * v.z;
+ float sx = s * v.x, sy = s * v.y, sz = s * v.z;
+ float xz = v.x * v.z, yz = v.y * v.z, xy = v.x * v.y;
+ // Inverse translation.
+ vec3 point = -pose.pos;
+
+ return
+ mat4(1.0 - 2.0 * (y2 + z2), 2.0 * (xy + sz), 2.0 * (xz - sy), 0.0,
+ 2.0 * (xy - sz), 1.0 - 2.0 * (x2 + z2), 2.0 * (sx + yz), 0.0,
+ 2.0 * (sy + xz), 2.0 * (yz - sx), 1.0 - 2.0 * (x2 + y2), 0.0,
+ 0.0, 0.0, 0.0, 1.0)*
+ mat4(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
+ point.x, point.y, point.z, 1.0);
+ }
+
+ void appLateLatch() {
+ uint poseIndex = (gl_LocalInvocationIndex & uint(1));
+ mat4 head_from_center = getInverseMatrix(sharedPose[poseIndex]);
+ bOut.viewMatrix[gl_LocalInvocationIndex] =
+ bIn.uEyeFromHeadMat[gl_LocalInvocationIndex] *
+ head_from_center * bIn.uPoseOffset[gl_LocalInvocationIndex];
+ bOut.viewProjMatrix[gl_LocalInvocationIndex] =
+ bIn.uProjMat[gl_LocalInvocationIndex] *
+ bOut.viewMatrix[gl_LocalInvocationIndex];
+ }
+
+ // Extract the app frame's pose.
+ Pose getPoseFromApp() {
+ Pose p;
+ p.quat = bSurfaceData.orientation[bIn.uRenderPoseIndex];
+ p.pos = bSurfaceData.translation[bIn.uRenderPoseIndex].xyz;
+ return p;
+ }
+
+ // See Posef::GetPoseOffset.
+ Pose getPoseOffset(Pose p1, Pose p2) {
+ Pose p;
+ p.quat = quatMul(quatInvert(p2.quat), p1.quat);
+ // TODO(jbates) Consider enabling positional EDS when it is better
+ // tested.
+ // p.pos = p2.pos - p1.pos;
+ p.pos = vec3(0.0);
+ return p;
+ }
+
+ void edsLateLatch() {
+ Pose pose1 = getPoseFromApp();
+ Pose correction;
+ // Ignore the texture pose if the quat is not unit-length.
+ float tex_quat_length = length(pose1.quat);
+ uint poseIndex = (gl_LocalInvocationIndex & uint(1));
+ if (abs(tex_quat_length - 1.0) < 0.001)
+ correction = getPoseOffset(pose1, sharedPose[poseIndex]);
+ else
+ correction = Pose(vec4(0, 0, 0, 1), vec3(0, 0, 0));
+ mat4 eye_old_from_eye_new_matrix = getInverseMatrix(correction);
+ bOut.viewProjMatrix[gl_LocalInvocationIndex] =
+ bIn.uEdsMat1[gl_LocalInvocationIndex] *
+ eye_old_from_eye_new_matrix * bIn.uEdsMat2[gl_LocalInvocationIndex];
+ // Currently unused, except for debugging:
+ bOut.viewMatrix[gl_LocalInvocationIndex] = eye_old_from_eye_new_matrix;
+ }
+
+ // One thread per surface view.
+ layout (local_size_x = kSurfaceViewMaxCount, local_size_y = 1,
+ local_size_z = 1) in;
+
+ void main() {
+ // First, thread 0 late latches pose and stores it into various places.
+ if (gl_LocalInvocationIndex == uint(0)) {
+ sharedPose[0].quat = bPose.data[bIn.uPoseIndex].orientation;
+ sharedPose[0].pos = bPose.data[bIn.uPoseIndex].translation.xyz;
+ sharedPose[1].quat = bPose.data[bIn.uPoseIndex].right_orientation;
+ sharedPose[1].pos = bPose.data[bIn.uPoseIndex].right_translation.xyz;
+ if (IS_APP_LATE_LATCH) {
+ bSurfaceData.orientation[bIn.uRenderPoseIndex] = sharedPose[0].quat;
+ bSurfaceData.translation[bIn.uRenderPoseIndex] = vec4(sharedPose[0].pos, 0.0);
+ // TODO(jbates) implement app late-latch support for separate eye poses.
+ // App late latch currently uses the same pose for both eye views.
+ sharedPose[1] = sharedPose[0];
+ }
+ bOut.quaternion = sharedPose[0].quat;
+ bOut.translation = vec4(sharedPose[0].pos, 0.0);
+ }
+
+ // Memory barrier to make sure all threads can see prior writes.
+ memoryBarrierShared();
+
+ // Execution barrier to block all threads here until all threads have
+ // reached this point -- ensures the late latching is done.
+ barrier();
+
+ if (IS_APP_LATE_LATCH)
+ appLateLatch();
+ else
+ edsLateLatch();
+ }
+)";
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+LateLatch::LateLatch(bool is_app_late_latch)
+ : LateLatch(is_app_late_latch, LocalHandle()) {}
+
+LateLatch::LateLatch(bool is_app_late_latch,
+ LocalHandle&& surface_metadata_fd)
+ : is_app_late_latch_(is_app_late_latch),
+ app_late_latch_output_(NULL),
+ eds_late_latch_output_(NULL) {
+ CHECK_GL();
+ glGenBuffers(1, &input_buffer_id_);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+ glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LateLatchInput), nullptr,
+ GL_DYNAMIC_DRAW);
+ glGenBuffers(1, &output_buffer_id_);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer_id_);
+ glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LateLatchOutput), nullptr,
+ GL_DYNAMIC_COPY);
+ CHECK_GL();
+
+ LocalHandle pose_buffer_fd;
+ pose_client_ = dvrPoseCreate();
+ if (!pose_client_) {
+ LOG(ERROR) << "LateLatch Error: failed to create pose client";
+ } else {
+ int ret = privateDvrPoseGetRingBufferFd(pose_client_, &pose_buffer_fd);
+ if (ret < 0) {
+ LOG(ERROR) << "LateLatch Error: failed to get pose ring buffer";
+ }
+ }
+
+ glGenBuffers(1, &pose_buffer_object_);
+ glGenBuffers(1, &metadata_buffer_id_);
+ if (!glBindSharedBufferQCOM) {
+ LOG(ERROR) << "Error: Missing gralloc buffer extension, no pose data";
+ } else {
+ if (pose_buffer_fd) {
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, pose_buffer_object_);
+ glBindSharedBufferQCOM(GL_SHADER_STORAGE_BUFFER,
+ kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync),
+ pose_buffer_fd.Release());
+ }
+ CHECK_GL();
+ }
+
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, metadata_buffer_id_);
+ if (surface_metadata_fd && glBindSharedBufferQCOM) {
+ glBindSharedBufferQCOM(GL_SHADER_STORAGE_BUFFER,
+ sizeof(DisplaySurfaceMetadata),
+ surface_metadata_fd.Release());
+ } else {
+ // Fall back on internal metadata buffer when none provided, for example
+ // when distortion is done in the application process.
+ glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(DisplaySurfaceMetadata),
+ nullptr, GL_DYNAMIC_COPY);
+ }
+ CHECK_GL();
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+
+ CHECK_GL();
+ LoadLateLatchShader();
+}
+
+LateLatch::~LateLatch() {
+ glDeleteBuffers(1, &metadata_buffer_id_);
+ glDeleteBuffers(1, &input_buffer_id_);
+ glDeleteBuffers(1, &output_buffer_id_);
+ glDeleteBuffers(1, &pose_buffer_object_);
+ dvrPoseDestroy(pose_client_);
+}
+
+void LateLatch::LoadLateLatchShader() {
+ std::string str;
+ str += "\n#define POSE_BINDING " STRINGIFY(POSE_BINDING);
+ str += "\n#define RENDER_POSE_BINDING " STRINGIFY(RENDER_POSE_BINDING);
+ str += "\n#define INPUT_BINDING " STRINGIFY(INPUT_BINDING);
+ str += "\n#define OUTPUT_BINDING " STRINGIFY(OUTPUT_BINDING);
+ str += "\n#define kPoseAsyncBufferTotalCount " STRINGIFY(
+ kPoseAsyncBufferTotalCount);
+ str += "\n#define kSurfaceBufferMaxCount " STRINGIFY(kSurfaceBufferMaxCount);
+ str += "\n#define kSurfaceBufferMaxCount " STRINGIFY(kSurfaceBufferMaxCount);
+ str += "\n#define kSurfaceViewMaxCount " STRINGIFY(kSurfaceViewMaxCount);
+ str += "\n#define IS_APP_LATE_LATCH ";
+ str += is_app_late_latch_ ? "true" : "false";
+ str += "\n";
+ str += kShaderLateLatch;
+ late_latch_program_.Link(str);
+ CHECK_GL();
+}
+
+void LateLatch::CaptureOutputData(LateLatchOutput* data) const {
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer_id_);
+ LateLatchOutput* out_data = static_cast<LateLatchOutput*>(glMapBufferRange(
+ GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchOutput), GL_MAP_READ_BIT));
+ *data = *out_data;
+ glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+ CHECK_GL();
+}
+
+void LateLatch::AddLateLatch(const LateLatchInput& data) const {
+ CHECK(is_app_late_latch_);
+ CHECK_GL();
+ late_latch_program_.Use();
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING,
+ metadata_buffer_id_);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, pose_buffer_object_);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, output_buffer_id_);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+ LateLatchInput* adata = (LateLatchInput*)glMapBufferRange(
+ GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchInput),
+ GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+ if (adata)
+ *adata = data;
+ else
+ LOG(ERROR) << "Error: LateLatchInput gl mapping is null";
+ glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+ CHECK_GL();
+
+ // The output buffer is going to be written but it may be read by
+ // earlier shaders, so we need a shader storage memory barrier.
+ glMemoryBarrier(GL_SHADER_STORAGE_BUFFER);
+
+ glDispatchCompute(1, 1, 1);
+ CHECK_GL();
+
+ // The transform feedback buffer is going to be read as a uniform by the app,
+ // so we need a uniform memory barrier.
+ glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+
+ if (app_late_latch_output_) {
+ // Capture the output data:
+ CaptureOutputData(app_late_latch_output_);
+ }
+#if PRINT_MATRIX
+ // Print the composed matrix to stderr:
+ LateLatchOutput out_data;
+ CaptureOutputData(&out_data);
+ CHECK_GL();
+ PE("LL APP slot:%d\n", data.render_pose_index);
+ PM4(data.proj_mat[0]);
+ PM4(out_data.view_proj_matrix[0]);
+ PM4(out_data.view_proj_matrix[1]);
+ PM4(out_data.view_proj_matrix[2]);
+ PM4(out_data.view_proj_matrix[3]);
+ PM4(out_data.view_matrix[0]);
+ PM4(out_data.view_matrix[1]);
+ PM4(out_data.view_matrix[2]);
+ PM4(out_data.view_matrix[3]);
+ PV4(out_data.pose_quaternion);
+ PV4(out_data.pose_translation);
+#endif
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, 0);
+ glUseProgram(0);
+}
+
+void LateLatch::AddEdsLateLatch(const LateLatchInput& data,
+ GLuint render_pose_buffer_object) const {
+ CHECK(!is_app_late_latch_);
+ late_latch_program_.Use();
+
+ // Fall back on internal buffer when none is provided.
+ if (!render_pose_buffer_object)
+ render_pose_buffer_object = metadata_buffer_id_;
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING,
+ render_pose_buffer_object);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, pose_buffer_object_);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, output_buffer_id_);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+ LateLatchInput* adata = (LateLatchInput*)glMapBufferRange(
+ GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchInput),
+ GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+ *adata = data;
+ glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
+ CHECK_GL();
+
+ glDispatchCompute(1, 1, 1);
+ CHECK_GL();
+
+ if (eds_late_latch_output_) {
+ // Capture the output data:
+ CaptureOutputData(eds_late_latch_output_);
+ }
+#if PRINT_MATRIX
+ // Print the composed matrix to stderr:
+ LateLatchOutput out_data;
+ CaptureOutputData(&out_data);
+ CHECK_GL();
+ PE("LL EDS\n");
+ PM4(out_data.view_proj_matrix[0]);
+ PM4(out_data.view_matrix[0]);
+ PV4(out_data.pose_quaternion);
+ PV4(out_data.pose_translation);
+#endif
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, 0);
+ glUseProgram(0);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/native_buffer_queue.cpp b/libs/vr/libdisplay/native_buffer_queue.cpp
new file mode 100644
index 0000000..2d1e23d
--- /dev/null
+++ b/libs/vr/libdisplay/native_buffer_queue.cpp
@@ -0,0 +1,152 @@
+#include "include/private/dvr/native_buffer_queue.h"
+
+#include <base/logging.h>
+#include <cutils/log.h>
+#include <sys/epoll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <array>
+
+#include <private/dvr/display_types.h>
+
+namespace android {
+namespace dvr {
+
+NativeBufferQueue::NativeBufferQueue(
+ const std::shared_ptr<DisplaySurfaceClient>& surface, size_t capacity)
+ : NativeBufferQueue(nullptr, surface, capacity) {}
+
+NativeBufferQueue::NativeBufferQueue(
+ EGLDisplay display, const std::shared_ptr<DisplaySurfaceClient>& surface,
+ size_t capacity)
+ : surface_(surface),
+ buffers_(capacity),
+ buffer_queue_(capacity) {
+ CHECK(surface);
+
+ epoll_fd_ = epoll_create(64);
+ if (epoll_fd_ < 0) {
+ ALOGE("NativeBufferQueue::NativeBufferQueue: Failed to create epoll fd: %s",
+ strerror(errno));
+ return;
+ }
+
+ // The kSurfaceBufferMaxCount must be >= the capacity so that shader code
+ // can bind surface buffer array data.
+ CHECK(kSurfaceBufferMaxCount >= capacity);
+
+ for (size_t i = 0; i < capacity; i++) {
+ uint32_t buffer_index = 0;
+ auto buffer = surface_->AllocateBuffer(&buffer_index);
+ if (!buffer) {
+ ALOGE("NativeBufferQueue::NativeBufferQueue: Failed to allocate buffer!");
+ return;
+ }
+
+ // TODO(jbates): store an index associated with each buffer so that we can
+ // determine which index in DisplaySurfaceMetadata it is associated
+ // with.
+ buffers_.push_back(new NativeBufferProducer(buffer, display, buffer_index));
+ NativeBufferProducer* native_buffer = buffers_.back().get();
+
+ epoll_event event = {.events = EPOLLIN | EPOLLET,
+ .data = {.ptr = native_buffer}};
+ if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, buffer->event_fd(), &event) <
+ 0) {
+ ALOGE(
+ "NativeBufferQueue::NativeBufferQueue: Failed to add buffer producer "
+ "to epoll set: %s",
+ strerror(errno));
+ return;
+ }
+
+ Enqueue(native_buffer);
+ }
+}
+
+NativeBufferQueue::~NativeBufferQueue() {
+ if (epoll_fd_ >= 0)
+ close(epoll_fd_);
+}
+
+bool NativeBufferQueue::WaitForBuffers() {
+ ATRACE_NAME("NativeBufferQueue::WaitForBuffers");
+ // Intentionally set this to one so that we don't waste time retrieving too
+ // many buffers.
+ constexpr size_t kMaxEvents = 1;
+ std::array<epoll_event, kMaxEvents> events;
+
+ while (buffer_queue_.IsEmpty()) {
+ int num_events = epoll_wait(epoll_fd_, events.data(), events.size(), -1);
+ if (num_events < 0 && errno != EINTR) {
+ ALOGE("NativeBufferQueue:WaitForBuffers: Failed to wait for buffers: %s",
+ strerror(errno));
+ return false;
+ }
+
+ ALOGD_IF(TRACE, "NativeBufferQueue::WaitForBuffers: num_events=%d",
+ num_events);
+
+ for (int i = 0; i < num_events; i++) {
+ NativeBufferProducer* buffer =
+ static_cast<NativeBufferProducer*>(events[i].data.ptr);
+ ALOGD_IF(TRACE,
+ "NativeBufferQueue::WaitForBuffers: event %d: buffer_id=%d "
+ "events=0x%x",
+ i, buffer->buffer()->id(), events[i].events);
+
+ if (events[i].events & EPOLLIN) {
+ const int ret = buffer->GainAsync();
+ if (ret < 0) {
+ ALOGE("NativeBufferQueue::WaitForBuffers: Failed to gain buffer: %s",
+ strerror(-ret));
+ continue;
+ }
+
+ Enqueue(buffer);
+ }
+ }
+ }
+
+ return true;
+}
+
+void NativeBufferQueue::Enqueue(NativeBufferProducer* buf) {
+ ATRACE_NAME("NativeBufferQueue::Enqueue");
+ if (buffer_queue_.IsFull()) {
+ ALOGE("NativeBufferQueue::Enqueue: Queue is full!");
+ return;
+ }
+
+ buffer_queue_.Append(buf);
+}
+
+NativeBufferProducer* NativeBufferQueue::Dequeue() {
+ ATRACE_NAME("NativeBufferQueue::Dequeue");
+ ALOGD_IF(TRACE, "NativeBufferQueue::Dequeue: count=%zd",
+ buffer_queue_.GetSize());
+
+ if (buffer_queue_.IsEmpty() && !WaitForBuffers())
+ return nullptr;
+
+ NativeBufferProducer* buf = buffer_queue_.Front();
+ buffer_queue_.PopFront();
+ if (buf == nullptr) {
+ ALOGE("NativeBufferQueue::Dequeue: Buffer at tail was nullptr!!!");
+ return nullptr;
+ }
+
+ return buf;
+}
+
+size_t NativeBufferQueue::GetFreeBufferCount() const {
+ return buffer_queue_.GetSize();
+}
+
+size_t NativeBufferQueue::GetQueueCapacity() const {
+ return buffer_queue_.GetCapacity();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/native_window.cpp b/libs/vr/libdisplay/native_window.cpp
new file mode 100644
index 0000000..63c81ed
--- /dev/null
+++ b/libs/vr/libdisplay/native_window.cpp
@@ -0,0 +1,459 @@
+#include <EGL/egl.h>
+
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include <system/window.h>
+#include <time.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/Errors.h>
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <cutils/log.h>
+
+#include <memory>
+#include <mutex>
+
+#include <dvr/graphics.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_client.h>
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/native_buffer_queue.h>
+
+namespace {
+
+constexpr int kDefaultDisplaySurfaceUsage =
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+constexpr int kWarpedDisplaySurfaceFlags = 0;
+constexpr int kUnwarpedDisplaySurfaceFlags =
+ DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS |
+ DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION |
+ DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC;
+constexpr int kDefaultBufferCount = 4;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// NativeWindow is an implementation of ANativeWindow. This class interacts with
+// displayd through the DisplaySurfaceClient and NativeBufferQueue.
+class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow,
+ LightRefBase<NativeWindow> > {
+ public:
+ explicit NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface);
+
+ void SetVisible(bool visible);
+ void SetZOrder(int z_order);
+ void PostEarly();
+
+ private:
+ friend class LightRefBase<NativeWindow>;
+
+ void Post(sp<NativeBufferProducer> buffer, int fence_fd);
+
+ static int SetSwapInterval(ANativeWindow* window, int interval);
+ static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fence_fd);
+ static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fence_fd);
+ static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fence_fd);
+ static int Query(const ANativeWindow* window, int what, int* value);
+ static int Perform(ANativeWindow* window, int operation, ...);
+
+ static int DequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer);
+ static int CancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer);
+ static int QueueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer);
+ static int LockBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer);
+
+ std::shared_ptr<DisplaySurfaceClient> surface_;
+
+ std::mutex lock_;
+ NativeBufferQueue buffer_queue_;
+ sp<NativeBufferProducer> next_post_buffer_;
+ bool next_buffer_already_posted_;
+
+ NativeWindow(const NativeWindow&) = delete;
+ void operator=(NativeWindow&) = delete;
+};
+
+NativeWindow::NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface)
+ : surface_(surface),
+ buffer_queue_(surface, kDefaultBufferCount),
+ next_post_buffer_(nullptr),
+ next_buffer_already_posted_(false) {
+ ANativeWindow::setSwapInterval = SetSwapInterval;
+ ANativeWindow::dequeueBuffer = DequeueBuffer;
+ ANativeWindow::cancelBuffer = CancelBuffer;
+ ANativeWindow::queueBuffer = QueueBuffer;
+ ANativeWindow::query = Query;
+ ANativeWindow::perform = Perform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED;
+ ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED;
+ ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED;
+ ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED;
+}
+
+void NativeWindow::SetVisible(bool visible) { surface_->SetVisible(visible); }
+
+void NativeWindow::SetZOrder(int z_order) { surface_->SetZOrder(z_order); }
+
+void NativeWindow::PostEarly() {
+ ATRACE_NAME("NativeWindow::PostEarly");
+ ALOGI_IF(TRACE, "NativeWindow::PostEarly");
+
+ std::lock_guard<std::mutex> autolock(lock_);
+
+ if (!next_buffer_already_posted_) {
+ next_buffer_already_posted_ = true;
+
+ if (!next_post_buffer_.get()) {
+ next_post_buffer_ = buffer_queue_.Dequeue();
+ }
+ ATRACE_ASYNC_BEGIN("BufferPost", next_post_buffer_->buffer()->id());
+ Post(next_post_buffer_, -1);
+ }
+}
+
+void NativeWindow::Post(sp<NativeBufferProducer> buffer, int fence_fd) {
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+ ALOGI_IF(TRACE, "NativeWindow::Post: buffer_id=%d, fence_fd=%d",
+ buffer->buffer()->id(), fence_fd);
+ ALOGW_IF(!surface_->visible(),
+ "NativeWindow::Post: Posting buffer on invisible surface!!!");
+ buffer->Post(fence_fd, 0);
+}
+
+int NativeWindow::SetSwapInterval(ANativeWindow* window, int interval) {
+ ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
+ return 0;
+}
+
+int NativeWindow::DequeueBuffer(ANativeWindow* window,
+ ANativeWindowBuffer** buffer, int* fence_fd) {
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+
+ NativeWindow* self = getSelf(window);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ if (!self->next_post_buffer_.get()) {
+ self->next_post_buffer_ = self->buffer_queue_.Dequeue();
+ }
+ ATRACE_ASYNC_BEGIN("BufferDraw", self->next_post_buffer_->buffer()->id());
+ *fence_fd = self->next_post_buffer_->ClaimReleaseFence().Release();
+ *buffer = self->next_post_buffer_.get();
+
+ ALOGI_IF(TRACE, "NativeWindow::DequeueBuffer: fence_fd=%d", *fence_fd);
+ return 0;
+}
+
+int NativeWindow::QueueBuffer(ANativeWindow* window,
+ ANativeWindowBuffer* buffer, int fence_fd) {
+ ATRACE_NAME("NativeWindow::QueueBuffer");
+ ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
+
+ NativeWindow* self = getSelf(window);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ NativeBufferProducer* native_buffer =
+ static_cast<NativeBufferProducer*>(buffer);
+ ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+ bool do_post = true;
+ if (self->next_buffer_already_posted_) {
+ // Check that the buffer is the one we expect, but handle it if this happens
+ // in production by allowing this buffer to post on top of the previous one.
+ DCHECK(native_buffer == self->next_post_buffer_.get());
+ if (native_buffer == self->next_post_buffer_.get()) {
+ do_post = false;
+ if (fence_fd >= 0)
+ close(fence_fd);
+ }
+ }
+ if (do_post) {
+ ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id());
+ self->Post(native_buffer, fence_fd);
+ }
+ self->next_buffer_already_posted_ = false;
+ self->next_post_buffer_ = nullptr;
+
+ return NO_ERROR;
+}
+
+int NativeWindow::CancelBuffer(ANativeWindow* window,
+ ANativeWindowBuffer* buffer, int fence_fd) {
+ ATRACE_NAME("NativeWindow::CancelBuffer");
+ ALOGI_IF(TRACE, "NativeWindow::CancelBuffer: fence_fd: %d", fence_fd);
+
+ NativeWindow* self = getSelf(window);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ NativeBufferProducer* native_buffer =
+ static_cast<NativeBufferProducer*>(buffer);
+ ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+ ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
+ bool do_enqueue = true;
+ if (self->next_buffer_already_posted_) {
+ // Check that the buffer is the one we expect, but handle it if this happens
+ // in production by returning this buffer to the buffer queue.
+ DCHECK(native_buffer == self->next_post_buffer_.get());
+ if (native_buffer == self->next_post_buffer_.get()) {
+ do_enqueue = false;
+ }
+ }
+ if (do_enqueue) {
+ self->buffer_queue_.Enqueue(native_buffer);
+ }
+ if (fence_fd >= 0)
+ close(fence_fd);
+ self->next_buffer_already_posted_ = false;
+ self->next_post_buffer_ = nullptr;
+
+ return NO_ERROR;
+}
+
+int NativeWindow::Query(const ANativeWindow* window, int what, int* value) {
+ NativeWindow* self = getSelf(const_cast<ANativeWindow*>(window));
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ *value = self->surface_->width();
+ return NO_ERROR;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = self->surface_->height();
+ return NO_ERROR;
+ case NATIVE_WINDOW_FORMAT:
+ *value = self->surface_->format();
+ return NO_ERROR;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ *value = 1;
+ return NO_ERROR;
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ *value = NATIVE_WINDOW_SURFACE;
+ return NO_ERROR;
+ case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+ *value = 1;
+ return NO_ERROR;
+ case NATIVE_WINDOW_DEFAULT_WIDTH:
+ *value = self->surface_->width();
+ return NO_ERROR;
+ case NATIVE_WINDOW_DEFAULT_HEIGHT:
+ *value = self->surface_->height();
+ return NO_ERROR;
+ case NATIVE_WINDOW_TRANSFORM_HINT:
+ *value = 0;
+ return NO_ERROR;
+ }
+
+ *value = 0;
+ return BAD_VALUE;
+}
+
+int NativeWindow::Perform(ANativeWindow* window, int operation, ...) {
+ NativeWindow* self = getSelf(window);
+ std::lock_guard<std::mutex> autolock(self->lock_);
+
+ va_list args;
+ va_start(args, operation);
+
+ // TODO(eieio): The following operations are not used at this time. They are
+ // included here to help document which operations may be useful and what
+ // parameters they take.
+ switch (operation) {
+ case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h);
+ return NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+ int format = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format);
+ return NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: {
+ int transform = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d",
+ transform);
+ return NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_SET_USAGE: {
+ int usage = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
+ return NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_CONNECT:
+ case NATIVE_WINDOW_DISCONNECT:
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ case NATIVE_WINDOW_API_CONNECT:
+ case NATIVE_WINDOW_API_DISCONNECT:
+ // TODO(eieio): we should implement these
+ return NO_ERROR;
+
+ case NATIVE_WINDOW_SET_BUFFER_COUNT: {
+ int buffer_count = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d",
+ buffer_count);
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+ android_dataspace_t data_space =
+ static_cast<android_dataspace_t>(va_arg(args, int));
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d",
+ data_space);
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_SET_SCALING_MODE: {
+ int mode = va_arg(args, int);
+ ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode);
+ return NO_ERROR;
+ }
+
+ case NATIVE_WINDOW_LOCK:
+ case NATIVE_WINDOW_UNLOCK_AND_POST:
+ case NATIVE_WINDOW_SET_CROP:
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ return INVALID_OPERATION;
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+int NativeWindow::DequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer) {
+ int fence_fd = -1;
+ int ret = DequeueBuffer(window, buffer, &fence_fd);
+
+ // wait for fence
+ if (ret == NO_ERROR && fence_fd != -1)
+ close(fence_fd);
+
+ return ret;
+}
+
+int NativeWindow::CancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return CancelBuffer(window, buffer, -1);
+}
+
+int NativeWindow::QueueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return QueueBuffer(window, buffer, -1);
+}
+
+int NativeWindow::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
+ ANativeWindowBuffer* /*buffer*/) {
+ return NO_ERROR;
+}
+
+} // namespace dvr
+} // namespace android
+
+static EGLNativeWindowType CreateDisplaySurface(int* display_width,
+ int* display_height, int format,
+ int usage, int flags) {
+ auto client = android::dvr::DisplayClient::Create();
+ if (!client) {
+ ALOGE("Failed to create display client!");
+ return nullptr;
+ }
+
+ // TODO(eieio,jbates): Consider passing flags and other parameters to get
+ // metrics based on specific surface requirements.
+ android::dvr::SystemDisplayMetrics metrics;
+ const int ret = client->GetDisplayMetrics(&metrics);
+ if (ret < 0) {
+ ALOGE("Failed to get display metrics: %s", strerror(-ret));
+ return nullptr;
+ }
+
+ int width, height;
+
+ if (flags & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) {
+ width = metrics.display_native_width;
+ height = metrics.display_native_height;
+ } else {
+ width = metrics.distorted_width;
+ height = metrics.distorted_height;
+ }
+
+ std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
+ client->CreateDisplaySurface(width, height, format, usage, flags);
+
+ if (display_width)
+ *display_width = metrics.display_native_width;
+ if (display_height)
+ *display_height = metrics.display_native_height;
+
+ // Set the surface visible by default.
+ // TODO(eieio,jbates): Remove this from here and set visible somewhere closer
+ // to the application to account for situations where the application wants to
+ // create surfaces that will be used later or shouldn't be visible yet.
+ surface->SetVisible(true);
+
+ return new android::dvr::NativeWindow(surface);
+}
+
+std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient(
+ struct DvrSurfaceParameter* parameters,
+ /*out*/ android::dvr::SystemDisplayMetrics* metrics);
+
+extern "C" EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
+ struct DvrSurfaceParameter* parameters) {
+ android::dvr::SystemDisplayMetrics metrics;
+ auto surface = CreateDisplaySurfaceClient(parameters, &metrics);
+ if (!surface) {
+ ALOGE("Failed to create display surface client");
+ return nullptr;
+ }
+ return new android::dvr::NativeWindow(surface);
+}
+
+extern "C" EGLNativeWindowType dvrCreateDisplaySurface() {
+ return CreateDisplaySurface(NULL, NULL, kDefaultDisplaySurfaceFormat,
+ kDefaultDisplaySurfaceUsage,
+ kUnwarpedDisplaySurfaceFlags);
+}
+
+extern "C" EGLNativeWindowType dvrCreateWarpedDisplaySurface(
+ int* display_width, int* display_height) {
+ return CreateDisplaySurface(
+ display_width, display_height, kDefaultDisplaySurfaceFormat,
+ kDefaultDisplaySurfaceUsage, kWarpedDisplaySurfaceFlags);
+}
+
+extern "C" void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window,
+ int visible) {
+ auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
+ native_window->SetVisible(visible);
+}
+
+extern "C" void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window,
+ int z_order) {
+ auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
+ native_window->SetZOrder(z_order);
+}
+
+extern "C" void dvrDisplayPostEarly(EGLNativeWindowType window) {
+ auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
+ native_window->PostEarly();
+}
diff --git a/libs/vr/libdisplay/native_window_test.cpp b/libs/vr/libdisplay/native_window_test.cpp
new file mode 100644
index 0000000..2f3bc33
--- /dev/null
+++ b/libs/vr/libdisplay/native_window_test.cpp
@@ -0,0 +1,43 @@
+#include <iostream>
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <dvr/graphics.h>
+#include <private/dvr/display_client.h>
+
+#include <cpp_free_mock/cpp_free_mock.h>
+
+// Checks querying the VSync of the device on display surface creation.
+TEST(CreateDisplaySurface, QueryVSyncPeriod) {
+ using ::testing::_;
+
+ const uint64_t kExpectedVSync = 123456;
+
+ // We only care about the expected VSync value
+ android::dvr::DisplayMetrics metrics;
+ metrics.vsync_period_ns = kExpectedVSync;
+
+ uint64_t outPeriod;
+
+ DvrSurfaceParameter display_params[] = {
+ DVR_SURFACE_PARAMETER_IN(WIDTH, 256),
+ DVR_SURFACE_PARAMETER_IN(HEIGHT, 256),
+ DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &outPeriod),
+ DVR_SURFACE_PARAMETER_LIST_END,
+ };
+
+ // inject the mocking code to the target method
+ auto mocked_function =
+ MOCKER(&android::dvr::DisplayClient::GetDisplayMetrics);
+
+ // instrument the mock function to return our custom metrics
+ EXPECT_CALL(*mocked_function, MOCK_FUNCTION(_, _))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(metrics),
+ ::testing::Return(0)));
+
+ ASSERT_NE(nullptr, dvrCreateDisplaySurfaceExtended(display_params));
+
+ EXPECT_EQ(kExpectedVSync, outPeriod);
+}
diff --git a/libs/vr/libdisplay/screenshot_client.cpp b/libs/vr/libdisplay/screenshot_client.cpp
new file mode 100644
index 0000000..78f5e0f
--- /dev/null
+++ b/libs/vr/libdisplay/screenshot_client.cpp
@@ -0,0 +1,66 @@
+#include "include/private/dvr/screenshot_client.h"
+
+#include <cutils/log.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::Transaction;
+using android::pdx::rpc::ClientPayload;
+using android::pdx::rpc::MessageBuffer;
+using android::pdx::rpc::ReplyBuffer;
+
+namespace android {
+namespace dvr {
+
+namespace {
+// Maximum supported pixels for screenshot capture. If the actual target buffer
+// is more than this, an error will be reported.
+constexpr int kMaxScreenshotPixels = 6000 * 4000;
+} // namespace
+
+int ScreenshotClient::Take(std::vector<uint8_t>* out_image, int index,
+ int* return_width, int* return_height) {
+ if (format_ != HAL_PIXEL_FORMAT_RGB_888) {
+ ALOGE("ScreenshotClient::Take: Unsupported layout format: format=%d",
+ format_);
+ return -ENOSYS;
+ }
+
+ // TODO(eieio): Make a cleaner way to ensure enough capacity for send or
+ // receive buffers. This method assumes TLS buffers that will maintain
+ // capacity across calls within the same thread.
+ MessageBuffer<ReplyBuffer>::Reserve(kMaxScreenshotPixels * 3);
+ auto status = InvokeRemoteMethod<DisplayScreenshotRPC::TakeScreenshot>(index);
+ if (!status) {
+ ALOGE("ScreenshotClient::Take: Failed to take screenshot: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *return_width = status.get().width;
+ *return_height = status.get().height;
+ *out_image = std::move(status.take().buffer);
+ return 0;
+}
+
+ScreenshotClient::ScreenshotClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayScreenshotRPC::kClientPath)) {
+ auto status = InvokeRemoteMethod<DisplayScreenshotRPC::GetFormat>();
+ if (!status) {
+ ALOGE(
+ "ScreenshotClient::ScreenshotClient: Failed to retrieve screenshot "
+ "layout: %s",
+ status.GetErrorMessage().c_str());
+
+ Close(status.error());
+ } else {
+ format_ = status.get();
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdisplay/system/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/tests/graphics_app_tests.cpp b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
new file mode 100644
index 0000000..7ea3952
--- /dev/null
+++ b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
@@ -0,0 +1,177 @@
+#include <dvr/graphics.h>
+#include <gtest/gtest.h>
+
+TEST(GraphicsAppTests, CreateWarpedDisplaySurfaceParams) {
+ int width = 0, height = 0;
+ EGLNativeWindowType window = dvrCreateWarpedDisplaySurface(&width, &height);
+ EXPECT_GT(width, 0);
+ EXPECT_GT(height, 0);
+ EXPECT_NE(window, nullptr);
+}
+
+TEST(GraphicsAppTests, CreateDisplaySurface) {
+ EGLNativeWindowType window = dvrCreateDisplaySurface();
+ EXPECT_NE(window, nullptr);
+}
+
+TEST(GraphicsAppTests, CreateDisplaySurfaceExtended) {
+ 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,
+ };
+
+ EGLNativeWindowType window = dvrCreateDisplaySurfaceExtended(surface_params);
+ EXPECT_NE(window, nullptr);
+ EXPECT_GT(display_width, 0);
+ EXPECT_GT(display_height, 0);
+ EXPECT_GT(surface_width, 0);
+ EXPECT_GT(surface_height, 0);
+ EXPECT_GT(inter_lens_meters, 0);
+ EXPECT_GT(left_fov[0], 0);
+ EXPECT_GT(left_fov[1], 0);
+ EXPECT_GT(left_fov[2], 0);
+ EXPECT_GT(left_fov[3], 0);
+ EXPECT_GT(right_fov[0], 0);
+ EXPECT_GT(right_fov[1], 0);
+ EXPECT_GT(right_fov[2], 0);
+ EXPECT_GT(right_fov[3], 0);
+}
+
+TEST(GraphicsAppTests, GetNativeDisplayDimensions) {
+ int width, height;
+ dvrGetNativeDisplayDimensions(&width, &height);
+ EXPECT_GT(width, 0);
+ EXPECT_GT(height, 0);
+}
+
+TEST(GraphicsAppTests, GetDisplaySurfaceInfo) {
+ int ret, width, height, format;
+ EGLNativeWindowType window = dvrCreateDisplaySurface();
+ ASSERT_NE(window, nullptr);
+ ret = dvrGetDisplaySurfaceInfo(window, &width, &height, &format);
+ ASSERT_EQ(0, ret);
+ ASSERT_GT(width, 0);
+ ASSERT_GT(height, 0);
+ ASSERT_NE(0, format);
+}
+
+// TODO(jpoichet) How to check it worked?
+TEST(GraphicsAppTests, GraphicsSurfaceSetVisible) {
+ DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
+ DvrGraphicsContext* context = nullptr;
+ int result = dvrGraphicsContextCreate(surface_params, &context);
+ ASSERT_GE(result, 0);
+ ASSERT_NE(context, nullptr);
+ dvrGraphicsSurfaceSetVisible(context, 0);
+ dvrGraphicsSurfaceSetVisible(context, 1);
+ dvrGraphicsSurfaceSetVisible(context, 2);
+}
+
+// TODO(jpoichet) How to check it worked?
+TEST(GraphicsAppTests, GraphicsSurfaceSetZOrder) {
+ DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
+ DvrGraphicsContext* context = nullptr;
+ int result = dvrGraphicsContextCreate(surface_params, &context);
+ ASSERT_GE(result, 0);
+ ASSERT_NE(context, nullptr);
+ dvrGraphicsSurfaceSetZOrder(context, -1);
+ dvrGraphicsSurfaceSetZOrder(context, 0);
+ dvrGraphicsSurfaceSetZOrder(context, 1);
+ dvrGraphicsSurfaceSetZOrder(context, 2);
+}
+
+TEST(GraphicsAppTests, GraphicsContext) {
+ DvrGraphicsContext* context = 0;
+ 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};
+ uint64_t vsync_period = 0;
+ 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_OUT(VSYNC_PERIOD, &vsync_period),
+ DVR_SURFACE_PARAMETER_LIST_END,
+ };
+ dvrGraphicsContextCreate(surface_params, &context);
+ EXPECT_NE(nullptr, context);
+
+ DvrFrameSchedule schedule;
+ int wait_result = dvrGraphicsWaitNextFrame(context, 0, &schedule);
+ EXPECT_EQ(wait_result, 0);
+ EXPECT_GE(schedule.vsync_count, 0u);
+
+ dvrBeginRenderFrame(context);
+
+ // Check range of vsync period from 70fps to 100fps.
+ // TODO(jbates) Once we have stable hardware, clamp this range down further.
+ EXPECT_LT(vsync_period, 1000000000ul / 70ul);
+ EXPECT_GT(vsync_period, 1000000000ul / 100ul);
+
+ dvrPresent(context);
+ dvrGraphicsContextDestroy(context);
+}
+
+TEST(GraphicsAppTests, CustomSurfaceSize) {
+ DvrGraphicsContext* context = 0;
+ 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;
+ int req_width = 256, req_height = 128;
+ DvrSurfaceParameter surface_params[] = {
+ DVR_SURFACE_PARAMETER_IN(WIDTH, req_width),
+ DVR_SURFACE_PARAMETER_IN(HEIGHT, req_height),
+ 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);
+ EXPECT_NE(nullptr, context);
+
+ EXPECT_EQ(req_width, surface_width);
+ EXPECT_EQ(req_height, surface_height);
+ dvrGraphicsContextDestroy(context);
+}
+
+TEST(GraphicsAppTests, CreateVideoMeshSurface) {
+ DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
+ DvrGraphicsContext* context = nullptr;
+ int result = dvrGraphicsContextCreate(surface_params, &context);
+ EXPECT_NE(nullptr, context);
+ EXPECT_EQ(result, 0);
+
+ DvrVideoMeshSurface* surface = dvrGraphicsVideoMeshSurfaceCreate(context);
+ EXPECT_NE(nullptr, surface);
+
+ dvrGraphicsVideoMeshSurfaceDestroy(surface);
+}
diff --git a/libs/vr/libdisplay/video_mesh_surface_client.cpp b/libs/vr/libdisplay/video_mesh_surface_client.cpp
new file mode 100644
index 0000000..04cc194
--- /dev/null
+++ b/libs/vr/libdisplay/video_mesh_surface_client.cpp
@@ -0,0 +1,61 @@
+#include "include/private/dvr/video_mesh_surface_client.h"
+
+using android::pdx::LocalChannelHandle;
+
+namespace android {
+namespace dvr {
+
+/* static */
+std::unique_ptr<VideoMeshSurfaceClient> VideoMeshSurfaceClient::Import(
+ LocalChannelHandle handle) {
+ return VideoMeshSurfaceClient::Create(std::move(handle));
+}
+
+VideoMeshSurfaceClient::VideoMeshSurfaceClient(LocalChannelHandle handle)
+ : BASE(std::move(handle), SurfaceTypeEnum::VideoMesh),
+ mapped_metadata_buffer_(nullptr) {
+ // TODO(jwcai) import more data if needed.
+}
+
+std::shared_ptr<ProducerQueue> VideoMeshSurfaceClient::GetProducerQueue() {
+ if (producer_queue_ == nullptr) {
+ // Create producer queue through DisplayRPC
+ auto status =
+ InvokeRemoteMethod<DisplayRPC::VideoMeshSurfaceCreateProducerQueue>();
+ if (!status) {
+ ALOGE(
+ "VideoMeshSurfaceClient::GetProducerQueue: failed to create producer "
+ "queue: %s",
+ status.GetErrorMessage().c_str());
+ return nullptr;
+ }
+
+ producer_queue_ =
+ ProducerQueue::Import<VideoMeshSurfaceBufferMetadata>(status.take());
+ }
+ return producer_queue_;
+}
+
+volatile VideoMeshSurfaceMetadata*
+VideoMeshSurfaceClient::GetMetadataBufferPtr() {
+ if (!mapped_metadata_buffer_) {
+ if (auto buffer_producer = GetMetadataBuffer()) {
+ void* addr = nullptr;
+ const int ret = buffer_producer->GetBlobReadWritePointer(
+ sizeof(VideoMeshSurfaceMetadata), &addr);
+ if (ret < 0) {
+ ALOGE(
+ "VideoMeshSurfaceClient::GetMetadataBufferPtr: Failed to map "
+ "surface metadata: %s",
+ strerror(-ret));
+ return nullptr;
+ }
+ mapped_metadata_buffer_ = static_cast<VideoMeshSurfaceMetadata*>(addr);
+ }
+ }
+
+ return mapped_metadata_buffer_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
new file mode 100644
index 0000000..c4cad50
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -0,0 +1,72 @@
+#include "include/private/dvr/vsync_client.h"
+
+#include <cutils/log.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+VSyncClient::VSyncClient(long timeout_ms)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayVSyncRPC::kClientPath),
+ timeout_ms) {}
+
+VSyncClient::VSyncClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayVSyncRPC::kClientPath)) {}
+
+int VSyncClient::Wait(int64_t* timestamp_ns) {
+ auto status = InvokeRemoteMethod<DisplayVSyncRPC::Wait>();
+ if (!status) {
+ ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+ *timestamp_ns = status.get();
+ return 0;
+}
+
+int VSyncClient::GetFd() { return event_fd(); }
+
+int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
+ auto status = InvokeRemoteMethod<DisplayVSyncRPC::GetLastTimestamp>();
+ if (!status) {
+ ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+ *timestamp_ns = status.get();
+ return 0;
+}
+
+int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
+ uint32_t* next_vsync_count) {
+ if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
+ return -EINVAL;
+
+ auto status = InvokeRemoteMethod<DisplayVSyncRPC::GetSchedInfo>();
+ if (!status) {
+ ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *vsync_period_ns = status.get().vsync_period_ns;
+ *timestamp_ns = status.get().timestamp_ns;
+ *next_vsync_count = status.get().next_vsync_count;
+ return 0;
+}
+
+int VSyncClient::Acknowledge() {
+ auto status = InvokeRemoteMethod<DisplayVSyncRPC::Acknowledge>();
+ ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/vsync_client_api.cpp b/libs/vr/libdisplay/vsync_client_api.cpp
new file mode 100644
index 0000000..56103ed
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client_api.cpp
@@ -0,0 +1,34 @@
+#include "include/private/dvr/vsync_client_api.h"
+
+#include <private/dvr/vsync_client.h>
+
+extern "C" {
+
+dvr_vsync_client* dvr_vsync_client_create() {
+ auto client = android::dvr::VSyncClient::Create();
+ return static_cast<dvr_vsync_client*>(client.release());
+}
+
+void dvr_vsync_client_destroy(dvr_vsync_client* client) {
+ delete static_cast<android::dvr::VSyncClient*>(client);
+}
+
+int dvr_vsync_client_wait(dvr_vsync_client* client, int64_t* timestamp_ns) {
+ return static_cast<android::dvr::VSyncClient*>(client)->Wait(timestamp_ns);
+}
+
+int dvr_vsync_client_get_fd(dvr_vsync_client* client) {
+ return static_cast<android::dvr::VSyncClient*>(client)->GetFd();
+}
+
+int dvr_vsync_client_acknowledge(dvr_vsync_client* client) {
+ return static_cast<android::dvr::VSyncClient*>(client)->Acknowledge();
+}
+
+int dvr_vsync_client_get_last_timestamp(dvr_vsync_client* client,
+ int64_t* timestamp_ns) {
+ return static_cast<android::dvr::VSyncClient*>(client)->GetLastTimestamp(
+ timestamp_ns);
+}
+
+} // extern "C"
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();
+}
diff --git a/libs/vr/libdvrgraphics/Android.mk b/libs/vr/libdvrgraphics/Android.mk
new file mode 100644
index 0000000..3d84319
--- /dev/null
+++ b/libs/vr/libdvrgraphics/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ blur.cpp \
+ debug_text.cpp \
+ egl_image.cpp \
+ gpu_profiler.cpp \
+ shader_program.cpp \
+ timer_query.cpp \
+ vr_gl_extensions.cpp \
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libchrome \
+ libbufferhub \
+ libdvrcommon \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libcutils \
+ libbase \
+ libpng
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdvrgraphics
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2.png b/libs/vr/libdvrgraphics/assets/controller_proto2.png
new file mode 100644
index 0000000..ffcb646
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2.png
Binary files differ
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_body.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_body.obj
new file mode 100644
index 0000000..4e54900
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_body.obj
@@ -0,0 +1,5018 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.001777 0.012900 0.101619
+v -0.005750 0.012900 0.096150
+v -0.004652 0.012900 0.092770
+v 0.005750 0.012900 0.096150
+v 0.004652 0.012900 0.099530
+v -0.001777 0.012900 0.090681
+v 0.004652 0.012900 0.092770
+v 0.001777 0.012900 0.090681
+v -0.001777 0.012900 0.101619
+v -0.004652 0.012900 0.099530
+v -0.001777 0.012900 0.073181
+v -0.004652 0.012900 0.075270
+v 0.001777 0.012900 0.073181
+v 0.004652 0.012900 0.075270
+v 0.005750 0.012900 0.078650
+v 0.004652 0.012900 0.082030
+v -0.005750 0.012900 0.078650
+v -0.004652 0.012900 0.082030
+v -0.001777 0.012900 0.084119
+v 0.001777 0.012900 0.084119
+v -0.016000 0.012900 0.050841
+v 0.016000 0.012900 0.050835
+v 0.013856 0.012900 0.058900
+v 0.008000 0.012900 0.064756
+v -0.000000 0.012900 0.066900
+v -0.008000 0.012900 0.064756
+v -0.013856 0.012900 0.058900
+v 0.008000 0.012900 0.037044
+v -0.008000 0.012900 0.037044
+v -0.013856 0.012900 0.042900
+v 0.000000 0.012900 0.034900
+v 0.013856 0.012900 0.042900
+v -0.020000 0.006760 0.075600
+v -0.020000 0.006760 -0.012398
+v -0.018877 -0.001982 0.044797
+v -0.018480 -0.003505 0.048732
+v -0.018777 -0.001945 0.075600
+v -0.017726 -0.003833 0.163093
+v -0.013161 -0.013053 0.058159
+v -0.016670 -0.008191 0.054819
+v -0.016119 -0.008731 0.075600
+v -0.009698 -0.015662 0.059338
+v -0.012025 -0.013632 0.075600
+v -0.006504 -0.016678 0.075600
+v -0.000000 -0.017750 0.075600
+v -0.000000 -0.015479 0.162587
+v 0.006873 -0.016611 0.058552
+v 0.005238 -0.017469 0.059981
+v -0.000000 -0.018152 0.060190
+v -0.006874 -0.016610 0.058551
+v -0.005577 -0.017375 0.059951
+v -0.000001 -0.017790 0.058889
+v -0.017783 -0.005639 0.052082
+v -0.016159 -0.008440 0.053561
+v -0.018202 -0.002766 0.042130
+v -0.019040 -0.001282 0.040457
+v -0.019004 -0.001521 0.036065
+v -0.019278 -0.000710 -0.012015
+v -0.018775 -0.002635 0.031956
+v -0.018323 -0.004451 0.028346
+v -0.017636 -0.005173 0.029543
+v -0.015438 -0.011195 0.021684
+v -0.017576 -0.006793 0.025305
+v -0.017651 -0.007495 -0.011666
+v -0.016133 -0.009192 0.024525
+v -0.009254 -0.016963 0.019047
+v -0.012694 -0.014052 0.021271
+v -0.014657 -0.012757 -0.011402
+v -0.012775 -0.014437 0.019994
+v -0.004877 -0.018641 0.018555
+v 0.000000 -0.019243 0.018406
+v -0.004053 -0.017659 -0.013120
+v 0.000000 -0.020012 -0.011045
+v -0.007741 -0.016386 -0.013187
+v -0.007793 -0.018450 -0.011122
+v -0.011145 -0.016607 -0.011212
+v -0.013377 -0.011450 -0.013446
+v -0.017416 0.000890 -0.014092
+v -0.010471 -0.009590 0.164744
+v -0.020000 0.006760 0.163598
+v -0.017045 0.000607 0.165278
+v -0.019121 0.000433 0.163292
+v -0.012455 -0.011131 0.162773
+v -0.015399 -0.007954 0.162910
+v -0.011105 -0.009754 0.164735
+v -0.008928 -0.013453 0.162674
+v -0.004759 -0.014945 0.162610
+v 0.000000 -0.013582 0.164534
+v -0.016850 0.012900 -0.012204
+v -0.016850 0.011003 -0.014204
+v -0.017833 0.012745 0.163406
+v -0.016850 0.011003 0.165404
+v 0.016850 0.011003 0.165404
+v 0.016850 0.012900 0.163404
+v -0.016850 0.012900 0.163404
+v -0.018496 0.012405 0.163413
+v -0.019171 0.011802 0.163424
+v -0.017585 0.010679 0.165421
+v -0.019183 0.011788 -0.012224
+v -0.019646 0.010959 0.163436
+v -0.017717 0.010496 0.165430
+v -0.019850 0.009900 -0.012263
+v -0.017851 0.009891 0.165462
+v -0.019850 0.009900 0.163463
+v -0.017851 0.009891 -0.014262
+v -0.019650 0.010946 -0.012236
+v -0.018511 0.012395 -0.012213
+v -0.017342 0.010872 -0.014211
+v -0.017849 0.012739 -0.012207
+v 0.019850 0.009900 -0.012263
+v 0.019850 0.009900 0.163463
+v 0.019652 0.010946 0.163437
+v 0.019180 0.011789 0.163424
+v 0.019649 0.010953 -0.012236
+v 0.017351 0.010866 0.165411
+v 0.018506 0.012398 0.163413
+v 0.017845 0.012741 0.163406
+v 0.018501 0.012401 -0.012213
+v 0.016850 0.012900 -0.012204
+v 0.016850 0.011003 -0.014204
+v 0.017836 0.012745 -0.012207
+v 0.017359 0.010861 -0.014211
+v 0.019174 0.011796 -0.012224
+v 0.020000 0.006760 -0.012398
+v 0.020000 0.006760 0.075600
+v 0.020000 0.006760 0.163598
+v 0.017851 0.009891 0.165462
+v 0.004914 -0.018632 0.018558
+v 0.012694 -0.014513 0.019962
+v 0.009842 -0.017439 -0.011172
+v 0.009194 -0.016995 0.019036
+v 0.012696 -0.014051 0.021272
+v 0.016134 -0.009190 0.024527
+v 0.018283 -0.005474 -0.011769
+v 0.015491 -0.011112 0.021736
+v 0.017878 -0.005923 0.026303
+v 0.016851 -0.008573 0.023600
+v 0.018521 -0.003703 0.029628
+v 0.019042 -0.001309 0.037868
+v 0.018891 -0.002096 0.033543
+v 0.018999 -0.001456 0.042247
+v 0.018278 -0.004181 0.049954
+v 0.018757 -0.002471 0.046344
+v 0.017676 -0.004685 0.048340
+v 0.018202 -0.002767 0.042132
+v 0.017483 -0.006410 0.053015
+v 0.016158 -0.008441 0.053562
+v 0.016251 -0.008973 0.055490
+v 0.013246 -0.012968 0.058115
+v 0.012687 -0.013111 0.057055
+v 0.009848 -0.015576 0.059304
+v 0.006504 -0.016678 0.075600
+v 0.012025 -0.013632 0.075600
+v 0.012453 -0.011132 0.162773
+v 0.015398 -0.007956 0.162909
+v 0.018777 -0.001945 0.075600
+v 0.016119 -0.008731 0.075600
+v 0.017726 -0.003834 0.163093
+v 0.016090 -0.005051 -0.013781
+v 0.019494 0.000840 -0.012095
+v 0.014597 -0.009662 -0.013539
+v 0.015987 -0.010626 -0.011508
+v 0.012048 -0.013130 -0.013358
+v 0.012738 -0.015322 -0.011275
+v 0.004233 -0.017617 -0.013122
+v 0.004123 -0.019588 -0.011066
+v 0.008927 -0.013454 0.162674
+v 0.004758 -0.014945 0.162610
+v 0.007966 -0.011801 0.164627
+v 0.011095 -0.009763 0.164734
+v -0.016170 0.000544 0.165275
+v 0.019122 0.000435 0.163292
+v -0.000000 -0.018116 -0.013096
+v 0.008618 -0.015654 -0.013225
+v -0.018000 0.006760 -0.014400
+v -0.016263 -0.005366 -0.013764
+v -0.010678 -0.014560 -0.013283
+v 0.018001 0.006760 -0.014400
+v 0.017116 0.010978 -0.014205
+v -0.017101 0.010981 -0.014205
+v -0.017576 0.010688 -0.014220
+v -0.017712 0.010505 -0.014230
+v 0.017851 0.009891 -0.014262
+v 0.017713 0.010505 -0.014230
+v -0.000000 -0.016565 0.057943
+v 0.005098 -0.015909 0.057716
+v -0.012314 -0.011868 0.055681
+v 0.012233 -0.011949 0.055734
+v -0.009091 -0.014324 0.057057
+v 0.009220 -0.014251 0.057022
+v -0.014780 -0.008209 0.052349
+v 0.014512 -0.008747 0.052978
+v 0.015923 -0.005957 0.049562
+v 0.016463 -0.004315 0.046176
+v -0.016130 -0.005374 0.048538
+v 0.016787 -0.003149 0.038024
+v -0.016587 -0.003874 0.044839
+v 0.016736 -0.003311 0.042260
+v 0.016623 -0.003908 0.033843
+v -0.016780 -0.003141 0.040668
+v 0.016216 -0.005454 0.030112
+v 0.018185 -0.002935 0.035641
+v 0.015501 -0.007542 0.027010
+v 0.014368 -0.009927 0.024580
+v -0.015201 -0.008261 0.026184
+v 0.017637 -0.005172 0.029546
+v 0.011788 -0.013271 0.022276
+v 0.008677 -0.015490 0.021243
+v 0.006874 -0.017616 0.019963
+v 0.004690 -0.016976 0.020729
+v -0.008734 -0.015460 0.021254
+v -0.000000 -0.017541 0.020568
+v 0.000001 -0.018813 0.019687
+v -0.006872 -0.017617 0.019962
+v -0.004662 -0.016983 0.020727
+v -0.011831 -0.013230 0.022299
+v -0.014150 -0.010300 0.024269
+v -0.016017 -0.006100 0.029007
+v -0.018185 -0.002936 0.035639
+v -0.016751 -0.003331 0.036408
+v -0.016508 -0.004384 0.032442
+v -0.017676 -0.004684 0.048338
+v -0.012688 -0.013110 0.057054
+v -0.005290 -0.015857 0.057697
+v -0.017359 0.010861 0.165411
+v -0.017114 0.010978 0.165405
+v 0.017105 0.010980 0.165405
+v 0.017998 0.006760 0.165600
+v 0.013678 -0.007019 0.164878
+v 0.017042 0.000595 0.165277
+v -0.017998 0.006760 0.165600
+v 0.004260 -0.013110 0.164559
+v -0.004262 -0.013110 0.164559
+v -0.007973 -0.011798 0.164627
+v -0.013690 -0.007003 0.164879
+v 0.014199 0.012900 0.042702
+v 0.000000 0.012900 0.034505
+v -0.014199 0.012900 0.042702
+v -0.008198 0.012900 0.036701
+v 0.008198 0.012900 0.036701
+v -0.014199 0.012900 0.059098
+v -0.008198 0.012900 0.065099
+v -0.000000 0.012900 0.067295
+v 0.008198 0.012900 0.065099
+v 0.014199 0.012900 0.059098
+v -0.001880 0.012900 0.072863
+v -0.004923 0.012900 0.075074
+v 0.001880 0.012900 0.072863
+v 0.004923 0.012900 0.075074
+v 0.006085 0.012900 0.078650
+v 0.004923 0.012900 0.082227
+v -0.006085 0.012900 0.078650
+v -0.004923 0.012900 0.082227
+v -0.001880 0.012900 0.084437
+v 0.001880 0.012900 0.084437
+v -0.005003 0.012900 0.099785
+v -0.001911 0.012900 0.102031
+v 0.001911 0.012900 0.090269
+v 0.005003 0.012900 0.092515
+v -0.001911 0.012900 0.090269
+v 0.005003 0.012900 0.099785
+v 0.006184 0.012900 0.096150
+v -0.005003 0.012900 0.092515
+v -0.006184 0.012900 0.096150
+v 0.001911 0.012900 0.102031
+v 0.017551 0.010715 -0.014201
+v 0.017144 0.009891 -0.014262
+v 0.017010 0.010505 -0.014230
+v 0.017287 0.006760 -0.014400
+v -0.017159 0.009891 -0.014262
+v -0.017025 0.010505 -0.014230
+v -0.017302 0.006760 -0.014400
+v -0.003838 -0.017246 -0.013142
+v -0.007274 -0.016060 -0.013204
+v -0.012667 -0.011115 -0.013463
+v -0.016493 0.001142 -0.014106
+v 0.015107 -0.004951 -0.013786
+v 0.013539 -0.009795 -0.013532
+v 0.011678 -0.012609 -0.013385
+v 0.003882 -0.017207 -0.013144
+v -0.000062 -0.017671 -0.013120
+v 0.008313 -0.015223 -0.013248
+v -0.015318 -0.005229 -0.013772
+v -0.010464 -0.013804 -0.013322
+v -0.003962 -0.019557 -0.011198
+v 0.016850 0.012900 0.075600
+v 0.019850 0.009900 0.075600
+v 0.019177 0.011793 0.075600
+v 0.018504 0.012399 0.075600
+v 0.019650 0.010950 0.075600
+v -0.016850 0.012900 0.075600
+v -0.019177 0.011795 0.075600
+v -0.017841 0.012742 0.075600
+v -0.019850 0.009900 0.075600
+v -0.019648 0.010952 0.075600
+v -0.018504 0.012400 0.075600
+v 0.017708 0.010514 0.165429
+v 0.017545 0.010700 0.165401
+v 0.017078 0.010513 0.165429
+v 0.017357 0.006760 0.165600
+v 0.017216 0.009891 0.165462
+v -0.017229 0.009891 0.165462
+v -0.017370 0.006760 0.165600
+v -0.017099 0.010496 0.165430
+v -0.000000 -0.013199 0.164554
+v 0.007511 -0.011520 0.164642
+v 0.010461 -0.009598 0.164743
+v 0.012897 -0.007011 0.164878
+v 0.016165 0.000533 0.165274
+v 0.004017 -0.012754 0.164578
+v -0.004019 -0.012754 0.164578
+v -0.007518 -0.011517 0.164642
+v -0.012909 -0.006996 0.164879
+v 0.000000 0.012900 0.163404
+v 0.000000 0.011003 0.165404
+v -0.000006 0.009891 0.165462
+v -0.000006 0.006760 0.165600
+v -0.000010 0.010505 0.165429
+v -0.000003 0.000539 0.165274
+v -0.000006 -0.007003 0.164879
+v -0.000005 -0.009594 0.164743
+v -0.000003 -0.011518 0.164642
+v -0.016850 0.012900 0.031793
+v -0.020000 0.006760 0.031793
+v 0.020000 0.006760 0.031793
+v 0.016850 0.012900 0.031793
+v -0.019181 0.011791 0.031793
+v -0.017846 0.012740 0.031793
+v 0.018502 0.012400 0.031793
+v 0.019650 0.010952 0.031793
+v 0.017838 0.012744 0.031793
+v 0.019850 0.009900 0.031793
+v 0.019175 0.011795 0.031793
+v -0.019850 0.009900 0.031793
+v -0.019650 0.010948 0.031793
+v -0.018509 0.012397 0.031793
+v -0.019180 0.011791 0.037044
+v -0.020000 0.006760 0.037044
+v -0.017846 0.012741 0.037044
+v -0.016850 0.012900 0.037044
+v -0.019850 0.009900 0.037044
+v -0.019649 0.010948 0.037044
+v -0.018508 0.012397 0.037044
+v -0.014664 -0.003820 0.165046
+v 0.014655 -0.003835 0.165045
+v 0.015513 -0.003806 0.165046
+v -0.015521 -0.003790 0.165047
+v -0.000004 -0.003827 0.165045
+v 0.000251 -0.015641 -0.013226
+v -0.019850 0.009900 0.043222
+v -0.019649 0.010949 0.043222
+v -0.018507 0.012398 0.043222
+v -0.019180 0.011792 0.043222
+v -0.020000 0.006760 0.043222
+v -0.017845 0.012741 0.043222
+v -0.019850 0.009900 0.050841
+v -0.019649 0.010950 0.050841
+v -0.018506 0.012398 0.050841
+v -0.019179 0.011793 0.050841
+v -0.020000 0.006760 0.050841
+v -0.017844 0.012741 0.050841
+v -0.016850 0.012900 0.050841
+v -0.019850 0.009900 0.059353
+v -0.019649 0.010951 0.059352
+v -0.018505 0.012399 0.059353
+v -0.019178 0.011794 0.059352
+v -0.020000 0.006760 0.059352
+v -0.017843 0.012741 0.059352
+v 0.020000 0.006760 0.025653
+v -0.016850 0.012900 0.025680
+v -0.020000 0.006760 0.025653
+v 0.016850 0.012900 0.025680
+v -0.019181 0.011790 0.025677
+v -0.017847 0.012740 0.025679
+v 0.018502 0.012401 0.025678
+v 0.019650 0.010952 0.025675
+v 0.017837 0.012744 0.025679
+v 0.000251 -0.013207 -0.013354
+v 0.000000 0.012900 -0.012204
+v 0.000000 0.012900 0.025680
+v 0.000251 -0.010455 -0.013498
+v -0.000007 0.010505 -0.014230
+v -0.000008 0.009891 -0.014262
+v 0.019850 0.009900 0.025671
+v 0.019175 0.011795 0.025677
+v -0.019850 0.009900 0.025671
+v -0.019650 0.010948 0.025675
+v -0.018509 0.012396 0.025678
+v 0.017448 -0.005338 0.075600
+v 0.019388 0.002407 0.075600
+v 0.017236 0.000838 -0.014090
+v 0.016380 0.000959 -0.014096
+v 0.020000 0.006760 0.037056
+v 0.019850 0.009900 0.037056
+v 0.019175 0.011795 0.037056
+v 0.018502 0.012400 0.037056
+v 0.019650 0.010952 0.037056
+v 0.020000 0.006760 0.043243
+v 0.019850 0.009900 0.043243
+v 0.019176 0.011794 0.043243
+v 0.018502 0.012400 0.043243
+v 0.019650 0.010951 0.043243
+v 0.016395 0.012900 0.050834
+v 0.020000 0.006760 0.050835
+v 0.019850 0.009900 0.050835
+v 0.019176 0.011794 0.050835
+v 0.018503 0.012400 0.050835
+v 0.019650 0.010951 0.050836
+v 0.017838 0.012744 0.043243
+v 0.016850 0.012900 0.037044
+v 0.017838 0.012744 0.037044
+v 0.000251 0.006760 -0.014400
+v 0.000251 -0.005090 -0.013779
+v 0.016850 0.012900 0.050835
+v 0.017838 0.012744 0.050835
+v 0.016850 0.012900 0.043243
+v 0.017838 0.012744 0.075600
+v 0.000251 0.001050 -0.014101
+v -0.017448 -0.005338 0.075600
+v -0.019388 0.002407 0.075600
+v 0.020000 0.006760 0.059351
+v 0.019850 0.009900 0.059351
+v 0.019176 0.011793 0.059351
+v 0.018503 0.012400 0.059351
+v 0.019650 0.010951 0.059351
+v 0.017838 0.012744 0.059351
+v 0.016850 0.012900 0.059351
+v -0.016850 0.012900 0.043222
+v 0.000000 0.011003 -0.014204
+v 0.018424 -0.002660 0.026136
+v -0.016395 0.012900 0.050840
+v -0.016850 0.012900 0.059352
+v -0.009165 0.012900 0.107061
+v 0.018510 -0.002803 0.055317
+v 0.009165 0.012900 0.107061
+v 0.000000 0.012900 0.107061
+v -0.016000 0.009159 0.050841
+v 0.016000 0.009159 0.050835
+v 0.013856 0.009159 0.058900
+v 0.008000 0.009159 0.064756
+v -0.000000 0.009159 0.066900
+v -0.008000 0.009159 0.064756
+v -0.013856 0.009159 0.058900
+v 0.008000 0.009159 0.037044
+v -0.008000 0.009159 0.037044
+v -0.013856 0.009159 0.042900
+v 0.000000 0.009159 0.034900
+v 0.013856 0.009159 0.042900
+v 0.001777 0.009159 0.101619
+v -0.005750 0.009159 0.096150
+v -0.004652 0.009159 0.092770
+v 0.005750 0.009159 0.096150
+v 0.004652 0.009159 0.099530
+v -0.001777 0.009159 0.090681
+v 0.004652 0.009159 0.092770
+v 0.001777 0.009159 0.090681
+v -0.001777 0.009159 0.101619
+v -0.004652 0.009159 0.099530
+v -0.001777 0.009159 0.073181
+v -0.004652 0.009159 0.075270
+v 0.001777 0.009159 0.073181
+v 0.004652 0.009159 0.075270
+v 0.005750 0.009159 0.078650
+v 0.004652 0.009159 0.082030
+v -0.005750 0.009159 0.078650
+v -0.004652 0.009159 0.082030
+v -0.001777 0.009159 0.084119
+v 0.001777 0.009159 0.084119
+v 0.019986 0.007045 -0.012386
+v -0.017987 0.007044 -0.014388
+v 0.017344 0.007044 0.165587
+v 0.017987 0.007044 -0.014388
+v -0.019986 0.007045 -0.012386
+v 0.017985 0.007044 0.165587
+v 0.019986 0.007044 0.163586
+v -0.017985 0.007044 0.165587
+v -0.019986 0.007044 0.163586
+v 0.017274 0.007044 -0.014388
+v -0.017289 0.007044 -0.014388
+v 0.019986 0.007045 0.075600
+v -0.019986 0.007045 0.075600
+v -0.017357 0.007044 0.165587
+v -0.000006 0.007044 0.165587
+v -0.019986 0.007045 0.031793
+v 0.019986 0.007045 0.031793
+v -0.019986 0.007045 0.037044
+v 0.000227 0.007044 -0.014388
+v -0.019986 0.007045 0.043222
+v -0.019986 0.007045 0.050841
+v -0.019986 0.007045 0.059352
+v 0.019986 0.007045 0.025654
+v -0.019986 0.007045 0.025654
+v 0.019986 0.007045 0.037056
+v 0.019986 0.007045 0.043243
+v 0.019986 0.007045 0.050835
+v 0.019986 0.007045 0.059351
+v -0.019559 0.006736 0.075337
+v -0.019559 0.006736 -0.011771
+v -0.019559 0.006736 0.163028
+v 0.019559 0.006736 -0.011771
+v 0.019559 0.006736 0.075337
+v 0.019559 0.006736 0.163028
+v -0.017603 0.006736 -0.013672
+v 0.017604 0.006736 -0.013672
+v 0.017935 0.006736 0.165023
+v -0.017935 0.006736 0.165023
+v 0.016906 0.006736 -0.013672
+v -0.016920 0.006736 -0.013672
+v 0.017297 0.006736 0.165023
+v -0.017309 0.006736 0.165023
+v -0.000006 0.006736 0.165023
+v -0.019559 0.006736 0.031683
+v 0.019559 0.006736 0.031683
+v -0.019559 0.006736 0.036915
+v -0.019559 0.006736 0.043072
+v -0.019559 0.006736 0.050664
+v -0.019559 0.006736 0.059146
+v 0.019559 0.006736 0.025563
+v -0.019559 0.006736 0.025563
+v 0.019559 0.006736 0.036927
+v 0.019559 0.006736 0.043092
+v 0.019559 0.006736 0.050658
+v 0.000245 0.006736 -0.013672
+v 0.019559 0.006736 0.059144
+v 0.019545 0.007020 -0.011759
+v -0.017590 0.007019 -0.013660
+v 0.017284 0.007019 0.165011
+v 0.017590 0.007019 -0.013660
+v -0.019545 0.007020 -0.011759
+v 0.017922 0.007019 0.165011
+v 0.019545 0.007020 0.163016
+v -0.017922 0.007019 0.165011
+v -0.019545 0.007020 0.163016
+v 0.016893 0.007019 -0.013660
+v -0.016908 0.007019 -0.013660
+v 0.019545 0.007020 0.075337
+v -0.019545 0.007020 0.075337
+v -0.017297 0.007019 0.165011
+v -0.000006 0.007019 0.165011
+v -0.019545 0.007020 0.031683
+v 0.019545 0.007020 0.031683
+v -0.019545 0.007020 0.036915
+v 0.000222 0.007019 -0.013660
+v -0.019545 0.007020 0.043072
+v -0.019545 0.007020 0.050664
+v -0.019545 0.007020 0.059146
+v 0.019545 0.007020 0.025565
+v -0.019545 0.007020 0.025565
+v 0.019545 0.007020 0.036927
+v 0.019545 0.007020 0.043092
+v 0.019545 0.007020 0.050658
+v 0.019545 0.007020 0.059144
+v -0.017984 0.007113 -0.014384
+v 0.017341 0.007113 0.165584
+v -0.019983 0.007114 -0.012383
+v -0.019983 0.007114 0.163583
+v 0.019983 0.007114 0.075600
+v -0.000006 0.007113 0.165584
+v -0.019983 0.007114 0.031793
+v -0.019983 0.007114 0.037044
+v -0.019983 0.007114 0.043222
+v -0.019983 0.007114 0.050841
+v -0.019983 0.007114 0.059352
+v -0.019983 0.007114 0.025655
+v 0.019983 0.007114 -0.012383
+v 0.017984 0.007113 -0.014384
+v 0.017981 0.007113 0.165584
+v 0.019983 0.007114 0.163583
+v -0.017981 0.007113 0.165584
+v 0.017271 0.007113 -0.014384
+v -0.017286 0.007113 -0.014384
+v -0.019983 0.007114 0.075600
+v -0.017354 0.007113 0.165584
+v 0.019983 0.007114 0.031793
+v 0.000221 0.007113 -0.014384
+v 0.019983 0.007114 0.025655
+v 0.019983 0.007114 0.037056
+v 0.019983 0.007114 0.043243
+v 0.019983 0.007114 0.050835
+v 0.019983 0.007114 0.059351
+v -0.017987 0.006687 0.165596
+v -0.019990 0.006685 0.163594
+v 0.017987 0.006687 0.165596
+v -0.019991 0.006672 -0.012393
+v 0.017992 0.006690 -0.014396
+v -0.017994 0.006691 -0.014396
+v 0.019990 0.006685 0.163594
+v -0.019993 0.006709 0.075600
+v 0.019994 0.006690 -0.012394
+v -0.017293 0.006694 -0.014397
+v 0.017343 0.006687 0.165596
+v -0.017356 0.006687 0.165596
+v -0.000006 0.006687 0.165596
+v -0.019987 0.006657 0.050770
+v 0.000251 0.006693 -0.014396
+v -0.019989 0.006665 0.043189
+v -0.019988 0.006662 0.037032
+v -0.019986 0.006649 0.031795
+v -0.019980 0.006628 0.025684
+v 0.019993 0.006709 0.075600
+v 0.017276 0.006692 -0.014396
+v 0.019981 0.006649 0.025658
+v 0.019985 0.006651 0.050782
+v 0.019989 0.006665 0.037065
+v 0.019987 0.006656 0.031814
+v 0.019988 0.006663 0.043231
+v -0.019982 0.006639 0.059227
+v 0.019982 0.006647 0.059303
+vt 0.554694 0.657391
+vt 0.433825 0.577068
+vt 0.537413 0.732767
+vt 0.171840 0.756473
+vt 0.004212 0.517422
+vt 0.508978 0.054638
+vt 0.392232 0.366129
+vt 0.535955 0.277647
+vt 0.676569 0.426611
+vt 0.570944 0.608854
+vt 0.657163 0.340708
+vt 0.197012 0.750978
+vt 0.634797 0.224341
+vt 0.536619 0.527298
+vt 0.536314 0.531147
+vt 0.536640 0.005947
+vt 0.644694 0.154642
+vt 0.655354 0.714621
+vt 0.454133 0.386270
+vt 0.251537 0.915962
+vt 0.562843 0.123107
+vt 0.676441 0.480812
+vt 0.655355 0.642524
+vt 0.637665 0.486200
+vt 0.432241 0.529792
+vt 0.433661 0.200774
+vt 0.633750 0.219086
+vt 0.454217 0.366000
+vt 0.347872 0.351543
+vt 0.532084 0.715142
+vt 0.083498 0.916082
+vt 0.432769 0.139167
+vt 0.029777 0.173023
+vt 0.971059 0.270165
+vt 0.196511 0.924853
+vt 0.069097 0.789362
+vt 0.432118 0.226219
+vt 0.960817 0.525768
+vt 0.062888 0.898170
+vt 0.751919 0.532438
+vt 0.645243 0.032928
+vt 0.536503 0.029725
+vt 0.107693 0.763735
+vt 0.746048 0.263094
+vt 0.039255 0.237927
+vt 0.745523 0.377500
+vt 0.794566 0.698936
+vt 0.748277 0.502909
+vt 0.981678 0.688262
+vt 0.655395 0.610961
+vt 0.014520 0.474610
+vt 0.967778 0.039348
+vt 0.955233 0.260973
+vt 0.015769 0.092595
+vt 0.634942 0.252270
+vt 0.709424 0.528492
+vt 0.674041 0.251333
+vt 0.637096 0.502534
+vt 0.535204 0.530002
+vt 0.273950 0.423995
+vt 0.635823 0.535625
+vt 0.359047 0.385937
+vt 0.553218 0.164920
+vt 0.192800 0.757975
+vt 0.634735 0.222563
+vt 0.535521 0.522712
+vt 0.329998 0.345864
+vt 0.029987 0.504457
+vt 0.004768 0.505706
+vt 0.299688 0.366041
+vt 0.595338 0.596712
+vt 0.023828 0.663170
+vt 0.537897 0.678838
+vt 0.961468 0.192683
+vt 0.971275 0.266931
+vt 0.304474 0.848948
+vt 0.031633 0.458764
+vt 0.747975 0.497125
+vt 0.283964 0.890144
+vt 0.960814 0.228346
+vt 0.361245 0.386645
+vt 0.349320 0.402955
+vt 0.330282 0.408849
+vt 0.039492 0.241423
+vt 0.536760 0.713602
+vt 0.442348 0.349999
+vt 0.102746 0.756393
+vt 0.016753 0.149163
+vt 0.214218 0.745926
+vt 0.750133 0.062884
+vt 0.609524 0.161420
+vt 0.014526 0.472387
+vt 0.977669 0.236466
+vt 0.030308 0.150596
+vt 0.297338 0.894235
+vt 0.019649 0.278496
+vt 0.546671 0.126797
+vt 0.015396 0.168157
+vt 0.212882 0.760696
+vt 0.787316 0.081761
+vt 0.012582 0.624053
+vt 0.774112 0.718076
+vt 0.727050 0.629840
+vt 0.099210 0.924604
+vt 0.284711 0.787354
+vt 0.984125 0.711960
+vt 0.983477 0.045252
+vt 0.709486 0.537653
+vt 0.706809 0.620714
+vt 0.775145 0.106442
+vt 0.316737 0.380021
+vt 0.539605 0.004563
+vt 0.696671 0.159777
+vt 0.434073 0.229407
+vt 0.392184 0.386776
+vt 0.748928 0.228219
+vt 0.330347 0.406689
+vt 0.774356 0.710607
+vt 0.539622 0.459478
+vt 0.070462 0.768085
+vt 0.433350 0.596419
+vt 0.017375 0.271882
+vt 0.988091 0.736164
+vt 0.038080 0.286727
+vt 0.956653 0.498984
+vt 0.643348 0.004427
+vt 0.567158 0.121473
+vt 0.004703 0.376145
+vt 0.536588 0.737505
+vt 0.490932 0.375868
+vt 0.675853 0.533596
+vt 0.636743 0.530371
+vt 0.590770 0.475309
+vt 0.538671 0.024090
+vt 0.538521 0.033513
+vt 0.043277 0.857867
+vt 0.749450 0.032688
+vt 0.645173 0.029445
+vt 0.754393 0.023111
+vt 0.589149 0.533687
+vt 0.013383 0.242884
+vt 0.779461 0.710223
+vt 0.531271 0.710635
+vt 0.587157 0.229693
+vt 0.438687 0.371517
+vt 0.674437 0.267925
+vt 0.655378 0.678955
+vt 0.732820 0.609916
+vt 0.709914 0.619362
+vt 0.756039 0.657320
+vt 0.736972 0.091197
+vt 0.261922 0.777755
+vt 0.428189 0.680875
+vt 0.774985 0.650207
+vt 0.566885 0.642223
+vt 0.265301 0.924613
+vt 0.534033 0.735766
+vt 0.403920 0.403362
+vt 0.534025 0.222534
+vt 0.344296 0.372255
+vt 0.423430 0.342895
+vt 0.751600 0.028401
+vt 0.674169 0.257466
+vt 0.655361 0.630077
+vt 0.040907 0.832261
+vt 0.588054 0.224282
+vt 0.954687 0.488290
+vt 0.536350 0.710608
+vt 0.635114 0.258340
+vt 0.434245 0.524422
+vt 0.455933 0.365409
+vt 0.098472 0.751315
+vt 0.034060 0.471008
+vt 0.971354 0.487374
+vt 0.550671 0.093325
+vt 0.277201 0.909329
+vt 0.025321 0.643014
+vt 0.709536 0.503914
+vt 0.984312 0.378254
+vt 0.979750 0.666962
+vt 0.978251 0.601641
+vt 0.709493 0.536905
+vt 0.751446 0.535548
+vt 0.751236 0.536631
+vt 0.746915 0.245359
+vt 0.970965 0.268624
+vt 0.708384 0.222919
+vt 0.549232 0.313231
+vt 0.055990 0.784833
+vt 0.691240 0.378056
+vt 0.009608 0.248066
+vt 0.587869 0.259631
+vt 0.329955 0.343701
+vt 0.432345 0.236884
+vt 0.012306 0.642800
+vt 0.013446 0.468400
+vt 0.962921 0.644716
+vt 0.761952 0.657429
+vt 0.956562 0.255253
+vt 0.682882 0.614869
+vt 0.655394 0.612791
+vt 0.038668 0.275454
+vt 0.154653 0.930696
+vt 0.603935 0.620730
+vt 0.971265 0.271920
+vt 0.980807 0.074201
+vt 0.008589 0.510309
+vt 0.423159 0.409961
+vt 0.979928 0.526074
+vt 0.746895 0.027032
+vt 0.639296 0.291549
+vt 0.707748 0.267567
+vt 0.655352 0.710416
+vt 0.302023 0.385460
+vt 0.978558 0.100001
+vt 0.707715 0.257157
+vt 0.709559 0.481150
+vt 0.678586 0.427749
+vt 0.538563 0.295323
+vt 0.589068 0.302785
+vt 0.321827 0.363518
+vt 0.749767 0.224348
+vt 0.004374 0.516796
+vt 0.537692 0.481776
+vt 0.707680 0.218088
+vt 0.540126 0.738139
+vt 0.095330 0.906145
+vt 0.051304 0.864865
+vt 0.748400 0.008421
+vt 0.749619 0.003632
+vt 0.748082 0.003099
+vt 0.538111 0.005071
+vt 0.541146 0.004472
+vt 0.677240 0.327978
+vt 0.531948 0.713658
+vt 0.655397 0.737485
+vt 0.673187 0.226015
+vt 0.521964 0.411128
+vt 0.979908 0.525604
+vt 0.423475 0.344711
+vt 0.749228 0.023240
+vt 0.274018 0.328282
+vt 0.802967 0.001424
+vt 0.290863 0.785753
+vt 0.311067 0.849306
+vt 0.772869 0.678776
+vt 0.774077 0.718536
+vt 0.526667 0.101677
+vt 0.249993 0.755988
+vt 0.971146 0.485923
+vt 0.779256 0.718195
+vt 0.531589 0.718580
+vt 0.954594 0.266047
+vt 0.433746 0.277285
+vt 0.409325 0.371161
+vt 0.009620 0.247452
+vt 0.330149 0.391516
+vt 0.339081 0.364414
+vt 0.956471 0.519081
+vt 0.025901 0.623647
+vt 0.116296 0.915735
+vt 0.309020 0.825194
+vt 0.008185 0.682350
+vt 0.036461 0.262598
+vt 0.955339 0.493327
+vt 0.676143 0.497530
+vt 0.009030 0.235849
+vt 0.709533 0.487456
+vt 0.535066 0.255380
+vt 0.348072 0.401160
+vt 0.531348 0.710226
+vt 0.636835 0.509223
+vt 0.034995 0.511718
+vt 0.301854 0.366717
+vt 0.536597 0.028305
+vt 0.029675 0.195353
+vt 0.643361 0.009756
+vt 0.536889 0.498065
+vt 0.655354 0.713178
+vt 0.405161 0.350797
+vt 0.590859 0.472850
+vt 0.433423 0.363566
+vt 0.299959 0.386171
+vt 0.972895 0.491371
+vt 0.008259 0.560156
+vt 0.250249 0.930884
+vt 0.297009 0.848558
+vt 0.978844 0.242936
+vt 0.956744 0.521765
+vt 0.432278 0.615510
+vt 0.028368 0.091511
+vt 0.428819 0.074182
+vt 0.300447 0.816374
+vt 0.590645 0.478125
+vt 0.675014 0.537098
+vt 0.588282 0.276082
+vt 0.779220 0.718583
+vt 0.016948 0.111647
+vt 0.525790 0.653731
+vt 0.278146 0.791181
+vt 0.746247 0.257677
+vt 0.533658 0.024110
+vt 0.011107 0.662066
+vt 0.434475 0.469057
+vt 0.033360 0.231697
+vt 0.584750 0.152850
+vt 0.544124 0.678517
+vt 0.503025 0.726987
+vt 0.134691 0.743314
+vt 0.971919 0.480712
+vt 0.361166 0.366144
+vt 0.718219 0.130605
+vt 0.536565 0.504705
+vt 0.633687 0.219826
+vt 0.588135 0.269890
+vt 0.641617 0.463663
+vt 0.640483 0.461614
+vt 0.629973 0.443284
+vt 0.423192 0.408130
+vt 0.645562 0.061918
+vt 0.540987 0.029754
+vt 0.047483 0.832403
+vt 0.645257 0.034371
+vt 0.056776 0.856726
+vt 0.749529 0.032224
+vt 0.754691 0.032153
+vt 0.538302 0.738043
+vt 0.744670 0.062316
+vt 0.533634 0.024500
+vt 0.012978 0.193447
+vt 0.174444 0.741716
+vt 0.544872 0.094017
+vt 0.644305 0.152511
+vt 0.676009 0.503630
+vt 0.248607 0.770773
+vt 0.684540 0.613164
+vt 0.538354 0.735708
+vt 0.803117 0.754596
+vt 0.216163 0.923612
+vt 0.509042 0.698525
+vt 0.534821 0.009833
+vt 0.589833 0.530107
+vt 0.655353 0.732167
+vt 0.977857 0.519571
+vt 0.579456 0.137911
+vt 0.231654 0.764947
+vt 0.034543 0.248596
+vt 0.533591 0.223650
+vt 0.432271 0.234785
+vt 0.434126 0.231462
+vt 0.688923 0.378114
+vt 0.708399 0.221031
+vt 0.034526 0.247939
+vt 0.292615 0.871304
+vt 0.971832 0.273596
+vt 0.799650 0.030402
+vt 0.425214 0.041700
+vt 0.670522 0.152200
+vt 0.695412 0.140669
+vt 0.707712 0.251038
+vt 0.707717 0.244226
+vt 0.676339 0.487134
+vt 0.432252 0.527666
+vt 0.650571 0.588307
+vt 0.311618 0.402432
+vt 0.422919 0.392337
+vt 0.979884 0.228177
+vt 0.535566 0.521602
+vt 0.588120 0.226046
+vt 0.674953 0.536432
+vt 0.628248 0.312708
+vt 0.745517 0.158086
+vt 0.637511 0.490851
+vt 0.954561 0.483546
+vt 0.977404 0.126169
+vt 0.766630 0.678460
+vt 0.020850 0.685237
+vt 0.954700 0.274866
+vt 0.116373 0.930626
+vt 0.778725 0.715144
+vt 0.434018 0.475875
+vt 0.969029 0.717057
+vt 0.504650 0.422273
+vt 0.414114 0.388961
+vt 0.536884 0.715094
+vt 0.658207 0.416637
+vt 0.749371 0.218152
+vt 0.961663 0.611119
+vt 0.707784 0.273832
+vt 0.591159 0.451477
+vt 0.589762 0.500619
+vt 0.033419 0.483409
+vt 0.302237 0.803799
+vt 0.349132 0.349732
+vt 0.502727 0.423468
+vt 0.404913 0.402032
+vt 0.393972 0.366687
+vt 0.359063 0.366870
+vt 0.580721 0.134282
+vt 0.432424 0.518965
+vt 0.029970 0.505122
+vt 0.666956 0.165275
+vt 0.130987 0.758053
+vt 0.033282 0.230988
+vt 0.165394 0.748950
+vt 0.959856 0.239506
+vt 0.673878 0.244528
+vt 0.978602 0.155059
+vt 0.194514 0.939639
+vt 0.432109 0.224126
+vt 0.248619 0.924770
+vt 0.214068 0.938346
+vt 0.743041 0.091680
+vt 0.707623 0.226473
+vt 0.672964 0.217825
+vt 0.408922 0.380917
+vt 0.013225 0.240276
+vt 0.637100 0.165993
+vt 0.019745 0.376348
+vt 0.537181 0.492060
+vt 0.501237 0.328565
+vt 0.971142 0.484002
+vt 0.025254 0.580845
+vt 0.046237 0.804550
+vt 0.799776 0.726015
+vt 0.019320 0.282406
+vt 0.747751 0.491741
+vt 0.747616 0.487298
+vt 0.751529 0.622943
+vt 0.965817 0.694833
+vt 0.775565 0.736773
+vt 0.977723 0.517740
+vt 0.535534 0.266906
+vt 0.536475 0.710112
+vt 0.708447 0.598419
+vt 0.511184 0.375053
+vt 0.588948 0.525537
+vt 0.134639 0.935257
+vt 0.232458 0.750383
+vt 0.977806 0.234635
+vt 0.587127 0.228828
+vt 0.774231 0.710110
+vt 0.033491 0.465502
+vt 0.564276 0.141784
+vt 0.012884 0.072516
+vt 0.961373 0.155913
+vt 0.433206 0.265388
+vt 0.587707 0.253630
+vt 0.667836 0.379009
+vt 0.673044 0.218479
+vt 0.393842 0.386229
+vt 0.673938 0.221318
+vt 0.978894 0.511274
+vt 0.134723 0.920201
+vt 0.533909 0.231992
+vt 0.587527 0.246988
+vt 0.424401 0.360512
+vt 0.779539 0.710633
+vt 0.773947 0.713601
+vt 0.431307 0.106060
+vt 0.019614 0.280387
+vt 0.548780 0.657506
+vt 0.288979 0.807196
+vt 0.031882 0.490231
+vt 0.962067 0.124384
+vt 0.234728 0.920862
+vt 0.627895 0.614871
+vt 0.443320 0.348593
+vt 0.537398 0.293340
+vt 0.432080 0.389619
+vt 0.424600 0.713567
+vt 0.534800 0.248802
+vt 0.723508 0.150293
+vt 0.535050 0.228106
+vt 0.707636 0.225824
+vt 0.772427 0.738055
+vt 0.312856 0.400772
+vt 0.675128 0.528262
+vt 0.029839 0.111471
+vt 0.028708 0.522273
+vt 0.433635 0.553350
+vt 0.014258 0.476311
+vt 0.954473 0.270805
+vt 0.283724 0.911294
+vt 0.273718 0.376163
+vt 0.668375 0.150233
+vt 0.715959 0.128100
+vt 0.680454 0.591088
+vt 0.055062 0.799398
+vt 0.050889 0.880505
+vt 0.754359 0.022717
+vt 0.754641 0.032550
+vt 0.533543 0.033869
+vt 0.538652 0.024553
+vt 0.541071 0.028294
+vt 0.330471 0.361139
+vt 0.747009 0.028476
+vt 0.432501 0.516834
+vt 0.116076 0.745878
+vt 0.588015 0.265250
+vt 0.586855 0.221581
+vt 0.778862 0.713659
+vt 0.956688 0.232390
+vt 0.488712 0.375927
+vt 0.503327 0.028036
+vt 0.321776 0.388098
+vt 0.636669 0.532149
+vt 0.773290 0.732766
+vt 0.748710 0.509329
+vt 0.709556 0.510690
+vt 0.034108 0.477084
+vt 0.634733 0.245542
+vt 0.025427 0.069662
+vt 0.978682 0.641721
+vt 0.731380 0.629037
+vt 0.749174 0.642084
+vt 0.709440 0.529141
+vt 0.674539 0.274234
+vt 0.621388 0.590325
+vt 0.019365 0.276651
+vt 0.776699 0.735852
+vt 0.637867 0.479935
+vt 0.520882 0.339321
+vt 0.750975 0.527204
+vt 0.645827 0.091805
+vt 0.646015 0.117769
+vt 0.971376 0.482292
+vt 0.434189 0.283992
+vt 0.590404 0.484365
+vt 0.959832 0.514154
+vt 0.675237 0.329210
+vt 0.979708 0.191026
+vt 0.437733 0.381368
+vt 0.675843 0.510408
+vt 0.503178 0.329675
+vt 0.025798 0.603493
+vt 0.794708 0.056991
+vt 0.012105 0.604851
+vt 0.017221 0.130184
+vt 0.747482 0.481345
+vt 0.955680 0.473929
+vt 0.745821 0.273495
+vt 0.536629 0.718536
+vt 0.232684 0.935844
+vt 0.185956 0.932429
+vt 0.134021 0.750309
+vt 0.014205 0.470309
+vt 0.770665 0.738140
+vt 0.589917 0.528339
+vt 0.710195 0.532052
+vt 0.434300 0.522334
+vt 0.532968 0.732810
+vt 0.217665 0.930945
+vt 0.579339 0.629079
+vt 0.037980 0.269265
+vt 0.588604 0.532806
+vt 0.030407 0.130646
+vt 0.967376 0.378295
+vt 0.635602 0.534880
+vt 0.433742 0.481481
+vt 0.432791 0.251722
+vt 0.551499 0.623682
+vt 0.675115 0.528897
+vt 0.415208 0.363401
+vt 0.655354 0.708970
+vt 0.673197 0.226652
+vt 0.534984 0.226345
+vt 0.533870 0.230887
+vt 0.774148 0.737529
+vt 0.537461 0.486426
+vt 0.655372 0.620846
+vt 0.710214 0.533946
+vt 0.075712 0.892473
+vt 0.455878 0.386811
+vt 0.635263 0.264024
+vt 0.709525 0.492136
+vt 0.535309 0.261328
+vt 0.540765 0.457546
+vt 0.638200 0.293545
+vt 0.004754 0.505085
+vt 0.536594 0.718076
+vt 0.066970 0.894065
+vt 0.539219 0.009858
+vt 0.064045 0.876558
+vt 0.535776 0.006830
+vt 0.540046 0.006873
+vt 0.752926 0.008342
+vt 0.751905 0.005321
+vt 0.747505 0.005422
+vt 0.643330 0.006812
+vt 0.750869 0.004295
+vt 0.978763 0.243402
+vt 0.748979 0.227388
+vt 0.551316 0.438743
+vt 0.655353 0.717380
+vt 0.516716 0.079815
+vt 0.637316 0.496500
+vt 0.515915 0.675884
+vt 0.750919 0.526369
+vt 0.746520 0.251844
+vt 0.154248 0.923133
+vt 0.194581 0.743421
+vt 0.962073 0.571203
+vt 0.038629 0.281388
+vt 0.295445 0.826673
+vt 0.534980 0.736642
+vt 0.434221 0.177364
+vt 0.537481 0.063633
+vt 0.979562 0.563043
+vt 0.959789 0.240026
+vt 0.074590 0.772179
+vt 0.018629 0.284195
+vt 0.962374 0.092665
+vt 0.433210 0.494645
+vt 0.258438 0.767007
+vt 0.727350 0.114562
+vt 0.697277 0.142739
+vt 0.597429 0.146563
+vt 0.433479 0.271736
+vt 0.635940 0.526832
+vt 0.344015 0.381758
+vt 0.590218 0.488996
+vt 0.589506 0.507278
+vt 0.442222 0.402540
+vt 0.538004 0.475527
+vt 0.588362 0.278896
+vt 0.316426 0.370673
+vt 0.746434 0.003017
+vt 0.275577 0.770394
+vt 0.646095 0.131027
+vt 0.676255 0.491827
+vt 0.773822 0.715094
+vt 0.296673 0.882054
+vt 0.777827 0.732811
+vt 0.786819 0.673874
+vt 0.433003 0.501783
+vt 0.763117 0.122406
+vt 0.123327 0.925350
+vt 0.743824 0.642148
+vt 0.600829 0.619378
+vt 0.655353 0.718823
+vt 0.588988 0.524669
+vt 0.009192 0.236470
+vt 0.154071 0.741716
+vt 0.772291 0.735789
+vt 0.587238 0.220708
+vt 0.978813 0.510808
+vt 0.561531 0.642164
+vt 0.093101 0.914476
+vt 0.271514 0.904463
+vt 0.175243 0.924813
+vt 0.988091 0.019862
+vt 0.745931 0.267570
+vt 0.600260 0.144983
+vt 0.619960 0.152558
+vt 0.621256 0.150570
+vt 0.749384 0.219039
+vt 0.311290 0.349875
+vt 0.536696 0.525523
+vt 0.589997 0.494618
+vt 0.635522 0.274981
+vt 0.533504 0.033479
+vt 0.538597 0.033956
+vt 0.543337 0.062375
+vt 0.749193 0.022772
+vt 0.733832 0.113928
+vt 0.751479 0.026980
+vt 0.644841 0.023120
+vt 0.645011 0.024559
+vt 0.645013 0.028037
+vt 0.535718 0.271520
+vt 0.956413 0.235079
+vt 0.433777 0.158150
+vt 0.054858 0.832802
+vt 0.150736 0.756501
+vt 0.655325 0.735104
+vt 0.745790 0.137738
+vt 0.633948 0.227137
+vt 0.008436 0.512945
+vt 0.633970 0.227876
+vt 0.312513 0.351535
+vt 0.955625 0.280389
+vt 0.751786 0.530237
+vt 0.960831 0.526445
+vt 0.588393 0.281351
+vt 0.034756 0.515254
+vt 0.979851 0.228650
+vt 0.174079 0.939853
+vt 0.583671 0.629880
+vt 0.954771 0.479510
+vt 0.010724 0.585680
+vt 0.430716 0.648801
+vt 0.971613 0.739967
+vt 0.306608 0.873583
+vt 0.012346 0.481019
+vt 0.626238 0.613166
+vt 0.709525 0.497824
+vt 0.404167 0.349459
+vt 0.960824 0.227823
+vt 0.673972 0.223152
+vt 0.655367 0.657822
+vt 0.432992 0.258751
+vt 0.971547 0.015732
+vt 0.228014 0.756387
+vt 0.083275 0.774579
+vt 0.060100 0.807273
+vt 0.025131 0.558293
+vt 0.963614 0.073193
+vt 0.154068 0.937899
+vt 0.972835 0.262921
+vt 0.674278 0.263197
+vt 0.707728 0.262864
+vt 0.749885 0.222152
+vt 0.635912 0.527572
+vt 0.707545 0.217380
+vt 0.646161 0.142743
+vt 0.338718 0.389165
+vt 0.675893 0.531763
+vt 0.959900 0.514674
+vt 0.531552 0.718192
+vt 0.635378 0.268715
+vt 0.443290 0.404005
+vt 0.036256 0.293301
+vt 0.028786 0.521557
+vt 0.963982 0.673742
+vt 0.433455 0.487915
+vn -0.521090 0.772906 0.362053
+vn -0.285351 0.888921 0.358321
+vn -0.567682 0.823248 0.000000
+vn -0.311600 0.950213 0.000000
+vn 0.372764 -0.088140 -0.923731
+vn 0.321855 0.605804 -0.727606
+vn 0.913199 -0.133217 -0.385123
+vn 0.747977 0.586436 -0.310843
+vn -0.992997 0.118139 0.000000
+vn -0.992990 0.118199 0.000000
+vn -0.939966 0.341268 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.870160 0.331315 0.364764
+vn -0.720412 0.591619 0.361930
+vn -0.939332 0.343010 0.000000
+vn -0.780839 0.624732 0.000000
+vn -0.984672 -0.174417 0.000336
+vn -0.952300 -0.305104 0.005982
+vn -0.902001 -0.203565 0.380730
+vn -0.857846 -0.346612 0.379420
+vn -0.250746 -0.957540 -0.142281
+vn -0.297408 -0.954362 0.027223
+vn -0.513463 -0.830318 -0.216629
+vn -0.581935 -0.812903 0.023255
+vn -0.778583 -0.505372 0.372032
+vn -0.857846 -0.346612 0.379420
+vn -0.886617 -0.462368 0.011231
+vn -0.952300 -0.305104 0.005982
+vn -0.660766 -0.650298 0.374834
+vn -0.778583 -0.505372 0.372032
+vn -0.788745 -0.614540 0.014893
+vn -0.886617 -0.462368 0.011231
+vn 0.000000 -0.925801 0.378011
+vn -0.000855 -0.999608 0.027986
+vn 0.238233 -0.894792 0.377614
+vn 0.296405 -0.954678 0.027101
+vn 0.296405 -0.954678 0.027101
+vn -0.000855 -0.999608 0.027986
+vn 0.231212 -0.957992 -0.169686
+vn 0.004517 -0.992742 -0.120182
+vn -0.238259 -0.894775 0.377637
+vn -0.471889 -0.796857 0.377279
+vn -0.297408 -0.954362 0.027223
+vn -0.581935 -0.812903 0.023255
+vn 0.581567 -0.813174 0.022981
+vn 0.296405 -0.954678 0.027101
+vn 0.511229 -0.844469 -0.159738
+vn 0.231212 -0.957992 -0.169686
+vn 0.238233 -0.894792 0.377614
+vn 0.296405 -0.954678 0.027101
+vn 0.471859 -0.796889 0.377249
+vn 0.581567 -0.813174 0.022981
+vn 0.007019 -0.844861 -0.534939
+vn 0.004517 -0.992742 -0.120182
+vn -0.250746 -0.957540 -0.142281
+vn 0.241984 -0.835699 -0.493002
+vn 0.231212 -0.957992 -0.169686
+vn 0.004517 -0.992742 -0.120182
+vn 0.007019 -0.844861 -0.534939
+vn 0.241984 -0.835699 -0.493002
+vn 0.004517 -0.992742 -0.120182
+vn -0.243419 -0.829015 -0.503470
+vn -0.250746 -0.957540 -0.142281
+vn -0.513463 -0.830318 -0.216629
+vn -0.243419 -0.829015 -0.503470
+vn 0.007019 -0.844861 -0.534939
+vn -0.250746 -0.957540 -0.142281
+vn -0.243419 -0.829015 -0.503470
+vn -0.513463 -0.830318 -0.216629
+vn -0.710895 -0.695605 -0.103736
+vn -0.460262 -0.773604 -0.435541
+vn -0.710895 -0.695605 -0.103736
+vn -0.849339 -0.521933 -0.078800
+vn -0.243419 -0.829015 -0.503470
+vn -0.710895 -0.695605 -0.103736
+vn -0.460262 -0.773604 -0.435541
+vn -0.604308 -0.711613 -0.358355
+vn -0.849339 -0.521933 -0.078800
+vn -0.913458 -0.399399 -0.077945
+vn -0.460262 -0.773604 -0.435541
+vn -0.849339 -0.521933 -0.078800
+vn -0.604308 -0.711613 -0.358355
+vn -0.639161 -0.723760 -0.260084
+vn -0.913458 -0.399399 -0.077945
+vn -0.939019 -0.338459 -0.060733
+vn -0.604308 -0.711613 -0.358355
+vn -0.913458 -0.399399 -0.077945
+vn -0.639161 -0.723760 -0.260084
+vn -0.639161 -0.723760 -0.260084
+vn -0.939019 -0.338459 -0.060733
+vn -0.949451 -0.312272 -0.032076
+vn -0.648624 -0.756632 -0.082432
+vn -0.949451 -0.312272 -0.032076
+vn -0.954279 -0.298907 0.002472
+vn -0.639161 -0.723760 -0.260084
+vn -0.949451 -0.312272 -0.032076
+vn -0.648624 -0.756632 -0.082432
+vn 0.028932 0.428152 -0.903243
+vn 0.000000 0.418594 -0.908174
+vn 0.068943 0.929284 -0.362875
+vn 0.000000 0.928819 -0.370533
+vn -0.648624 -0.756632 -0.082432
+vn -0.954279 -0.298907 0.002472
+vn -0.951202 -0.308125 0.016572
+vn -0.647672 -0.752810 0.117467
+vn -0.951202 -0.308125 0.016572
+vn -0.949874 -0.309738 0.042452
+vn -0.859060 -0.359823 -0.364066
+vn -0.899920 -0.425957 0.093297
+vn -0.909937 -0.184063 -0.371666
+vn -0.939317 -0.333574 0.080082
+vn -0.648624 -0.756632 -0.082432
+vn -0.951202 -0.308125 0.016572
+vn -0.647672 -0.752810 0.117467
+vn -0.638581 -0.710667 0.295241
+vn -0.949874 -0.309738 0.042452
+vn -0.939317 -0.333574 0.080082
+vn -0.647672 -0.752810 0.117467
+vn -0.949874 -0.309738 0.042452
+vn -0.638581 -0.710667 0.295241
+vn -0.758811 -0.548713 -0.350885
+vn -0.782121 -0.606665 0.142282
+vn -0.859060 -0.359823 -0.364066
+vn -0.899920 -0.425957 0.093297
+vn -0.638581 -0.710667 0.295241
+vn -0.939317 -0.333574 0.080082
+vn -0.899920 -0.425957 0.093297
+vn -0.609223 -0.680913 0.406454
+vn -0.899920 -0.425957 0.093297
+vn -0.782121 -0.606665 0.142282
+vn -0.638581 -0.710667 0.295241
+vn -0.899920 -0.425957 0.093297
+vn -0.609223 -0.680913 0.406454
+vn -0.467923 -0.734358 0.491698
+vn -0.782121 -0.606665 0.142282
+vn -0.633088 -0.762062 0.135871
+vn -0.609223 -0.680913 0.406454
+vn -0.782121 -0.606665 0.142282
+vn -0.467923 -0.734358 0.491698
+vn -0.584690 -0.736127 -0.340962
+vn -0.633088 -0.762062 0.135871
+vn -0.758811 -0.548713 -0.350885
+vn -0.782121 -0.606665 0.142282
+vn -0.467923 -0.734358 0.491698
+vn -0.633088 -0.762062 0.135871
+vn -0.429001 -0.879885 0.204353
+vn -0.429001 -0.879885 0.204353
+vn -0.382251 -0.857281 -0.344896
+vn -0.200814 -0.956675 0.210824
+vn -0.191601 -0.913507 -0.358878
+vn -0.237198 -0.806417 0.541691
+vn -0.429001 -0.879885 0.204353
+vn -0.200814 -0.956675 0.210824
+vn -0.467923 -0.734358 0.491698
+vn -0.429001 -0.879885 0.204353
+vn -0.237198 -0.806417 0.541691
+vn 0.000702 -0.938897 -0.344197
+vn -0.001221 -0.985450 0.169959
+vn -0.191601 -0.913507 -0.358878
+vn -0.200814 -0.956675 0.210824
+vn -0.001221 -0.985450 0.169959
+vn 0.000275 -0.811299 0.584631
+vn -0.200814 -0.956675 0.210824
+vn 0.000702 -0.938897 -0.344197
+vn -0.191601 -0.913507 -0.358878
+vn 0.002747 -0.441857 -0.897081
+vn -0.087345 -0.430315 -0.898443
+vn 0.212324 -0.913234 -0.347739
+vn 0.439571 -0.822101 -0.361838
+vn 0.209422 -0.955035 0.209880
+vn 0.453122 -0.859397 0.236892
+vn 0.000275 -0.811299 0.584631
+vn -0.001221 -0.985450 0.169959
+vn 0.209422 -0.955035 0.209880
+vn -0.277025 -0.360404 -0.890711
+vn -0.171580 -0.405969 -0.897636
+vn -0.584690 -0.736127 -0.340962
+vn -0.382251 -0.857281 -0.344896
+vn 0.106113 -0.434098 -0.894594
+vn 0.212324 -0.913234 -0.347739
+vn 0.002747 -0.441857 -0.897081
+vn 0.000702 -0.938897 -0.344197
+vn -0.342519 -0.277268 -0.897666
+vn -0.277025 -0.360404 -0.890711
+vn -0.758811 -0.548713 -0.350885
+vn -0.584690 -0.736127 -0.340962
+vn -0.386469 -0.166759 -0.907102
+vn -0.342519 -0.277268 -0.897666
+vn -0.859060 -0.359823 -0.364066
+vn -0.758811 -0.548713 -0.350885
+vn -0.991137 -0.132636 -0.007416
+vn -0.734553 0.678551 -0.000092
+vn -0.991018 -0.133704 -0.002472
+vn -0.734685 0.678408 0.000000
+vn 0.319082 -0.282245 0.904723
+vn 0.000000 -0.052339 0.998629
+vn 0.253949 -0.348223 0.902358
+vn 0.000000 -0.052402 0.998626
+vn 0.311907 -0.598482 -0.737925
+vn 0.739459 -0.596169 -0.312703
+vn 0.233778 -0.773910 -0.588566
+vn 0.590148 -0.771523 -0.237652
+vn -0.087286 -0.425991 0.900507
+vn 0.000000 -0.434743 0.900555
+vn 0.000000 -0.052096 0.998642
+vn 0.000000 -0.052157 0.998639
+vn 0.377487 -0.139135 0.915503
+vn 0.000000 -0.052309 0.998631
+vn 0.352987 -0.211408 0.911431
+vn 0.000000 -0.052309 0.998631
+vn -0.254165 -0.348043 0.902367
+vn -0.174140 -0.397386 0.900977
+vn 0.000031 -0.052432 0.998624
+vn -0.000031 -0.052432 0.998624
+vn -0.319323 -0.281907 0.904744
+vn -0.353195 -0.211129 0.911415
+vn -0.778583 -0.505372 0.372032
+vn -0.857846 -0.346612 0.379420
+vn 0.253949 -0.348223 0.902358
+vn 0.660743 -0.650336 0.374808
+vn 0.319082 -0.282245 0.904723
+vn 0.778579 -0.505401 0.372000
+vn -0.353195 -0.211129 0.911415
+vn -0.377613 -0.138923 0.915483
+vn -0.857846 -0.346612 0.379420
+vn -0.902001 -0.203565 0.380730
+vn 0.000000 -0.434743 0.900555
+vn -0.087286 -0.425991 0.900507
+vn 0.000000 -0.925801 0.378011
+vn -0.238259 -0.894775 0.377637
+vn -0.174140 -0.397386 0.900977
+vn -0.254165 -0.348043 0.902367
+vn -0.471889 -0.796857 0.377279
+vn -0.660766 -0.650298 0.374834
+vn 0.238233 -0.894792 0.377614
+vn 0.087254 -0.426017 0.900498
+vn 0.000000 -0.925801 0.378011
+vn 0.000000 -0.434743 0.900555
+vn -0.254165 -0.348043 0.902367
+vn -0.319323 -0.281907 0.904744
+vn -0.660766 -0.650298 0.374834
+vn -0.778583 -0.505372 0.372032
+vn -0.919299 0.124793 0.373250
+vn -0.395796 0.091006 0.913818
+vn -0.870160 0.331315 0.364764
+vn -0.392471 0.192390 0.899418
+vn 0.396049 0.205456 0.894948
+vn 0.396174 0.091223 0.913633
+vn 0.867846 0.335834 0.366140
+vn 0.919256 0.124211 0.373550
+vn -0.312362 0.298720 0.901774
+vn -0.720412 0.591619 0.361930
+vn -0.392471 0.192390 0.899418
+vn -0.870160 0.331315 0.364764
+vn -0.992942 0.118598 0.000000
+vn -0.919299 0.124793 0.373250
+vn -0.939332 0.343010 0.000000
+vn -0.870160 0.331315 0.364764
+vn -0.285351 0.888921 0.358321
+vn -0.122289 0.433856 0.892645
+vn -0.069248 0.929275 0.362841
+vn -0.029177 0.427853 0.903377
+vn -0.782587 0.622541 0.000000
+vn -0.569973 0.821663 0.000000
+vn -0.722183 0.589362 -0.362083
+vn -0.523403 0.771371 -0.361988
+vn -0.313581 0.949562 0.000000
+vn -0.079471 0.996837 0.000000
+vn -0.287517 0.888248 -0.358259
+vn -0.070103 0.929281 -0.362660
+vn -0.122289 0.433856 0.892645
+vn -0.285351 0.888921 0.358321
+vn -0.224988 0.373677 0.899859
+vn -0.521090 0.772906 0.362053
+vn -0.569973 0.821663 0.000000
+vn -0.313581 0.949562 0.000000
+vn -0.523403 0.771371 -0.361988
+vn -0.287517 0.888248 -0.358259
+vn -0.939966 0.341268 0.000000
+vn -0.782587 0.622541 0.000000
+vn -0.870643 0.329573 -0.365189
+vn -0.722183 0.589362 -0.362083
+vn -0.285351 0.888921 0.358321
+vn -0.069248 0.929275 0.362841
+vn -0.311600 0.950213 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.224988 0.373677 0.899859
+vn -0.521090 0.772906 0.362053
+vn -0.312362 0.298720 0.901774
+vn -0.720412 0.591619 0.361930
+vn -0.312152 0.299273 -0.901663
+vn -0.393298 0.193094 -0.898906
+vn -0.722183 0.589362 -0.362083
+vn -0.870643 0.329573 -0.365189
+vn -0.720412 0.591619 0.361930
+vn -0.521090 0.772906 0.362053
+vn -0.780839 0.624732 0.000000
+vn -0.567682 0.823248 0.000000
+vn -0.224897 0.374747 -0.899436
+vn -0.312152 0.299273 -0.901663
+vn -0.523403 0.771371 -0.361988
+vn -0.722183 0.589362 -0.362083
+vn -0.121224 0.433042 -0.893185
+vn -0.224897 0.374747 -0.899436
+vn -0.287517 0.888248 -0.358259
+vn -0.523403 0.771371 -0.361988
+vn -0.029329 0.427604 -0.903490
+vn -0.121224 0.433042 -0.893185
+vn -0.070103 0.929281 -0.362660
+vn -0.287517 0.888248 -0.358259
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992997 0.118139 0.000000
+vn -0.992990 0.118199 0.000000
+vn 0.000000 0.052309 -0.998631
+vn 0.000000 0.048190 -0.998838
+vn -0.393298 0.193094 -0.898906
+vn -0.396046 0.091008 -0.913710
+vn -0.734964 0.678106 0.000061
+vn -0.670866 0.741578 0.000000
+vn -0.730909 0.682475 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.029085 0.427176 0.903701
+vn 0.121893 0.433583 0.892832
+vn 0.069399 0.929325 0.362684
+vn 0.286943 0.888480 0.358145
+vn 0.720131 0.591401 0.362843
+vn 0.527037 0.768230 0.363393
+vn 0.320333 0.310109 0.895108
+vn 0.235030 0.375908 0.896356
+vn 0.121893 0.433583 0.892832
+vn 0.235030 0.375908 0.896356
+vn 0.286943 0.888480 0.358145
+vn 0.527037 0.768230 0.363393
+vn 0.939143 0.343526 0.000000
+vn 0.993040 0.117774 0.000000
+vn 0.868458 0.334950 -0.365499
+vn 0.919351 0.124242 -0.373307
+vn 0.310045 -0.595674 0.740976
+vn 0.000000 -0.675817 0.737069
+vn 0.263930 -0.785352 0.559967
+vn 0.000000 -0.675849 0.737040
+vn 0.568602 0.822613 0.000000
+vn 0.780351 0.625342 0.000000
+vn 0.524598 0.770401 -0.362326
+vn 0.719155 0.592775 -0.362538
+vn 0.780351 0.625342 0.000000
+vn 0.939143 0.343526 0.000000
+vn 0.719155 0.592775 -0.362538
+vn 0.868458 0.334950 -0.365499
+vn 0.867846 0.335834 0.366140
+vn 0.720131 0.591401 0.362843
+vn 0.396049 0.205456 0.894948
+vn 0.320333 0.310109 0.895108
+vn 0.311536 0.950234 0.000000
+vn 0.568602 0.822613 0.000000
+vn 0.286295 0.888581 -0.358411
+vn 0.524598 0.770401 -0.362326
+vn 0.078190 0.996939 0.000000
+vn 0.311536 0.950234 0.000000
+vn 0.068943 0.929284 -0.362875
+vn 0.286295 0.888581 -0.358411
+vn 0.325795 0.316060 -0.891046
+vn 0.719155 0.592775 -0.362538
+vn 0.392297 0.200055 -0.897820
+vn 0.868458 0.334950 -0.365499
+vn -0.381763 0.058475 0.922409
+vn 0.000000 0.044008 0.999031
+vn -0.395796 0.091006 0.913818
+vn 0.000000 0.048434 0.998826
+vn 0.939246 0.343245 0.000000
+vn 0.993048 0.117711 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.993044 0.117741 0.000000
+vn -0.353195 -0.211129 0.911415
+vn -0.319323 -0.281907 0.904744
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn 0.239788 -0.804483 0.543423
+vn 0.209422 -0.955035 0.209880
+vn 0.453122 -0.859397 0.236892
+vn 0.239788 -0.804483 0.543423
+vn 0.000275 -0.811299 0.584631
+vn 0.209422 -0.955035 0.209880
+vn 0.640223 -0.679501 -0.358321
+vn 0.802405 -0.482688 -0.350938
+vn 0.640007 -0.753384 0.151006
+vn 0.797224 -0.586336 0.143685
+vn 0.239788 -0.804483 0.543423
+vn 0.453122 -0.859397 0.236892
+vn 0.640007 -0.753384 0.151006
+vn 0.466575 -0.738743 0.486381
+vn 0.640007 -0.753384 0.151006
+vn 0.797224 -0.586336 0.143685
+vn 0.466575 -0.738743 0.486381
+vn 0.239788 -0.804483 0.543423
+vn 0.640007 -0.753384 0.151006
+vn 0.596342 -0.683993 0.420155
+vn 0.797224 -0.586336 0.143685
+vn 0.897571 -0.428521 0.103613
+vn 0.596342 -0.683993 0.420155
+vn 0.466575 -0.738743 0.486381
+vn 0.797224 -0.586336 0.143685
+vn 0.366104 -0.154792 -0.917610
+vn 0.877705 -0.298509 -0.374869
+vn 0.374497 -0.244701 -0.894356
+vn 0.802405 -0.482688 -0.350938
+vn 0.596342 -0.683993 0.420155
+vn 0.897571 -0.428521 0.103613
+vn 0.934625 -0.355612 0.004151
+vn 0.897571 -0.428521 0.103613
+vn 0.797224 -0.586336 0.143685
+vn 0.877705 -0.298509 -0.374869
+vn 0.802405 -0.482688 -0.350938
+vn 0.638345 -0.713270 0.289416
+vn 0.934625 -0.355612 0.004151
+vn 0.949811 -0.310561 0.037569
+vn 0.638345 -0.713270 0.289416
+vn 0.596342 -0.683993 0.420155
+vn 0.934625 -0.355612 0.004151
+vn 0.638345 -0.713270 0.289416
+vn 0.949811 -0.310561 0.037569
+vn 0.950440 -0.308980 0.034579
+vn 0.985937 -0.163336 -0.035341
+vn 0.897571 -0.428521 0.103613
+vn 0.904830 -0.170541 -0.390126
+vn 0.877705 -0.298509 -0.374869
+vn 0.650787 -0.750218 0.116827
+vn 0.950440 -0.308980 0.034579
+vn 0.957065 -0.289866 0.002228
+vn 0.650787 -0.750218 0.116827
+vn 0.638345 -0.713270 0.289416
+vn 0.950440 -0.308980 0.034579
+vn 0.650787 -0.750218 0.116827
+vn 0.957065 -0.289866 0.002228
+vn 0.949985 -0.312053 -0.012299
+vn 0.649294 -0.756172 -0.081364
+vn 0.949985 -0.312053 -0.012299
+vn 0.946362 -0.321059 -0.036318
+vn 0.649294 -0.756172 -0.081364
+vn 0.650787 -0.750218 0.116827
+vn 0.949985 -0.312053 -0.012299
+vn 0.912059 -0.149299 0.381914
+vn 0.989641 -0.143562 -0.000641
+vn 0.750260 0.586248 0.305652
+vn 0.738513 0.674239 0.000000
+vn 0.637546 -0.726174 -0.257307
+vn 0.946362 -0.321059 -0.036318
+vn 0.922100 -0.382311 -0.059756
+vn 0.637546 -0.726174 -0.257307
+vn 0.649294 -0.756172 -0.081364
+vn 0.946362 -0.321059 -0.036318
+vn 0.946362 -0.321059 -0.036318
+vn 0.981702 -0.190316 0.006439
+vn 0.922100 -0.382311 -0.059756
+vn 0.637546 -0.726174 -0.257307
+vn 0.922100 -0.382311 -0.059756
+vn 0.898822 -0.432030 -0.073948
+vn 0.898822 -0.432030 -0.073948
+vn 0.951306 -0.308211 0.004853
+vn 0.834906 -0.542718 -0.091587
+vn 0.884960 -0.465507 0.012208
+vn 0.602210 -0.707044 -0.370720
+vn 0.898822 -0.432030 -0.073948
+vn 0.834906 -0.542718 -0.091587
+vn 0.602210 -0.707044 -0.370720
+vn 0.637546 -0.726174 -0.257307
+vn 0.898822 -0.432030 -0.073948
+vn 0.834906 -0.542718 -0.091587
+vn 0.884960 -0.465507 0.012208
+vn 0.726236 -0.680854 -0.094976
+vn 0.789045 -0.614139 0.015504
+vn 0.602210 -0.707044 -0.370720
+vn 0.834906 -0.542718 -0.091587
+vn 0.726236 -0.680854 -0.094976
+vn 0.789045 -0.614139 0.015504
+vn 0.581567 -0.813174 0.022981
+vn 0.726236 -0.680854 -0.094976
+vn 0.511229 -0.844469 -0.159738
+vn 0.461423 -0.761703 -0.454861
+vn 0.726236 -0.680854 -0.094976
+vn 0.511229 -0.844469 -0.159738
+vn 0.461423 -0.761703 -0.454861
+vn 0.602210 -0.707044 -0.370720
+vn 0.726236 -0.680854 -0.094976
+vn -0.513463 -0.830318 -0.216629
+vn -0.581935 -0.812903 0.023255
+vn -0.710895 -0.695605 -0.103736
+vn -0.788745 -0.614540 0.014893
+vn 0.241984 -0.835699 -0.493002
+vn 0.511229 -0.844469 -0.159738
+vn 0.231212 -0.957992 -0.169686
+vn 0.241984 -0.835699 -0.493002
+vn 0.461423 -0.761703 -0.454861
+vn 0.511229 -0.844469 -0.159738
+vn 0.471859 -0.796889 0.377249
+vn 0.581567 -0.813174 0.022981
+vn 0.660743 -0.650336 0.374808
+vn 0.789045 -0.614139 0.015504
+vn -0.471889 -0.796857 0.377279
+vn -0.660766 -0.650298 0.374834
+vn -0.581935 -0.812903 0.023255
+vn -0.788745 -0.614540 0.014893
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.171580 -0.405969 -0.897636
+vn -0.087345 -0.430315 -0.898443
+vn -0.382251 -0.857281 -0.344896
+vn -0.191601 -0.913507 -0.358878
+vn 0.087254 -0.426017 0.900498
+vn 0.238233 -0.894792 0.377614
+vn 0.173990 -0.397360 0.901018
+vn 0.471859 -0.796889 0.377249
+vn 0.173990 -0.397360 0.901018
+vn 0.471859 -0.796889 0.377249
+vn 0.253949 -0.348223 0.902358
+vn 0.660743 -0.650336 0.374808
+vn 0.319082 -0.282245 0.904723
+vn 0.778579 -0.505401 0.372000
+vn 0.352987 -0.211408 0.911431
+vn 0.857781 -0.346641 0.379541
+vn 0.901970 -0.203534 0.380821
+vn 0.377487 -0.139135 0.915503
+vn 0.857781 -0.346641 0.379541
+vn 0.352987 -0.211408 0.911431
+vn 0.375234 -0.102697 0.921223
+vn 0.912059 -0.149299 0.381914
+vn 0.329149 0.602020 0.727484
+vn 0.750260 0.586248 0.305652
+vn 0.122871 0.434167 -0.892414
+vn 0.286295 0.888581 -0.358411
+vn 0.228679 0.372332 -0.899486
+vn 0.524598 0.770401 -0.362326
+vn 0.028932 0.428152 -0.903243
+vn 0.068943 0.929284 -0.362875
+vn 0.122871 0.434167 -0.892414
+vn 0.286295 0.888581 -0.358411
+vn -0.048831 -0.536314 -0.842605
+vn 0.048037 -0.537777 -0.841717
+vn -0.000397 -0.487642 -0.873044
+vn 0.241984 -0.835699 -0.493002
+vn 0.048037 -0.537777 -0.841717
+vn 0.120276 -0.592529 -0.796519
+vn 0.007019 -0.844861 -0.534939
+vn 0.048037 -0.537777 -0.841717
+vn 0.241984 -0.835699 -0.493002
+vn -0.127296 -0.607792 0.783827
+vn -0.114081 -0.538145 0.835096
+vn 0.133795 -0.607355 0.783083
+vn 0.113316 -0.537375 0.835696
+vn 0.461423 -0.761703 -0.454861
+vn 0.120276 -0.592529 -0.796519
+vn 0.138436 -0.667855 -0.731304
+vn 0.241984 -0.835699 -0.493002
+vn 0.120276 -0.592529 -0.796519
+vn 0.461423 -0.761703 -0.454861
+vn -0.171456 -0.865459 -0.470726
+vn 0.147194 -0.854141 -0.498775
+vn -0.176492 -0.781012 -0.599058
+vn 0.183848 -0.767314 -0.614352
+vn 0.602210 -0.707044 -0.370720
+vn 0.138436 -0.667855 -0.731304
+vn 0.183848 -0.767314 -0.614352
+vn 0.461423 -0.761703 -0.454861
+vn 0.138436 -0.667855 -0.731304
+vn 0.602210 -0.707044 -0.370720
+vn -0.154304 -0.850013 0.503654
+vn 0.165595 -0.881167 0.442857
+vn -0.123083 -0.939557 0.319503
+vn 0.105839 -0.942204 0.317883
+vn 0.602210 -0.707044 -0.370720
+vn 0.183848 -0.767314 -0.614352
+vn 0.147194 -0.854141 -0.498775
+vn -0.123083 -0.939557 0.319503
+vn 0.105839 -0.942204 0.317883
+vn -0.165385 -0.979921 0.111366
+vn 0.128944 -0.988856 0.074406
+vn 0.637546 -0.726174 -0.257307
+vn 0.147194 -0.854141 -0.498775
+vn 0.095371 -0.953529 -0.285809
+vn 0.602210 -0.707044 -0.370720
+vn 0.147194 -0.854141 -0.498775
+vn 0.637546 -0.726174 -0.257307
+vn -0.121832 -0.784555 0.607972
+vn 0.125158 -0.786652 0.604577
+vn -0.154304 -0.850013 0.503654
+vn 0.165595 -0.881167 0.442857
+vn 0.637546 -0.726174 -0.257307
+vn 0.095371 -0.953529 -0.285809
+vn 0.166633 -0.977095 -0.132361
+vn -0.127296 -0.607792 0.783827
+vn 0.133795 -0.607355 0.783083
+vn -0.157268 -0.676224 0.719714
+vn 0.172404 -0.693613 0.699412
+vn 0.649294 -0.756172 -0.081364
+vn 0.166633 -0.977095 -0.132361
+vn 0.128944 -0.988856 0.074406
+vn 0.637546 -0.726174 -0.257307
+vn 0.166633 -0.977095 -0.132361
+vn 0.649294 -0.756172 -0.081364
+vn -0.140114 -0.669504 -0.729474
+vn 0.138436 -0.667855 -0.731304
+vn -0.119057 -0.591407 -0.797536
+vn 0.120276 -0.592529 -0.796519
+vn 0.650787 -0.750218 0.116827
+vn 0.128944 -0.988856 0.074406
+vn 0.105839 -0.942204 0.317883
+vn 0.649294 -0.756172 -0.081364
+vn 0.128944 -0.988856 0.074406
+vn 0.650787 -0.750218 0.116827
+vn -0.165385 -0.979921 0.111366
+vn 0.128944 -0.988856 0.074406
+vn -0.145208 -0.987646 -0.058901
+vn 0.166633 -0.977095 -0.132361
+vn 0.650787 -0.750218 0.116827
+vn 0.105839 -0.942204 0.317883
+vn 0.165595 -0.881167 0.442857
+vn -0.123999 -0.951033 -0.283125
+vn 0.095371 -0.953529 -0.285809
+vn -0.171456 -0.865459 -0.470726
+vn 0.147194 -0.854141 -0.498775
+vn 0.638345 -0.713270 0.289416
+vn 0.165595 -0.881167 0.442857
+vn 0.125158 -0.786652 0.604577
+vn 0.650787 -0.750218 0.116827
+vn 0.165595 -0.881167 0.442857
+vn 0.638345 -0.713270 0.289416
+vn 0.596342 -0.683993 0.420155
+vn 0.125158 -0.786652 0.604577
+vn 0.172404 -0.693613 0.699412
+vn 0.638345 -0.713270 0.289416
+vn 0.125158 -0.786652 0.604577
+vn 0.596342 -0.683993 0.420155
+vn -0.145208 -0.987646 -0.058901
+vn 0.166633 -0.977095 -0.132361
+vn -0.123999 -0.951033 -0.283125
+vn 0.095371 -0.953529 -0.285809
+vn 0.596342 -0.683993 0.420155
+vn 0.172404 -0.693613 0.699412
+vn 0.133795 -0.607355 0.783083
+vn -0.114081 -0.538145 0.835096
+vn -0.046175 -0.491786 0.869491
+vn 0.113316 -0.537375 0.835696
+vn 0.046267 -0.491606 0.869588
+vn 0.466575 -0.738743 0.486381
+vn 0.133795 -0.607355 0.783083
+vn 0.113316 -0.537375 0.835696
+vn 0.596342 -0.683993 0.420155
+vn 0.133795 -0.607355 0.783083
+vn 0.466575 -0.738743 0.486381
+vn 0.239788 -0.804483 0.543423
+vn 0.113316 -0.537375 0.835696
+vn 0.046267 -0.491606 0.869588
+vn 0.466575 -0.738743 0.486381
+vn 0.113316 -0.537375 0.835696
+vn 0.239788 -0.804483 0.543423
+vn -0.046175 -0.491786 0.869491
+vn 0.000031 -0.438709 0.898629
+vn 0.046267 -0.491606 0.869588
+vn 0.239788 -0.804483 0.543423
+vn 0.046267 -0.491606 0.869588
+vn 0.000275 -0.811299 0.584631
+vn -0.237198 -0.806417 0.541691
+vn -0.046175 -0.491786 0.869491
+vn -0.114081 -0.538145 0.835096
+vn 0.000275 -0.811299 0.584631
+vn -0.237198 -0.806417 0.541691
+vn -0.200814 -0.956675 0.210824
+vn -0.237198 -0.806417 0.541691
+vn 0.000275 -0.811299 0.584631
+vn -0.046175 -0.491786 0.869491
+vn -0.467923 -0.734358 0.491698
+vn -0.114081 -0.538145 0.835096
+vn -0.127296 -0.607792 0.783827
+vn -0.467923 -0.734358 0.491698
+vn -0.237198 -0.806417 0.541691
+vn -0.114081 -0.538145 0.835096
+vn -0.467923 -0.734358 0.491698
+vn -0.127296 -0.607792 0.783827
+vn -0.157268 -0.676224 0.719714
+vn -0.609223 -0.680913 0.406454
+vn -0.157268 -0.676224 0.719714
+vn -0.121832 -0.784555 0.607972
+vn -0.609223 -0.680913 0.406454
+vn -0.467923 -0.734358 0.491698
+vn -0.157268 -0.676224 0.719714
+vn -0.609223 -0.680913 0.406454
+vn -0.121832 -0.784555 0.607972
+vn -0.154304 -0.850013 0.503654
+vn -0.638581 -0.710667 0.295241
+vn -0.154304 -0.850013 0.503654
+vn -0.123083 -0.939557 0.319503
+vn -0.638581 -0.710667 0.295241
+vn -0.609223 -0.680913 0.406454
+vn -0.154304 -0.850013 0.503654
+vn -0.647672 -0.752810 0.117467
+vn -0.123083 -0.939557 0.319503
+vn -0.165385 -0.979921 0.111366
+vn -0.647672 -0.752810 0.117467
+vn -0.638581 -0.710667 0.295241
+vn -0.123083 -0.939557 0.319503
+vn -0.647672 -0.752810 0.117467
+vn -0.165385 -0.979921 0.111366
+vn -0.145208 -0.987646 -0.058901
+vn -0.648624 -0.756632 -0.082432
+vn -0.145208 -0.987646 -0.058901
+vn -0.123999 -0.951033 -0.283125
+vn -0.648624 -0.756632 -0.082432
+vn -0.647672 -0.752810 0.117467
+vn -0.145208 -0.987646 -0.058901
+vn -0.639161 -0.723760 -0.260084
+vn -0.123999 -0.951033 -0.283125
+vn -0.171456 -0.865459 -0.470726
+vn -0.639161 -0.723760 -0.260084
+vn -0.648624 -0.756632 -0.082432
+vn -0.123999 -0.951033 -0.283125
+vn -0.639161 -0.723760 -0.260084
+vn -0.171456 -0.865459 -0.470726
+vn -0.176492 -0.781012 -0.599058
+vn -0.604308 -0.711613 -0.358355
+vn -0.639161 -0.723760 -0.260084
+vn -0.176492 -0.781012 -0.599058
+vn -0.460262 -0.773604 -0.435541
+vn -0.140114 -0.669504 -0.729474
+vn -0.119057 -0.591407 -0.797536
+vn -0.460262 -0.773604 -0.435541
+vn -0.604308 -0.711613 -0.358355
+vn -0.140114 -0.669504 -0.729474
+vn -0.243419 -0.829015 -0.503470
+vn -0.119057 -0.591407 -0.797536
+vn -0.048831 -0.536314 -0.842605
+vn -0.243419 -0.829015 -0.503470
+vn -0.460262 -0.773604 -0.435541
+vn -0.119057 -0.591407 -0.797536
+vn -0.243419 -0.829015 -0.503470
+vn -0.048831 -0.536314 -0.842605
+vn 0.007019 -0.844861 -0.534939
+vn -0.745103 0.591285 -0.308552
+vn -0.734685 0.678408 0.000000
+vn -0.500057 0.836990 -0.222241
+vn -0.670866 0.741578 0.000000
+vn 0.253949 -0.348223 0.902358
+vn 0.000000 -0.052402 0.998626
+vn 0.173990 -0.397360 0.901018
+vn 0.000031 -0.052339 0.998629
+vn 0.381762 0.058535 0.922405
+vn 0.396174 0.091223 0.913633
+vn 0.000000 0.044038 0.999030
+vn 0.000000 0.048464 0.998825
+vn 0.000000 0.052126 0.998641
+vn -0.392471 0.192390 0.899418
+vn 0.000000 0.048434 0.998826
+vn -0.395796 0.091006 0.913818
+vn -0.381975 0.058596 -0.922313
+vn -0.396046 0.091008 -0.913710
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.048190 -0.998838
+vn -0.316758 0.606690 -0.729103
+vn -0.214888 0.828363 -0.517338
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732851 -0.680389
+vn -0.393298 0.193094 -0.898906
+vn -0.396046 0.091008 -0.913710
+vn -0.870643 0.329573 -0.365189
+vn -0.919325 0.124518 -0.373278
+vn 0.173990 -0.397360 0.901018
+vn 0.000031 -0.052339 0.998629
+vn 0.087254 -0.426017 0.900498
+vn 0.000000 -0.052126 0.998641
+vn -0.912094 -0.149238 0.381855
+vn -0.902001 -0.203565 0.380730
+vn -0.375319 -0.102543 0.921206
+vn -0.377613 -0.138923 0.915483
+vn 0.000000 0.044008 0.999031
+vn 0.000000 0.044069 0.999029
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.048160 -0.998840
+vn 0.395989 0.090856 -0.913750
+vn 0.000000 0.043978 -0.999033
+vn 0.382011 0.058628 -0.922296
+vn -0.319323 -0.281907 0.904744
+vn -0.254165 -0.348043 0.902367
+vn 0.000000 -0.052279 0.998633
+vn 0.000031 -0.052432 0.998624
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.719155 0.592775 -0.362538
+vn 0.325795 0.316060 -0.891046
+vn 0.524598 0.770401 -0.362326
+vn 0.228679 0.372332 -0.899486
+vn 0.919351 0.124242 -0.373307
+vn 0.395989 0.090856 -0.913750
+vn 0.868458 0.334950 -0.365499
+vn 0.392297 0.200055 -0.897820
+vn 0.998866 0.047610 0.000000
+vn 0.993055 0.117650 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.395989 0.090856 -0.913750
+vn 0.000000 0.048160 -0.998840
+vn 0.392297 0.200055 -0.897820
+vn 0.002930 0.055209 -0.998470
+vn -0.316758 0.606690 -0.729103
+vn -0.745103 0.591285 -0.308552
+vn -0.214888 0.828363 -0.517338
+vn -0.500057 0.836990 -0.222241
+vn 0.000000 0.052279 -0.998633
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.052309 -0.998631
+vn 0.000000 0.048190 -0.998838
+vn 0.392297 0.200055 -0.897820
+vn 0.002930 0.055209 -0.998470
+vn 0.325795 0.316060 -0.891046
+vn 0.228679 0.372332 -0.899486
+vn 0.228679 0.372332 -0.899486
+vn 0.002930 0.055209 -0.998470
+vn 0.122871 0.434167 -0.892414
+vn 0.028932 0.428152 -0.903243
+vn -0.393298 0.193094 -0.898906
+vn -0.312152 0.299273 -0.901663
+vn 0.000000 0.052309 -0.998631
+vn -0.224897 0.374747 -0.899436
+vn -0.029329 0.427604 -0.903490
+vn 0.000000 0.052309 -0.998631
+vn -0.121224 0.433042 -0.893185
+vn -0.224897 0.374747 -0.899436
+vn 0.000000 0.418594 -0.908174
+vn 0.028932 0.428152 -0.903243
+vn 0.000000 0.052279 -0.998633
+vn 0.002930 0.055209 -0.998470
+vn 0.184245 -0.380941 -0.906056
+vn 0.106113 -0.434098 -0.894594
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn -0.171580 -0.405969 -0.897636
+vn -0.277025 -0.360404 -0.890711
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.366104 -0.154792 -0.917610
+vn 0.374497 -0.244701 -0.894356
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn -0.386469 -0.166759 -0.907102
+vn 0.000000 -0.052309 -0.998631
+vn -0.342519 -0.277268 -0.897666
+vn 0.000000 -0.052309 -0.998631
+vn 0.106113 -0.434098 -0.894594
+vn 0.002747 -0.441857 -0.897081
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn -0.277025 -0.360404 -0.890711
+vn -0.342519 -0.277268 -0.897666
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.386469 -0.166759 -0.907102
+vn -0.385668 -0.101262 -0.917064
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.171580 -0.405969 -0.897636
+vn 0.000000 -0.052309 -0.998631
+vn -0.087345 -0.430315 -0.898443
+vn 0.000000 -0.052339 -0.998629
+vn 0.184245 -0.380941 -0.906056
+vn 0.000000 -0.052309 -0.998631
+vn 0.304368 -0.320665 -0.896958
+vn 0.000000 -0.052339 -0.998629
+vn -0.087345 -0.430315 -0.898443
+vn 0.000000 -0.052339 -0.998629
+vn 0.002747 -0.441857 -0.897081
+vn 0.000000 -0.052339 -0.998629
+vn 0.374497 -0.244701 -0.894356
+vn 0.304368 -0.320665 -0.896958
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.372764 -0.088140 -0.923731
+vn 0.321855 0.605804 -0.727606
+vn -0.316758 0.606690 -0.729103
+vn 0.000000 0.699991 -0.714152
+vn -0.379990 -0.085209 -0.921058
+vn 0.000000 -0.052309 -0.998631
+vn 0.209422 -0.955035 0.209880
+vn -0.001221 -0.985450 0.169959
+vn 0.212324 -0.913234 -0.347739
+vn 0.000702 -0.938897 -0.344197
+vn 0.184245 -0.380941 -0.906056
+vn 0.439571 -0.822101 -0.361838
+vn 0.106113 -0.434098 -0.894594
+vn 0.212324 -0.913234 -0.347739
+vn 0.720131 0.591401 0.362843
+vn 0.867846 0.335834 0.366140
+vn 0.781137 0.624360 0.000000
+vn 0.939468 0.342637 0.000000
+vn 0.527037 0.768230 0.363393
+vn 0.720131 0.591401 0.362843
+vn 0.569396 0.822064 0.000000
+vn 0.781137 0.624360 0.000000
+vn 0.867846 0.335834 0.366140
+vn 0.919256 0.124211 0.373550
+vn 0.939468 0.342637 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.919256 0.124211 0.373550
+vn 0.998866 0.047610 0.000000
+vn 0.922245 0.060458 0.381850
+vn -0.992964 0.118414 0.000000
+vn -0.992942 0.118598 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.939332 0.343010 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.939246 0.343245 0.000000
+vn 0.780499 0.625156 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.000000 0.928845 0.370470
+vn 0.000000 0.418172 0.908368
+vn 0.069399 0.929325 0.362684
+vn 0.029085 0.427176 0.903701
+vn 0.396174 0.091223 0.913633
+vn 0.396049 0.205456 0.894948
+vn 0.000000 0.048464 0.998825
+vn 0.003998 0.056093 0.998418
+vn 0.000000 0.052005 0.998647
+vn 0.000000 0.052126 0.998641
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.048434 0.998826
+vn -0.392471 0.192390 0.899418
+vn 0.000000 0.052126 0.998641
+vn -0.312362 0.298720 0.901774
+vn -0.224988 0.373677 0.899859
+vn -0.029177 0.427853 0.903377
+vn -0.122289 0.433856 0.892645
+vn 0.000000 0.052126 0.998641
+vn -0.224988 0.373677 0.899859
+vn 0.396049 0.205456 0.894948
+vn 0.320333 0.310109 0.895108
+vn 0.003998 0.056093 0.998418
+vn 0.235030 0.375908 0.896356
+vn 0.235030 0.375908 0.896356
+vn 0.121893 0.433583 0.892832
+vn 0.003998 0.056093 0.998418
+vn 0.029085 0.427176 0.903701
+vn 0.000000 0.418172 0.908368
+vn 0.000000 0.052005 0.998647
+vn 0.029085 0.427176 0.903701
+vn 0.003998 0.056093 0.998418
+vn -0.174140 -0.397386 0.900977
+vn -0.087286 -0.425991 0.900507
+vn -0.000031 -0.052432 0.998624
+vn 0.000000 -0.052096 0.998642
+vn 0.087254 -0.426017 0.900498
+vn 0.000000 -0.052126 0.998641
+vn 0.000000 -0.434743 0.900555
+vn 0.000000 -0.052157 0.998639
+vn 0.000000 -0.052309 0.998631
+vn 0.377487 -0.139135 0.915503
+vn 0.000000 -0.052370 0.998628
+vn 0.375234 -0.102697 0.921223
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 0.703021 0.711169
+vn -0.375319 -0.102543 0.921206
+vn -0.329091 0.602117 0.727430
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052370 0.998628
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052402 0.998626
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052248 0.998634
+vn 0.000031 -0.052339 0.998629
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052402 0.998626
+vn 0.000031 -0.052432 0.998624
+vn -0.000031 -0.052432 0.998624
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052248 0.998634
+vn 0.000000 -0.052279 0.998633
+vn 0.000031 -0.052432 0.998624
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 0.052126 0.998641
+vn 0.000000 0.052005 0.998647
+vn -0.029177 0.427853 0.903377
+vn 0.000000 0.418172 0.908368
+vn 0.000000 0.048464 0.998825
+vn 0.003998 0.056093 0.998418
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.052005 0.998647
+vn -0.069248 0.929275 0.362841
+vn -0.029177 0.427853 0.903377
+vn 0.000000 0.928845 0.370470
+vn 0.000000 0.418172 0.908368
+vn 0.000031 -0.052339 0.998629
+vn 0.000000 -0.052248 0.998634
+vn 0.000000 -0.052126 0.998641
+vn 0.000000 -0.052157 0.998639
+vn -0.000031 -0.052432 0.998624
+vn 0.000000 -0.052096 0.998642
+vn 0.000000 -0.052248 0.998634
+vn 0.000000 -0.052157 0.998639
+vn 0.660743 -0.650336 0.374808
+vn 0.789045 -0.614139 0.015504
+vn 0.778579 -0.505401 0.372000
+vn 0.884960 -0.465507 0.012208
+vn 0.310045 -0.595674 0.740976
+vn 0.263930 -0.785352 0.559967
+vn 0.741037 -0.593385 0.314257
+vn 0.621805 -0.753893 0.212141
+vn -0.157268 -0.676224 0.719714
+vn 0.172404 -0.693613 0.699412
+vn -0.121832 -0.784555 0.607972
+vn 0.125158 -0.786652 0.604577
+vn -0.048831 -0.536314 -0.842605
+vn -0.119057 -0.591407 -0.797536
+vn 0.048037 -0.537777 -0.841717
+vn 0.120276 -0.592529 -0.796519
+vn 0.007019 -0.844861 -0.534939
+vn -0.000397 -0.487642 -0.873044
+vn 0.048037 -0.537777 -0.841717
+vn 0.007019 -0.844861 -0.534939
+vn -0.048831 -0.536314 -0.842605
+vn -0.000397 -0.487642 -0.873044
+vn 0.000275 -0.811299 0.584631
+vn 0.046267 -0.491606 0.869588
+vn 0.000031 -0.438709 0.898629
+vn 0.000275 -0.811299 0.584631
+vn 0.000031 -0.438709 0.898629
+vn -0.046175 -0.491786 0.869491
+vn 0.439571 -0.822101 -0.361838
+vn 0.640223 -0.679501 -0.358321
+vn 0.453122 -0.859397 0.236892
+vn 0.640007 -0.753384 0.151006
+vn 0.304368 -0.320665 -0.896958
+vn 0.640223 -0.679501 -0.358321
+vn 0.184245 -0.380941 -0.906056
+vn 0.439571 -0.822101 -0.361838
+vn 0.374497 -0.244701 -0.894356
+vn 0.802405 -0.482688 -0.350938
+vn 0.304368 -0.320665 -0.896958
+vn 0.640223 -0.679501 -0.358321
+vn -0.382251 -0.857281 -0.344896
+vn -0.429001 -0.879885 0.204353
+vn -0.584690 -0.736127 -0.340962
+vn -0.633088 -0.762062 0.135871
+vn -0.176492 -0.781012 -0.599058
+vn 0.183848 -0.767314 -0.614352
+vn -0.140114 -0.669504 -0.729474
+vn 0.138436 -0.667855 -0.731304
+vn -0.604308 -0.711613 -0.358355
+vn -0.176492 -0.781012 -0.599058
+vn -0.140114 -0.669504 -0.729474
+vn 0.004517 -0.992742 -0.120182
+vn -0.000855 -0.999608 0.027986
+vn -0.250746 -0.957540 -0.142281
+vn -0.297408 -0.954362 0.027223
+vn 0.568818 0.822464 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.780499 0.625156 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992964 0.118414 0.000000
+vn -0.992942 0.118598 0.000000
+vn 0.780499 0.625156 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.780499 0.625156 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.993044 0.117741 0.000000
+vn -0.939844 0.341603 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.079319 0.996849 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.079380 0.996844 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.939844 0.341603 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.992990 0.118199 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.939844 0.341603 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993040 0.117774 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn -0.912094 -0.149238 0.381855
+vn -0.750227 0.586307 0.305621
+vn -0.990001 -0.141058 -0.000305
+vn -0.737682 0.675148 0.000000
+vn -0.991137 -0.132636 -0.007416
+vn -0.992570 -0.121649 -0.002442
+vn -0.734553 0.678551 -0.000092
+vn -0.730939 0.682443 -0.000031
+vn -0.992527 -0.122013 0.001740
+vn -0.992570 -0.121649 -0.002442
+vn -0.954279 -0.298907 0.002472
+vn -0.951202 -0.308125 0.016572
+vn -0.087286 -0.425991 0.900507
+vn -0.174140 -0.397386 0.900977
+vn -0.238259 -0.894775 0.377637
+vn -0.471889 -0.796857 0.377279
+vn 0.000000 -0.925801 0.378011
+vn -0.238259 -0.894775 0.377637
+vn -0.000855 -0.999608 0.027986
+vn -0.297408 -0.954362 0.027223
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn -0.377613 -0.138923 0.915483
+vn -0.353195 -0.211129 0.911415
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.352987 -0.211408 0.911431
+vn 0.000000 -0.052309 0.998631
+vn 0.319082 -0.282245 0.904723
+vn 0.000000 -0.052339 0.998629
+vn -0.849339 -0.521933 -0.078800
+vn -0.710895 -0.695605 -0.103736
+vn -0.886617 -0.462368 0.011231
+vn -0.788745 -0.614540 0.014893
+vn 0.000000 0.048160 -0.998840
+vn 0.000000 0.048190 -0.998838
+vn 0.002930 0.055209 -0.998470
+vn 0.000000 0.052279 -0.998633
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn -0.916742 -0.125557 -0.379234
+vn -0.909937 -0.184063 -0.371666
+vn -0.991018 -0.133704 -0.002472
+vn -0.939317 -0.333574 0.080082
+vn -0.939703 0.341993 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.939844 0.341603 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992990 0.118199 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.568658 0.822574 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.079471 0.996837 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.070103 0.929281 -0.362660
+vn 0.000000 0.928819 -0.370533
+vn -0.568658 0.822574 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.939703 0.341993 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.939703 0.341993 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.939332 0.343010 0.000000
+vn -0.780839 0.624732 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.567682 0.823248 0.000000
+vn -0.311600 0.950213 0.000000
+vn -0.568658 0.822574 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.780839 0.624732 0.000000
+vn -0.567682 0.823248 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.568658 0.822574 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.992964 0.118414 0.000000
+vn -0.939703 0.341993 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.992964 0.118414 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.079471 0.996837 0.000000
+vn -0.079380 0.996844 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.078190 0.996939 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311664 0.950192 0.000000
+vn 0.078190 0.996939 0.000000
+vn 0.311536 0.950234 0.000000
+vn 0.311664 0.950192 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.311536 0.950234 0.000000
+vn 0.568602 0.822613 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.780351 0.625342 0.000000
+vn 0.939143 0.343526 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.568602 0.822613 0.000000
+vn 0.780351 0.625342 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.939143 0.343526 0.000000
+vn 0.993040 0.117774 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.939966 0.341268 0.000000
+vn -0.782587 0.622541 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.569973 0.821663 0.000000
+vn -0.313581 0.949562 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.079380 0.996844 0.000000
+vn -0.313581 0.949562 0.000000
+vn -0.079471 0.996837 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.782587 0.622541 0.000000
+vn -0.569973 0.821663 0.000000
+vn -0.919325 0.124518 -0.373278
+vn -0.992997 0.118139 0.000000
+vn -0.870643 0.329573 -0.365189
+vn -0.939966 0.341268 0.000000
+vn 0.922348 0.060580 -0.381580
+vn 0.919351 0.124242 -0.373307
+vn 0.998866 0.047610 0.000000
+vn 0.993040 0.117774 0.000000
+vn -0.734964 0.678106 0.000061
+vn -0.730909 0.682475 0.000000
+vn -0.990831 -0.134986 0.005646
+vn -0.992527 -0.122013 0.001740
+vn -0.379990 -0.085209 -0.921058
+vn -0.916742 -0.125557 -0.379234
+vn -0.316758 0.606690 -0.729103
+vn -0.745103 0.591285 -0.308552
+vn 0.990046 -0.140663 -0.004853
+vn 0.913199 -0.133217 -0.385123
+vn 0.737157 0.675722 -0.000092
+vn 0.747977 0.586436 -0.310843
+vn 0.778579 -0.505401 0.372000
+vn 0.884960 -0.465507 0.012208
+vn 0.857781 -0.346641 0.379541
+vn 0.951306 -0.308211 0.004853
+vn 0.984351 -0.176215 -0.001099
+vn 0.901970 -0.203534 0.380821
+vn 0.951306 -0.308211 0.004853
+vn 0.857781 -0.346641 0.379541
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.366104 -0.154792 -0.917610
+vn 0.362879 -0.103340 -0.926089
+vn 0.904830 -0.170541 -0.390126
+vn 0.877705 -0.298509 -0.374869
+vn 0.362879 -0.103340 -0.926089
+vn 0.366104 -0.154792 -0.917610
+vn -0.385668 -0.101262 -0.917064
+vn -0.386469 -0.166759 -0.907102
+vn -0.909937 -0.184063 -0.371666
+vn -0.859060 -0.359823 -0.364066
+vn 0.897571 -0.428521 0.103613
+vn 0.985937 -0.163336 -0.035341
+vn 0.934625 -0.355612 0.004151
+vn -0.922339 0.060549 -0.381607
+vn -0.998866 0.047610 0.000000
+vn -0.919325 0.124518 -0.373278
+vn -0.992997 0.118139 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.568818 0.822464 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.939246 0.343245 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.939246 0.343245 0.000000
+vn 0.993048 0.117711 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.993055 0.117650 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993048 0.117711 0.000000
+vn -0.311879 -0.598488 -0.737932
+vn 0.000000 -0.679106 -0.734040
+vn -0.233778 -0.773910 -0.588566
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 0.418594 -0.908174
+vn 0.000000 0.052279 -0.998633
+vn -0.029329 0.427604 -0.903490
+vn 0.000000 0.052309 -0.998631
+vn 0.311664 0.950192 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.069399 0.929325 0.362684
+vn 0.286943 0.888480 0.358145
+vn 0.078433 0.996919 0.000000
+vn 0.312178 0.950024 0.000000
+vn 0.286943 0.888480 0.358145
+vn 0.527037 0.768230 0.363393
+vn 0.312178 0.950024 0.000000
+vn 0.569396 0.822064 0.000000
+vn 0.992243 -0.124275 0.003082
+vn 0.992707 -0.120552 -0.000946
+vn 0.731704 0.681622 0.000031
+vn 0.730525 0.682886 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.311664 0.950192 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.992707 -0.120552 -0.000946
+vn 0.990875 -0.134469 -0.009247
+vn 0.730525 0.682886 0.000000
+vn 0.735447 0.677583 -0.000153
+vn 0.311756 0.950162 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.311756 0.950162 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.312178 0.950024 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.568818 0.822464 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.568818 0.822464 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079319 0.996849 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079380 0.996844 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.886617 -0.462368 0.011231
+vn -0.952300 -0.305104 0.005982
+vn -0.849339 -0.521933 -0.078800
+vn -0.913458 -0.399399 -0.077945
+vn -0.952300 -0.305104 0.005982
+vn -0.984672 -0.174417 0.000336
+vn -0.913458 -0.399399 -0.077945
+vn -0.939019 -0.338459 -0.060733
+vn -0.990831 -0.134986 0.005646
+vn -0.989839 -0.142190 0.001251
+vn -0.734964 0.678106 0.000061
+vn -0.738163 0.674622 0.000000
+vn -0.990001 -0.141058 -0.000305
+vn -0.989839 -0.142190 0.001251
+vn -0.984672 -0.174417 0.000336
+vn -0.939019 -0.338459 -0.060733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 0.418594 -0.908174
+vn -0.029329 0.427604 -0.903490
+vn 0.000000 0.928819 -0.370533
+vn -0.070103 0.929281 -0.362660
+vn 0.078220 0.996936 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311756 0.950162 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.312178 0.950024 0.000000
+vn 0.569396 0.822064 0.000000
+vn 0.311756 0.950162 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.569396 0.822064 0.000000
+vn 0.781137 0.624360 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.781137 0.624360 0.000000
+vn 0.939468 0.342637 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.939468 0.342637 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.993055 0.117650 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993048 0.117711 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 1.000000 0.000000
+vn 0.078190 0.996939 0.000000
+vn 0.000000 0.928819 -0.370533
+vn 0.068943 0.929284 -0.362875
+vn -0.079165 0.996861 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079259 0.996854 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079044 0.996871 0.000000
+vn -0.079165 0.996861 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.079259 0.996854 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.079319 0.996849 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.079165 0.996861 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.079259 0.996854 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.079044 0.996871 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.079165 0.996861 0.000000
+vn -0.311600 0.950213 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.079044 0.996871 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079319 0.996849 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079259 0.996854 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.079044 0.996871 0.000000
+vn 0.949811 -0.310561 0.037569
+vn 0.934625 -0.355612 0.004151
+vn 0.985937 -0.163336 -0.035341
+vn 0.950440 -0.308980 0.034579
+vn 0.949811 -0.310561 0.037569
+vn 0.985937 -0.163336 -0.035341
+vn 0.990046 -0.140663 -0.004853
+vn 0.990875 -0.134469 -0.009247
+vn 0.985937 -0.163336 -0.035341
+vn 0.950440 -0.308980 0.034579
+vn 0.992243 -0.124275 0.003082
+vn 0.731704 0.681622 0.000031
+vn 0.990438 -0.137855 0.005310
+vn 0.736588 0.676342 0.000092
+vn 0.990438 -0.137855 0.005310
+vn 0.736588 0.676342 0.000092
+vn 0.989089 -0.147315 0.001312
+vn 0.739720 0.672914 0.000000
+vn 0.989641 -0.143562 -0.000641
+vn 0.984351 -0.176215 -0.001099
+vn 0.989089 -0.147315 0.001312
+vn 0.981702 -0.190316 0.006439
+vn 0.898822 -0.432030 -0.073948
+vn 0.981702 -0.190316 0.006439
+vn 0.951306 -0.308211 0.004853
+vn 0.984351 -0.176215 -0.001099
+vn 0.898822 -0.432030 -0.073948
+vn 0.922100 -0.382311 -0.059756
+vn 0.981702 -0.190316 0.006439
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.069399 0.929325 0.362684
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.069248 0.929275 0.362841
+vn 0.000000 1.000000 0.000000
+vn -0.069248 0.929275 0.362841
+vn 0.000000 0.928845 0.370470
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.069399 0.929325 0.362684
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.928845 0.370470
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.552338 0.770490 -0.318225
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn -0.552336 0.770518 -0.318162
+vn 0.000000 1.000000 0.000000
+vn -0.552336 0.770518 -0.318162
+vn 0.000000 1.000000 0.000000
+vn -0.637722 0.770266 0.001282
+vn 0.000000 1.000000 0.000000
+vn -0.637722 0.770266 0.001282
+vn 0.000000 1.000000 0.000000
+vn -0.552246 0.770001 0.319567
+vn 0.000000 1.000000 0.000000
+vn -0.552246 0.770001 0.319567
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.552244 0.770028 0.319505
+vn 0.000000 1.000000 0.000000
+vn 0.552244 0.770028 0.319505
+vn 0.000000 1.000000 0.000000
+vn 0.637722 0.770266 0.001160
+vn 0.000000 1.000000 0.000000
+vn 0.637722 0.770266 0.001160
+vn 0.000000 1.000000 0.000000
+vn 0.552338 0.770490 -0.318225
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.552244 0.770028 0.319505
+vn 0.499989 -0.000000 0.866032
+vn 0.865566 -0.000000 0.500796
+vn -0.552246 0.770001 0.319567
+vn -0.318864 0.770272 0.552274
+vn -0.865518 -0.000000 0.500877
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 0.770266 0.637723
+vn 0.318864 0.770272 0.552274
+vn 0.000000 -0.000000 1.000000
+vn 0.499989 -0.000000 0.866032
+vn -0.318864 0.770272 -0.552274
+vn -0.552336 0.770518 -0.318162
+vn -0.499989 0.000000 -0.866032
+vn -0.866523 0.000000 -0.499137
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 0.770266 -0.637723
+vn 0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.637722 0.770266 0.001160
+vn 0.552338 0.770490 -0.318225
+vn 0.999998 -0.000000 0.001831
+vn 0.866482 0.000000 -0.499209
+vn -0.552336 0.770518 -0.318162
+vn -0.637722 0.770266 0.001282
+vn -0.866523 0.000000 -0.499137
+vn -0.999998 -0.000000 0.002014
+vn -0.637722 0.770266 0.001282
+vn -0.552246 0.770001 0.319567
+vn -0.999998 -0.000000 0.002014
+vn -0.865518 -0.000000 0.500877
+vn -0.318864 0.770272 0.552274
+vn 0.000000 0.770266 0.637723
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.552338 0.770490 -0.318225
+vn 0.318864 0.770272 -0.552274
+vn 0.866482 0.000000 -0.499209
+vn 0.499989 0.000000 -0.866032
+vn 0.552244 0.770028 0.319505
+vn 0.637722 0.770266 0.001160
+vn 0.865566 -0.000000 0.500796
+vn 0.999998 -0.000000 0.001831
+vn 0.000000 0.770266 -0.637723
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 0.000000 -1.000000
+vn -0.499989 0.000000 -0.866032
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn -0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn -0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn 0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn -0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 0.365076
+vn -1.000000 0.000000 0.000000
+vn -0.809030 -0.000000 0.587767
+vn -0.502506 0.783714 0.365076
+vn -0.191936 0.783706 0.590733
+vn -0.809030 -0.000000 0.587767
+vn -0.309004 -0.000000 0.951061
+vn 0.502529 0.783702 -0.365070
+vn 0.191964 0.783728 -0.590695
+vn 0.809045 0.000000 -0.587747
+vn 0.309068 0.000000 -0.951040
+vn -0.502529 0.783702 -0.365070
+vn -0.621130 0.783707 0.000000
+vn -0.809045 0.000000 -0.587747
+vn -1.000000 0.000000 0.000000
+vn 0.191964 0.783728 -0.590695
+vn -0.191964 0.783728 -0.590695
+vn 0.309068 0.000000 -0.951040
+vn -0.309068 0.000000 -0.951040
+vn -0.191936 0.783706 0.590733
+vn 0.191936 0.783706 0.590733
+vn -0.309004 -0.000000 0.951061
+vn 0.309004 -0.000000 0.951061
+vn 0.502506 0.783714 0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.502506 0.783714 0.365076
+vn 0.309004 -0.000000 0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.621130 0.783707 0.000000
+vn 0.502529 0.783702 -0.365070
+vn 1.000000 0.000000 0.000000
+vn 0.809045 0.000000 -0.587747
+vn -0.191964 0.783728 -0.590695
+vn -0.502529 0.783702 -0.365070
+vn -0.309068 0.000000 -0.951040
+vn -0.809045 0.000000 -0.587747
+vn -0.502506 0.783714 0.365076
+vn -0.191936 0.783706 0.590733
+vn -0.809030 -0.000000 0.587767
+vn -0.309004 -0.000000 0.951061
+vn -0.191936 0.783706 0.590733
+vn 0.191936 0.783706 0.590733
+vn -0.309004 -0.000000 0.951061
+vn 0.309004 -0.000000 0.951061
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 0.365076
+vn -1.000000 0.000000 0.000000
+vn -0.809030 -0.000000 0.587767
+vn 0.621130 0.783707 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 1.000000 0.000000 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 0.590733
+vn 0.502506 0.783714 0.365076
+vn 0.309004 -0.000000 0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.502506 0.783714 0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 1.000000 0.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn -0.502506 0.783714 -0.365076
+vn -0.309004 0.000000 -0.951061
+vn -0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 -0.590733
+vn -0.191936 0.783706 -0.590733
+vn 0.309004 0.000000 -0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.502506 0.783714 -0.365076
+vn 0.191936 0.783706 -0.590733
+vn 0.809030 0.000000 -0.587767
+vn 0.309004 0.000000 -0.951061
+vn -0.502506 0.783714 -0.365076
+vn -0.621130 0.783707 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.730909 0.682475 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.730939 0.682443 -0.000031
+vn -0.670866 0.741578 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993055 0.117650 0.000000
+vn -0.737682 0.675148 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.738163 0.674622 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.735447 0.677583 -0.000153
+vn 0.737157 0.675722 -0.000092
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732851 -0.680389
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675866 0.737025
+vn 0.321855 0.605804 -0.727606
+vn 0.000000 0.699991 -0.714152
+vn 0.214916 0.828380 -0.517299
+vn 0.000000 0.732866 -0.680373
+vn -0.329091 0.602117 0.727430
+vn 0.000000 0.703021 0.711169
+vn -0.230361 0.846581 0.479828
+vn 0.000000 0.736456 0.676486
+vn 0.329149 0.602020 0.727484
+vn 0.230361 0.846581 0.479828
+vn 0.000000 0.702975 0.711215
+vn 0.000000 0.736456 0.676486
+vn -0.311879 -0.598488 -0.737932
+vn -0.233778 -0.773910 -0.588566
+vn -0.739452 -0.596163 -0.312731
+vn -0.590148 -0.771523 -0.237652
+vn 0.311907 -0.598482 -0.737925
+vn 0.233778 -0.773910 -0.588566
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679106 -0.734040
+vn 0.738513 0.674239 0.000000
+vn 0.739720 0.672914 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.922245 0.060458 0.381850
+vn -0.741037 -0.593385 0.314257
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.730939 0.682443 -0.000031
+vn -0.670866 0.741578 0.000000
+vn -0.734553 0.678551 -0.000092
+vn -0.670866 0.741578 0.000000
+vn 0.741037 -0.593385 0.314257
+vn 0.621805 -0.753893 0.212141
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.922245 0.060458 0.381850
+vn -0.741037 -0.593385 0.314257
+vn -0.381763 0.058475 0.922409
+vn -0.310013 -0.595732 0.740942
+vn 0.750260 0.586248 0.305652
+vn 0.738513 0.674239 0.000000
+vn 0.531041 0.824090 0.197157
+vn 0.670866 0.741578 0.000000
+vn -0.310013 -0.595732 0.740942
+vn -0.741037 -0.593385 0.314257
+vn -0.263930 -0.785352 0.559967
+vn -0.621790 -0.753906 0.212136
+vn -0.381975 0.058596 -0.922313
+vn -0.311879 -0.598488 -0.737932
+vn -0.922339 0.060549 -0.381607
+vn -0.739452 -0.596163 -0.312731
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.922245 0.060458 0.381850
+vn 0.381762 0.058535 0.922405
+vn 0.741037 -0.593385 0.314257
+vn 0.310045 -0.595674 0.740976
+vn 0.922348 0.060580 -0.381580
+vn 0.739459 -0.596169 -0.312703
+vn 0.382011 0.058628 -0.922296
+vn 0.311907 -0.598482 -0.737925
+vn 0.736588 0.676342 0.000092
+vn 0.731704 0.681622 0.000031
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679106 -0.734040
+vn -0.741037 -0.593385 0.314257
+vn -0.742650 -0.669679 -0.000000
+vn -0.621790 -0.753906 0.212136
+vn -0.742346 -0.670016 -0.000000
+vn 0.000000 -0.675866 0.737025
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 0.732866 -0.680373
+vn 0.500093 0.836968 -0.222243
+vn 0.214916 0.828380 -0.517299
+vn 0.590148 -0.771523 -0.237652
+vn 0.233778 -0.773910 -0.588566
+vn 0.621805 -0.753893 0.212141
+vn 0.263930 -0.785352 0.559967
+vn 0.531041 0.824090 0.197157
+vn 0.230361 0.846581 0.479828
+vn -0.233778 -0.773910 -0.588566
+vn -0.214888 0.828363 -0.517338
+vn -0.590148 -0.771523 -0.237652
+vn -0.500057 0.836990 -0.222241
+vn -0.621790 -0.753906 0.212136
+vn -0.531041 0.824090 0.197157
+vn -0.263930 -0.785352 0.559967
+vn -0.230361 0.846581 0.479828
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.621790 -0.753906 0.212136
+vn -0.531041 0.824090 0.197157
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.732851 -0.680389
+vn 0.000000 0.732866 -0.680373
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.500057 0.836990 -0.222241
+vn -0.670866 0.741578 0.000000
+vn -0.590148 -0.771523 -0.237652
+vn -0.742346 -0.670016 -0.000000
+vn 0.500093 0.836968 -0.222243
+vn 0.590148 -0.771523 -0.237652
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.531041 0.824090 0.197157
+vn 0.621805 -0.753893 0.212141
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.000000 -0.679867 -0.733336
+vn 0.233778 -0.773910 -0.588566
+vn 0.000000 0.732866 -0.680373
+vn 0.214916 0.828380 -0.517299
+vn 0.000000 0.736456 0.676486
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675866 0.737025
+vn -0.214888 0.828363 -0.517338
+vn -0.233778 -0.773910 -0.588566
+vn 0.000000 0.732851 -0.680389
+vn 0.000000 -0.679867 -0.733336
+vn 0.230361 0.846581 0.479828
+vn 0.263930 -0.785352 0.559967
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.675849 0.737040
+vn -0.230361 0.846581 0.479828
+vn 0.000000 0.736456 0.676486
+vn -0.263930 -0.785352 0.559967
+vn 0.000000 -0.675849 0.737040
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.730525 0.682886 0.000000
+vn 0.735447 0.677583 -0.000153
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.329149 0.602020 0.727484
+vn 0.750260 0.586248 0.305652
+vn 0.230361 0.846581 0.479828
+vn 0.531041 0.824090 0.197157
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn -0.329091 0.602117 0.727430
+vn -0.230361 0.846581 0.479828
+vn -0.750227 0.586307 0.305621
+vn -0.531041 0.824090 0.197157
+vn -0.310013 -0.595732 0.740942
+vn -0.263930 -0.785352 0.559967
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675866 0.737025
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.703021 0.711169
+vn 0.000000 0.702990 0.711200
+vn 0.000000 0.736456 0.676486
+vn 0.000000 0.736456 0.676486
+vn 0.747977 0.586436 -0.310843
+vn 0.500093 0.836968 -0.222243
+vn 0.737157 0.675722 -0.000092
+vn 0.670866 0.741578 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.000000 0.702975 0.711215
+vn 0.000000 0.736456 0.676486
+vn 0.000000 0.702990 0.711200
+vn 0.000000 0.736456 0.676486
+vn -0.734553 0.678551 -0.000092
+vn -0.670866 0.741578 0.000000
+vn -0.734685 0.678408 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.321855 0.605804 -0.727606
+vn 0.214916 0.828380 -0.517299
+vn 0.747977 0.586436 -0.310843
+vn 0.500093 0.836968 -0.222243
+vn 0.731704 0.681622 0.000031
+vn 0.730525 0.682886 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.739452 -0.596163 -0.312731
+vn -0.590148 -0.771523 -0.237652
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.739459 -0.596169 -0.312703
+vn 0.742650 -0.669679 -0.000000
+vn 0.590148 -0.771523 -0.237652
+vn 0.742346 -0.670016 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.738163 0.674622 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.734964 0.678106 0.000061
+vn -0.670866 0.741578 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.739720 0.672914 0.000000
+vn 0.736588 0.676342 0.000092
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.750227 0.586307 0.305621
+vn -0.531041 0.824090 0.197157
+vn -0.737682 0.675148 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.000000 0.044038 0.999030
+vn 0.000000 0.044069 0.999029
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.044069 0.999029
+vn 0.000000 0.048464 0.998825
+vn 0.000000 0.044038 0.999030
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.048160 -0.998840
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.922348 0.060580 -0.381580
+vn 0.382011 0.058628 -0.922296
+vn 0.919351 0.124242 -0.373307
+vn 0.395989 0.090856 -0.913750
+vn 0.396174 0.091223 0.913633
+vn 0.381762 0.058535 0.922405
+vn 0.919256 0.124211 0.373550
+vn 0.922245 0.060458 0.381850
+vn -0.396046 0.091008 -0.913710
+vn -0.381975 0.058596 -0.922313
+vn -0.919325 0.124518 -0.373278
+vn -0.922339 0.060549 -0.381607
+vn -0.922245 0.060458 0.381850
+vn -0.381763 0.058475 0.922409
+vn -0.919299 0.124793 0.373250
+vn -0.395796 0.091006 0.913818
+vn -0.992942 0.118598 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.919299 0.124793 0.373250
+vn -0.922245 0.060458 0.381850
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn -0.739452 -0.596163 -0.312731
+vn -0.742650 -0.669679 -0.000000
+vn -0.922339 0.060549 -0.381607
+vn -0.998866 0.047610 0.000000
+vn 0.739459 -0.596169 -0.312703
+vn 0.922348 0.060580 -0.381580
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.741037 -0.593385 0.314257
+vn 0.922245 0.060458 0.381850
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.000000 0.043978 -0.999033
+vn 0.382011 0.058628 -0.922296
+vn 0.000000 -0.679106 -0.734040
+vn 0.311907 -0.598482 -0.737925
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 0.044008 0.999031
+vn 0.000000 0.044069 0.999029
+vn -0.311879 -0.598488 -0.737932
+vn -0.381975 0.058596 -0.922313
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 0.043978 -0.999033
+vn 0.310045 -0.595674 0.740976
+vn 0.381762 0.058535 0.922405
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 0.044038 0.999030
+vn -0.310013 -0.595732 0.740942
+vn 0.000000 -0.675849 0.737040
+vn -0.381763 0.058475 0.922409
+vn 0.000000 0.044008 0.999031
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn 0.738513 0.674239 0.000000
+vn 0.989641 -0.143562 -0.000641
+vn 0.739720 0.672914 0.000000
+vn 0.989089 -0.147315 0.001312
+vn 0.946362 -0.321059 -0.036318
+vn 0.990438 -0.137855 0.005310
+vn 0.981702 -0.190316 0.006439
+vn 0.989089 -0.147315 0.001312
+vn 0.949985 -0.312053 -0.012299
+vn 0.992243 -0.124275 0.003082
+vn 0.946362 -0.321059 -0.036318
+vn 0.990438 -0.137855 0.005310
+vn 0.737157 0.675722 -0.000092
+vn 0.735447 0.677583 -0.000153
+vn 0.990046 -0.140663 -0.004853
+vn 0.990875 -0.134469 -0.009247
+vn -0.737682 0.675148 0.000000
+vn -0.738163 0.674622 0.000000
+vn -0.990001 -0.141058 -0.000305
+vn -0.989839 -0.142190 0.001251
+vn -0.949451 -0.312272 -0.032076
+vn -0.939019 -0.338459 -0.060733
+vn -0.990831 -0.134986 0.005646
+vn -0.989839 -0.142190 0.001251
+vn 0.957065 -0.289866 0.002228
+vn 0.950440 -0.308980 0.034579
+vn 0.992707 -0.120552 -0.000946
+vn 0.990875 -0.134469 -0.009247
+vn 0.949985 -0.312053 -0.012299
+vn 0.957065 -0.289866 0.002228
+vn 0.992243 -0.124275 0.003082
+vn 0.992707 -0.120552 -0.000946
+vn 0.985937 -0.163336 -0.035341
+vn 0.904830 -0.170541 -0.390126
+vn 0.990046 -0.140663 -0.004853
+vn 0.913199 -0.133217 -0.385123
+vn -0.909937 -0.184063 -0.371666
+vn -0.916742 -0.125557 -0.379234
+vn -0.385668 -0.101262 -0.917064
+vn -0.379990 -0.085209 -0.921058
+vn -0.990831 -0.134986 0.005646
+vn -0.992527 -0.122013 0.001740
+vn -0.949451 -0.312272 -0.032076
+vn -0.954279 -0.298907 0.002472
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.745103 0.591285 -0.308552
+vn -0.916742 -0.125557 -0.379234
+vn -0.734685 0.678408 0.000000
+vn -0.991018 -0.133704 -0.002472
+vn -0.730909 0.682475 0.000000
+vn -0.730939 0.682443 -0.000031
+vn -0.992527 -0.122013 0.001740
+vn -0.992570 -0.121649 -0.002442
+vn -0.949874 -0.309738 0.042452
+vn -0.951202 -0.308125 0.016572
+vn -0.991137 -0.132636 -0.007416
+vn -0.992570 -0.121649 -0.002442
+vn -0.912094 -0.149238 0.381855
+vn -0.990001 -0.141058 -0.000305
+vn -0.902001 -0.203565 0.380730
+vn -0.984672 -0.174417 0.000336
+vn 0.000000 0.703021 0.711169
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 0.702990 0.711200
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052370 0.998628
+vn 0.000000 0.702990 0.711200
+vn 0.000000 0.702975 0.711215
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn -0.377613 -0.138923 0.915483
+vn -0.375319 -0.102543 0.921206
+vn 0.000000 -0.052370 0.998628
+vn 0.375234 -0.102697 0.921223
+vn 0.000000 0.702975 0.711215
+vn 0.329149 0.602020 0.727484
+vn -0.385668 -0.101262 -0.917064
+vn -0.379990 -0.085209 -0.921058
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.362879 -0.103340 -0.926089
+vn 0.372764 -0.088140 -0.923731
+vn -0.750227 0.586307 0.305621
+vn -0.912094 -0.149238 0.381855
+vn -0.329091 0.602117 0.727430
+vn -0.375319 -0.102543 0.921206
+vn 0.377487 -0.139135 0.915503
+vn 0.901970 -0.203534 0.380821
+vn 0.375234 -0.102697 0.921223
+vn 0.912059 -0.149299 0.381914
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.984351 -0.176215 -0.001099
+vn 0.989641 -0.143562 -0.000641
+vn 0.901970 -0.203534 0.380821
+vn 0.912059 -0.149299 0.381914
+vn -0.991137 -0.132636 -0.007416
+vn -0.991018 -0.133704 -0.002472
+vn -0.949874 -0.309738 0.042452
+vn -0.939317 -0.333574 0.080082
+vn 0.362879 -0.103340 -0.926089
+vn 0.372764 -0.088140 -0.923731
+vn 0.904830 -0.170541 -0.390126
+vn 0.913199 -0.133217 -0.385123
+f 96/173/1 91/443/2 296/560/3
+f 296/560/3 91/443/2 293/381/4
+f 585/367/5 178/687/6 589/699/7
+f 589/699/7 178/687/6 124/80/8
+f 386/509/9 334/510/10 387/48/11
+f 387/48/11 334/510/10 335/178/12
+f 353/597/13 352/373/14 337/266/15
+f 337/266/15 352/373/14 343/631/16
+f 100/392/17 97/511/18 295/614/19
+f 295/614/19 97/511/18 292/726/20
+f 420/481/21 37/2/22 82/707/23
+f 82/707/23 37/2/22 38/423/24
+f 51/340/25 44/153/26 42/598/27
+f 42/598/27 44/153/26 43/692/28
+f 84/536/29 38/423/30 419/121/31
+f 419/121/31 38/423/30 37/2/32
+f 83/260/33 84/536/34 41/290/35
+f 41/290/35 84/536/34 419/121/36
+f 46/513/37 45/357/38 168/291/39
+f 168/291/39 45/357/38 152/292/40
+f 152/292/41 45/357/42 48/6/43
+f 48/6/43 45/357/42 49/505/44
+f 87/72/45 86/177/46 44/153/47
+f 44/153/47 86/177/46 43/692/48
+f 153/460/49 152/292/50 151/596/51
+f 151/596/51 152/292/50 48/6/52
+f 168/291/53 152/292/54 167/479/55
+f 167/479/55 152/292/54 153/460/56
+f 52/322/57 49/165/58 51/424/59
+f 47/228/60 48/136/61 49/165/62
+f 52/322/63 47/228/64 49/165/65
+f 50/489/66 51/424/67 42/189/68
+f 50/489/69 52/322/70 51/424/71
+f 50/489/72 42/189/73 39/120/74
+f 223/611/75 39/120/76 40/172/77
+f 50/489/78 39/120/79 223/611/80
+f 54/87/81 40/172/82 53/499/83
+f 223/611/84 40/172/85 54/87/86
+f 222/546/87 53/499/88 36/309/89
+f 54/87/90 53/499/91 222/546/92
+f 222/546/93 36/309/94 35/644/95
+f 55/405/96 35/644/97 56/331/98
+f 222/546/99 35/644/100 55/405/101
+f 120/355/102 429/179/103 119/682/104
+f 119/682/104 429/179/103 379/558/105
+f 55/405/106 56/331/107 57/602/108
+f 219/12/109 57/602/110 59/89/111
+f 64/388/112 63/429/113 58/603/114
+f 58/603/114 63/429/113 60/148/115
+f 55/405/116 57/602/117 219/12/118
+f 61/704/119 59/89/120 60/439/121
+f 219/12/122 59/89/123 61/704/124
+f 68/197/125 62/154/126 64/388/127
+f 64/388/127 62/154/126 63/429/128
+f 61/704/129 60/439/130 63/249/131
+f 65/615/132 63/249/133 62/629/134
+f 61/704/135 63/249/136 65/615/137
+f 67/105/138 62/629/139 69/244/140
+f 65/615/141 62/629/142 67/105/143
+f 76/725/144 69/635/145 68/197/146
+f 68/197/146 69/635/145 62/154/147
+f 67/105/148 69/244/149 66/393/150
+f 66/47/151 75/430/152 70/425/153
+f 70/425/153 75/430/152 285/382/154
+f 214/293/155 66/393/156 70/262/157
+f 67/105/158 66/393/159 214/293/160
+f 73/693/161 71/338/162 285/382/163
+f 285/382/163 71/338/162 70/425/164
+f 71/245/165 213/76/166 70/262/167
+f 73/693/168 285/382/169 173/123/170
+f 173/123/170 285/382/169 72/106/171
+f 166/52/172 130/708/173 128/356/174
+f 128/356/174 130/708/173 131/537/175
+f 213/76/176 71/245/177 128/694/178
+f 177/180/179 74/49/180 76/725/181
+f 76/725/181 74/49/180 75/430/182
+f 165/107/183 166/52/184 173/652/185
+f 173/652/185 166/52/184 73/703/186
+f 77/514/187 177/180/188 68/197/189
+f 68/197/189 177/180/188 76/725/190
+f 176/181/191 77/514/192 64/388/193
+f 64/388/193 77/514/192 68/197/194
+f 598/108/195 324/182/196 599/184/197
+f 599/184/197 324/182/196 371/183/198
+f 229/516/199 308/639/200 170/515/201
+f 170/515/201 308/639/200 307/103/202
+f 472/288/203 469/406/204 528/93/205
+f 528/93/205 469/406/204 525/672/206
+f 233/696/207 88/50/208 311/467/209
+f 311/467/209 88/50/208 305/201/210
+f 230/246/211 309/376/212 346/198/213
+f 346/198/213 309/376/212 345/150/214
+f 85/554/215 234/640/216 79/689/217
+f 79/689/217 234/640/216 312/204/218
+f 235/538/219 347/691/220 84/536/221
+f 84/536/221 347/691/220 38/423/222
+f 170/539/223 154/557/224 229/88/225
+f 229/88/225 154/557/224 155/94/226
+f 347/691/227 81/285/228 38/423/229
+f 38/423/229 81/285/228 82/707/230
+f 88/263/231 233/303/232 46/377/233
+f 46/377/233 233/303/232 87/72/234
+f 234/195/235 85/101/236 86/177/237
+f 86/177/237 85/101/236 83/260/238
+f 168/291/239 232/54/240 46/513/241
+f 46/513/241 232/54/240 88/445/242
+f 85/101/243 235/538/244 83/260/245
+f 83/260/245 235/538/244 84/536/246
+f 104/464/247 103/695/248 100/392/249
+f 100/392/249 103/695/248 101/482/250
+f 297/520/251 127/122/252 112/555/253
+f 112/555/253 127/122/252 111/264/254
+f 98/51/255 97/511/256 101/482/257
+f 101/482/257 97/511/256 100/392/258
+f 294/636/259 104/464/260 295/614/261
+f 295/614/261 104/464/260 100/392/262
+f 91/443/263 226/547/264 95/77/265
+f 95/77/265 226/547/264 92/196/266
+f 373/78/267 388/427/268 99/167/269
+f 99/167/269 388/427/268 107/374/270
+f 374/428/271 370/540/272 109/690/273
+f 109/690/273 370/540/272 89/541/274
+f 226/547/275 91/443/276 225/92/277
+f 225/92/277 91/443/276 96/173/278
+f 388/427/279 374/428/280 107/374/281
+f 107/374/281 374/428/280 109/690/282
+f 387/48/283 373/78/284 106/265/285
+f 106/265/285 373/78/284 99/167/286
+f 91/443/287 95/77/288 293/381/289
+f 293/381/289 95/77/288 291/304/290
+f 225/92/291 96/173/292 98/51/293
+f 98/51/293 96/173/292 97/511/294
+f 181/250/295 182/174/296 99/167/297
+f 99/167/297 182/174/296 106/265/298
+f 97/511/299 96/173/300 292/726/301
+f 292/726/301 96/173/300 296/560/302
+f 108/422/303 181/250/304 107/374/305
+f 107/374/305 181/250/304 99/167/306
+f 180/527/307 108/422/308 109/690/309
+f 109/690/309 108/422/308 107/374/310
+f 90/310/311 180/527/312 89/541/313
+f 89/541/313 180/527/312 109/690/314
+f 564/599/315 559/56/316 386/509/317
+f 386/509/317 559/56/316 334/510/318
+f 271/586/319 270/583/320 182/585/321
+f 182/585/321 270/583/320 105/341/322
+f 360/556/323 516/342/324 354/559/325
+f 354/559/325 516/342/324 515/507/326
+f 93/612/327 227/426/328 94/723/329
+f 94/723/329 227/426/328 117/124/330
+f 113/202/331 116/604/332 298/96/333
+f 298/96/333 116/604/332 115/461/334
+f 227/426/335 115/461/336 117/124/337
+f 117/124/337 115/461/336 116/604/338
+f 376/600/339 384/185/340 114/53/341
+f 114/53/341 384/185/340 110/199/342
+f 474/251/343 471/102/344 530/380/345
+f 530/380/345 471/102/344 527/632/346
+f 375/44/347 385/301/348 118/483/349
+f 118/483/349 385/301/348 123/253/350
+f 385/301/351 376/600/352 123/253/353
+f 123/253/353 376/600/352 114/53/354
+f 112/555/355 113/202/356 297/520/357
+f 297/520/357 113/202/356 298/96/358
+f 377/653/359 375/44/360 121/378/361
+f 121/378/361 375/44/360 118/483/362
+f 372/542/363 377/653/364 119/682/365
+f 119/682/365 377/653/364 121/378/366
+f 266/186/367 123/253/368 184/75/369
+f 184/75/369 123/253/368 114/53/370
+f 569/252/371 573/543/372 103/552/373
+f 103/552/373 573/543/372 302/3/374
+f 402/55/375 399/512/376 397/57/377
+f 397/57/377 399/512/376 394/407/378
+f 347/462/379 235/648/380 344/1/381
+f 344/1/381 235/648/380 313/155/382
+f 209/633/383 128/694/384 131/95/385
+f 209/633/386 213/76/387 128/694/388
+f 164/613/389 162/465/390 129/100/391
+f 129/100/391 162/465/390 135/110/392
+f 209/633/393 131/95/394 129/484/395
+f 132/176/396 129/484/397 135/156/398
+f 132/176/399 209/633/400 129/484/401
+f 133/411/402 135/156/403 137/286/404
+f 133/411/405 132/176/406 135/156/407
+f 159/408/408 134/446/409 161/375/410
+f 161/375/410 134/446/409 162/465/411
+f 133/411/412 137/286/413 136/544/414
+f 137/637/415 135/110/416 134/446/417
+f 134/446/417 135/110/416 162/465/418
+f 206/553/419 136/544/420 138/412/421
+f 206/553/422 133/411/423 136/544/424
+f 206/553/425 138/412/426 140/409/427
+f 430/372/428 137/637/429 160/74/430
+f 160/74/430 137/637/429 134/446/431
+f 202/545/432 140/409/433 139/688/434
+f 202/545/435 206/553/436 140/409/437
+f 202/545/438 139/688/439 141/709/440
+f 145/203/441 141/709/442 143/438/443
+f 145/203/444 202/545/445 141/709/446
+f 587/404/447 600/410/448 126/305/449
+f 126/305/449 600/410/448 125/37/450
+f 144/638/451 143/438/452 142/379/453
+f 144/638/454 145/203/455 143/438/456
+f 143/91/457 434/63/458 142/306/459
+f 144/638/460 142/379/461 146/104/462
+f 146/444/463 156/607/464 148/97/465
+f 148/97/465 156/607/464 389/673/466
+f 147/649/467 146/104/468 148/31/469
+f 147/649/470 144/638/471 146/104/472
+f 148/97/473 389/673/474 149/248/475
+f 149/248/475 389/673/474 157/32/476
+f 147/649/477 148/31/478 149/39/479
+f 157/32/480 153/460/481 149/248/482
+f 149/248/482 153/460/481 151/596/483
+f 150/582/484 149/39/485 151/490/486
+f 150/582/487 147/649/488 149/39/489
+f 42/598/490 43/692/491 39/299/492
+f 39/299/492 43/692/491 41/290/493
+f 47/228/494 151/490/495 48/136/496
+f 47/228/497 150/582/498 151/490/499
+f 167/479/500 153/460/501 154/557/502
+f 154/557/502 153/460/501 157/32/503
+f 86/177/504 83/260/505 43/692/506
+f 43/692/506 83/260/505 41/290/507
+f 277/151/508 278/616/509 413/525/510
+f 413/525/510 278/616/509 381/526/511
+f 595/323/512 590/663/513 412/41/514
+f 412/41/514 590/663/513 272/135/515
+f 413/525/516 283/175/517 418/320/518
+f 418/320/518 283/175/517 276/664/519
+f 74/49/520 72/106/521 75/430/522
+f 75/430/522 72/106/521 285/382/523
+f 232/54/524 168/291/525 169/298/526
+f 169/298/526 168/291/525 167/479/527
+f 169/298/528 167/479/529 170/539/530
+f 170/539/530 167/479/529 154/557/531
+f 229/88/532 155/94/533 346/98/534
+f 346/98/534 155/94/533 158/33/535
+f 172/276/536 230/330/537 158/33/538
+f 158/33/538 230/330/537 346/98/539
+f 583/267/540 587/404/541 228/643/542
+f 228/643/542 587/404/541 126/305/543
+f 179/205/544 121/378/545 122/34/546
+f 122/34/546 121/378/545 118/483/547
+f 120/355/548 119/682/549 179/205/550
+f 179/205/550 119/682/549 121/378/551
+f 224/706/552 186/324/553 185/674/554
+f 47/228/555 186/324/556 190/584/557
+f 52/322/558 186/324/559 47/228/560
+f 216/300/561 211/463/562 207/650/563
+f 207/650/563 211/463/562 208/79/564
+f 150/582/565 190/584/566 188/573/567
+f 47/228/568 190/584/569 150/582/570
+f 195/403/571 193/261/572 191/43/573
+f 191/43/573 193/261/572 192/227/574
+f 147/649/575 188/573/576 192/227/577
+f 150/582/578 188/573/579 147/649/580
+f 218/346/581 201/339/582 221/99/583
+f 221/99/583 201/339/582 199/35/584
+f 147/649/585 192/227/586 193/261/587
+f 221/99/588 199/35/589 220/64/590
+f 220/64/590 199/35/589 196/651/591
+f 144/638/592 193/261/593 194/454/594
+f 147/649/595 193/261/596 144/638/597
+f 205/335/598 203/466/599 218/346/600
+f 218/346/600 203/466/599 201/339/601
+f 144/638/602 194/454/603 198/601/604
+f 216/300/605 207/650/606 217/152/607
+f 217/152/607 207/650/606 204/20/608
+f 145/203/609 198/601/610 196/651/611
+f 144/638/612 198/601/613 145/203/614
+f 187/705/615 188/573/616 189/36/617
+f 189/36/617 188/573/616 190/584/618
+f 202/545/619 196/651/620 199/35/621
+f 145/203/622 196/651/623 202/545/624
+f 220/64/625 196/651/626 200/4/627
+f 200/4/627 196/651/626 198/601/628
+f 202/545/629 199/35/630 201/339/631
+f 197/675/632 194/454/633 195/403/634
+f 195/403/634 194/454/633 193/261/635
+f 206/553/636 201/339/637 203/466/638
+f 202/545/639 201/339/640 206/553/641
+f 133/411/642 203/466/643 204/20/644
+f 206/553/645 203/466/646 133/411/647
+f 200/4/648 198/601/649 197/675/650
+f 197/675/650 198/601/649 194/454/651
+f 133/411/652 204/20/653 207/650/654
+f 211/463/655 215/605/656 208/79/657
+f 208/79/657 215/605/656 210/354/658
+f 132/176/659 207/650/660 208/79/661
+f 133/411/662 207/650/663 132/176/664
+f 209/633/665 208/79/666 210/354/667
+f 132/176/668 208/79/669 209/633/670
+f 215/605/671 212/287/672 210/354/673
+f 209/633/674 210/354/675 213/76/676
+f 214/293/677 215/605/678 211/463/679
+f 213/76/680 214/293/681 70/262/682
+f 214/293/683 213/76/684 215/605/685
+f 67/105/686 211/463/687 216/300/688
+f 67/105/689 214/293/690 211/463/691
+f 67/105/692 216/300/693 217/152/694
+f 65/615/695 217/152/696 205/335/697
+f 65/615/698 67/105/699 217/152/700
+f 65/615/701 205/335/702 218/346/703
+f 61/704/704 218/346/705 221/99/706
+f 61/704/707 65/615/708 218/346/709
+f 219/12/710 221/99/711 220/64/712
+f 219/12/713 61/704/714 221/99/715
+f 219/12/716 220/64/717 200/4/718
+f 55/405/719 200/4/720 197/675/721
+f 55/405/722 219/12/723 200/4/724
+f 222/546/725 197/675/726 195/403/727
+f 222/546/728 55/405/729 197/675/730
+f 222/546/731 195/403/732 191/43/733
+f 54/87/734 222/546/735 191/43/736
+f 223/611/737 187/705/738 189/36/739
+f 223/611/740 54/87/741 187/705/742
+f 50/489/743 189/36/744 224/706/745
+f 50/489/746 223/611/747 189/36/748
+f 50/489/749 224/706/750 52/322/751
+f 34/38/752 371/183/753 498/289/754
+f 498/289/754 371/183/753 519/40/755
+f 170/515/756 307/103/757 169/149/758
+f 169/149/758 307/103/757 306/109/759
+f 567/297/760 127/634/761 554/247/762
+f 554/247/762 127/634/761 301/508/763
+f 304/337/764 101/157/765 302/3/766
+f 302/3/766 101/157/765 103/552/767
+f 553/302/768 105/341/769 571/134/770
+f 571/134/770 105/341/769 270/583/771
+f 175/662/772 503/42/773 272/135/774
+f 272/135/774 503/42/773 508/321/775
+f 182/174/776 105/284/777 106/265/778
+f 106/265/778 105/284/777 102/125/779
+f 169/149/780 306/109/781 232/336/782
+f 232/336/782 306/109/781 310/200/783
+f 582/480/784 82/707/785 581/5/786
+f 581/5/786 82/707/785 81/285/787
+f 573/543/788 558/641/789 302/3/790
+f 302/3/790 558/641/789 316/343/791
+f 267/229/792 183/587/793 570/665/794
+f 570/665/794 183/587/793 566/491/795
+f 235/648/796 85/554/797 313/155/798
+f 313/155/798 85/554/797 79/689/799
+f 254/115/800 260/81/801 255/7/802
+f 255/7/802 260/81/801 258/311/803
+f 255/7/804 258/311/805 251/698/806
+f 251/698/806 258/311/805 259/394/807
+f 263/82/808 260/81/809 253/158/810
+f 253/158/810 260/81/809 254/115/811
+f 264/83/812 263/82/813 252/208/814
+f 252/208/814 263/82/813 253/158/815
+f 251/698/816 259/394/817 250/161/818
+f 250/161/818 259/394/817 262/193/819
+f 248/171/820 243/504/821 246/574/822
+f 244/421/823 243/504/824 249/468/825
+f 249/468/825 243/504/824 248/171/826
+f 242/395/827 247/722/828 243/504/829
+f 243/504/829 247/722/828 246/574/830
+f 244/421/831 286/528/832 245/469/833
+f 245/469/833 286/528/832 427/8/834
+f 265/70/835 257/283/836 436/485/837
+f 123/253/838 266/186/839 118/483/840
+f 118/483/840 266/186/839 122/34/841
+f 110/199/842 183/710/843 114/53/844
+f 114/53/844 183/710/843 184/75/845
+f 580/455/846 422/472/847 557/194/848
+f 557/194/848 422/472/847 287/561/849
+f 183/587/850 267/229/851 184/588/852
+f 184/588/852 267/229/851 268/589/853
+f 175/239/854 34/38/855 503/344/856
+f 503/344/856 34/38/855 498/289/857
+f 382/590/858 383/277/859 271/586/860
+f 271/586/860 383/277/859 270/583/861
+f 184/588/862 268/589/863 266/591/864
+f 122/230/865 266/591/864 268/589/863
+f 122/230/866 268/589/867 179/231/868
+f 179/231/868 268/589/867 120/628/869
+f 182/585/870 181/16/871 271/586/872
+f 181/16/871 108/232/873 271/586/872
+f 90/233/874 271/586/875 180/112/876
+f 180/112/876 271/586/875 108/232/877
+f 429/126/878 120/628/879 382/590/880
+f 382/590/880 120/628/879 268/589/881
+f 174/617/882 165/358/883 282/359/884
+f 282/359/884 165/358/883 280/486/885
+f 74/618/886 177/345/887 274/654/888
+f 274/654/888 177/345/887 284/399/889
+f 159/413/890 161/666/891 277/151/892
+f 277/151/892 161/666/891 278/616/893
+f 176/332/894 283/175/895 77/21/896
+f 77/21/896 283/175/895 275/127/897
+f 165/358/898 173/17/899 280/486/900
+f 280/486/900 173/17/899 281/333/901
+f 177/345/902 77/21/903 284/399/904
+f 284/399/904 77/21/903 275/127/905
+f 176/332/906 78/608/907 283/175/908
+f 283/175/908 78/608/907 276/664/909
+f 74/618/910 274/654/911 72/655/912
+f 72/655/912 274/654/911 273/656/913
+f 174/617/914 282/359/915 163/312/916
+f 163/312/916 282/359/915 279/487/917
+f 72/655/918 273/656/919 173/17/920
+f 173/17/920 273/656/919 281/333/921
+f 161/666/922 163/312/923 278/616/924
+f 278/616/924 163/312/923 279/487/925
+f 601/137/926 269/325/927 585/492/928
+f 585/492/928 269/325/927 178/326/929
+f 175/662/930 272/135/931 586/493/932
+f 586/493/932 272/135/931 590/663/933
+f 128/356/934 71/243/935 166/52/936
+f 166/52/936 71/243/935 73/703/937
+f 174/206/938 130/708/939 165/107/940
+f 165/107/940 130/708/939 166/52/941
+f 113/202/942 112/555/943 288/447/944
+f 288/447/944 112/555/943 290/702/945
+f 116/604/946 113/202/947 289/619/948
+f 289/619/948 113/202/947 288/447/949
+f 112/555/950 111/264/951 290/702/952
+f 290/702/952 111/264/951 287/561/953
+f 287/561/954 111/264/955 557/194/956
+f 557/194/956 111/264/955 568/347/957
+f 363/313/958 294/636/959 364/278/960
+f 364/278/960 294/636/959 295/614/961
+f 400/169/962 402/55/963 395/163/964
+f 395/163/964 402/55/963 397/57/965
+f 314/419/966 315/128/967 94/723/968
+f 94/723/968 315/128/967 93/612/969
+f 127/634/970 297/521/971 301/508/972
+f 301/508/972 297/521/971 299/645/973
+f 318/676/974 304/337/975 316/343/976
+f 316/343/976 304/337/975 302/3/977
+f 101/157/978 304/337/979 98/606/980
+f 98/606/980 304/337/979 225/129/981
+f 92/226/982 226/327/983 304/337/984
+f 304/337/984 226/327/983 225/129/985
+f 297/521/986 298/431/987 299/645/988
+f 298/431/987 115/569/989 299/645/988
+f 115/569/990 227/476/991 299/645/992
+f 299/645/992 227/476/991 93/548/993
+f 315/236/994 318/676/995 93/548/996
+f 93/548/996 318/676/995 299/645/997
+f 234/640/998 233/696/999 312/204/1000
+f 312/204/1000 233/696/999 311/467/1001
+f 232/336/1002 310/200/1003 88/50/1004
+f 88/50/1004 310/200/1003 305/201/1005
+f 309/376/1006 230/246/1007 591/442/1008
+f 591/442/1008 230/246/1007 583/142/1009
+f 592/434/1010 303/168/1011 581/271/1012
+f 581/271/1012 303/168/1011 231/143/1013
+f 319/147/1014 309/376/1015 593/565/1016
+f 593/565/1016 309/376/1015 591/442/1017
+f 320/23/1018 308/639/1019 348/701/1020
+f 348/701/1020 308/639/1019 345/150/1021
+f 321/164/1022 307/103/1023 320/23/1024
+f 320/23/1024 307/103/1023 308/639/1025
+f 322/571/1026 306/109/1027 321/164/1028
+f 321/164/1028 306/109/1027 307/103/1029
+f 79/689/1030 312/204/1031 321/164/1032
+f 321/164/1032 312/204/1031 322/571/1033
+f 313/155/1034 79/689/1035 320/23/1036
+f 320/23/1036 79/689/1035 321/164/1037
+f 344/1/1038 313/155/1039 348/701/1040
+f 348/701/1040 313/155/1039 320/23/1041
+f 592/434/1042 171/307/1043 593/565/1044
+f 593/565/1044 171/307/1043 319/147/1045
+f 304/337/1046 318/676/1047 92/226/1048
+f 92/226/1048 318/676/1047 315/236/1049
+f 301/508/1050 299/645/1051 316/343/1052
+f 316/343/1052 299/645/1051 318/676/1053
+f 95/77/1054 92/196/1055 314/419/1056
+f 314/419/1056 92/196/1055 315/128/1057
+f 306/109/1058 322/571/1059 310/200/1060
+f 310/200/1060 322/571/1059 305/201/1061
+f 312/204/1062 311/467/1063 322/571/1064
+f 322/571/1064 311/467/1063 305/201/1065
+f 154/557/1066 157/32/1067 155/94/1068
+f 155/94/1068 157/32/1067 389/673/1069
+f 474/256/1070 530/141/1071 475/353/1072
+f 475/353/1072 530/141/1071 531/84/1073
+f 217/152/1074 204/20/1075 205/335/1076
+f 205/335/1076 204/20/1075 203/466/1077
+f 224/706/1078 189/36/1079 186/324/1080
+f 186/324/1080 189/36/1079 190/584/1081
+f 52/322/1082 185/674/1083 186/324/1084
+f 52/322/1085 224/706/1086 185/674/1087
+f 213/76/1088 210/354/1089 212/287/1090
+f 213/76/1091 212/287/1092 215/605/1093
+f 130/708/1094 164/613/1095 131/537/1096
+f 131/537/1096 164/613/1095 129/100/1097
+f 163/215/1098 164/613/1099 174/206/1100
+f 174/206/1100 164/613/1099 130/708/1101
+f 161/375/1102 162/465/1103 163/215/1104
+f 163/215/1104 162/465/1103 164/613/1105
+f 75/430/1106 66/47/1107 76/725/1108
+f 76/725/1108 66/47/1107 69/635/1109
+f 191/43/1110 192/227/1111 187/705/1112
+f 187/705/1112 192/227/1111 188/573/1113
+f 54/87/1114 191/43/1115 187/705/1116
+f 49/308/1117 45/471/1118 51/340/1119
+f 51/340/1119 45/471/1118 44/153/1120
+f 401/575/1121 400/169/1122 396/711/1123
+f 396/711/1123 400/169/1122 395/163/1124
+f 563/368/1125 572/498/1126 363/313/1127
+f 363/313/1127 572/498/1126 294/636/1128
+f 395/163/1129 397/57/1130 333/216/1131
+f 333/216/1131 397/57/1130 330/360/1132
+f 396/711/1133 395/163/1134 329/712/1135
+f 329/712/1135 395/163/1134 333/216/1136
+f 397/57/1137 394/407/1138 330/360/1139
+f 330/360/1139 394/407/1138 332/361/1140
+f 342/334/1141 337/266/1142 335/178/1143
+f 335/178/1143 337/266/1142 327/697/1144
+f 343/631/1145 339/362/1146 336/576/1147
+f 336/576/1147 339/362/1146 328/268/1148
+f 339/362/1149 340/22/1150 328/268/1151
+f 328/268/1151 340/22/1150 323/217/1152
+f 337/266/1153 343/631/1154 327/697/1155
+f 327/697/1155 343/631/1154 336/576/1156
+f 352/373/1157 355/24/1158 343/631/1159
+f 343/631/1159 355/24/1158 339/362/1160
+f 351/58/1161 353/597/1162 342/334/1163
+f 342/334/1163 353/597/1162 337/266/1164
+f 334/510/1165 341/534/1166 335/178/1167
+f 335/178/1167 341/534/1166 342/334/1168
+f 576/116/1169 384/185/1170 574/414/1171
+f 574/414/1171 384/185/1170 332/361/1172
+f 582/480/1173 80/724/1174 588/25/1175
+f 588/25/1175 80/724/1174 33/363/1176
+f 598/108/1177 597/295/1178 324/182/1179
+f 324/182/1179 597/295/1178 338/370/1180
+f 596/61/1181 597/295/1182 56/364/1183
+f 56/364/1183 597/295/1182 57/488/1184
+f 233/303/1185 234/195/1186 87/72/1187
+f 87/72/1187 234/195/1186 86/177/1188
+f 46/377/1189 87/72/1190 45/471/1191
+f 45/471/1191 87/72/1190 44/153/1192
+f 171/307/1193 344/1/1194 319/147/1195
+f 319/147/1195 344/1/1194 348/701/1196
+f 348/701/1197 345/150/1198 319/147/1199
+f 319/147/1199 345/150/1198 309/376/1200
+f 81/73/1201 347/462/1202 171/307/1203
+f 171/307/1203 347/462/1202 344/1/1204
+f 346/198/1205 345/150/1206 229/516/1207
+f 229/516/1207 345/150/1206 308/639/1208
+f 40/562/1209 39/299/1210 419/121/1211
+f 419/121/1211 39/299/1210 41/290/1212
+f 267/229/1213 383/277/1214 268/589/1215
+f 268/589/1215 383/277/1214 382/590/1216
+f 284/399/1217 378/630/1218 274/654/1219
+f 274/654/1219 378/630/1218 349/716/1220
+f 584/684/1221 58/603/1222 599/184/1223
+f 599/184/1223 58/603/1222 60/148/1224
+f 357/391/1225 359/660/1226 351/58/1227
+f 351/58/1227 359/660/1226 353/597/1228
+f 358/622/1229 361/529/1230 352/373/1231
+f 352/373/1231 361/529/1230 355/24/1232
+f 341/534/1233 350/272/1234 342/334/1235
+f 342/334/1235 350/272/1234 351/58/1236
+f 559/56/1237 560/478/1238 334/510/1239
+f 334/510/1239 560/478/1238 341/534/1240
+f 359/660/1241 358/622/1242 353/597/1243
+f 353/597/1243 358/622/1242 352/373/1244
+f 366/420/1245 365/570/1246 359/660/1247
+f 359/660/1247 365/570/1246 358/622/1248
+f 370/540/1249 380/46/1250 89/541/1251
+f 89/541/1251 380/46/1250 379/558/1252
+f 365/570/1253 368/224/1254 358/622/1255
+f 358/622/1255 368/224/1254 361/529/1256
+f 364/278/1257 366/420/1258 357/391/1259
+f 357/391/1259 366/420/1258 359/660/1260
+f 350/272/1261 356/623/1262 351/58/1263
+f 351/58/1263 356/623/1262 357/391/1264
+f 560/478/1265 561/620/1266 341/534/1267
+f 341/534/1267 561/620/1266 350/272/1268
+f 295/614/1269 292/726/1270 364/278/1271
+f 364/278/1271 292/726/1270 366/420/1272
+f 296/560/1273 293/381/1274 365/570/1275
+f 365/570/1275 293/381/1274 368/224/1276
+f 292/726/1277 296/560/1278 366/420/1279
+f 366/420/1279 296/560/1278 365/570/1280
+f 356/623/1281 363/313/1282 357/391/1283
+f 357/391/1283 363/313/1282 364/278/1284
+f 561/620/1285 562/642/1286 350/272/1287
+f 350/272/1287 562/642/1286 356/623/1288
+f 562/642/1289 563/368/1290 356/623/1291
+f 356/623/1291 563/368/1290 363/313/1292
+f 378/630/1293 381/526/1294 279/487/1295
+f 279/487/1295 381/526/1294 278/616/1296
+f 392/328/1297 277/151/1298 418/320/1299
+f 418/320/1299 277/151/1298 413/525/1300
+f 370/540/1301 323/217/1302 380/46/1303
+f 380/46/1303 323/217/1302 237/190/1304
+f 595/323/1305 412/41/1306 601/137/1307
+f 601/137/1307 412/41/1306 269/325/1308
+f 372/542/1309 380/46/1310 326/389/1311
+f 326/389/1311 380/46/1310 237/190/1312
+f 326/389/1313 331/212/1314 372/542/1315
+f 372/542/1315 331/212/1314 377/653/1316
+f 331/212/1317 329/712/1318 377/653/1319
+f 377/653/1319 329/712/1318 375/44/1320
+f 333/216/1321 330/360/1322 385/301/1323
+f 385/301/1323 330/360/1322 376/600/1324
+f 329/712/1325 333/216/1326 375/44/1327
+f 375/44/1327 333/216/1326 385/301/1328
+f 330/360/1329 332/361/1330 376/600/1331
+f 376/600/1331 332/361/1330 384/185/1332
+f 335/178/1333 327/697/1334 387/48/1335
+f 387/48/1335 327/697/1334 373/78/1336
+f 336/576/1337 328/268/1338 388/427/1339
+f 388/427/1339 328/268/1338 374/428/1340
+f 328/268/1341 323/217/1342 374/428/1343
+f 374/428/1343 323/217/1342 370/540/1344
+f 327/697/1345 336/576/1346 373/78/1347
+f 373/78/1347 336/576/1346 388/427/1348
+f 102/125/1349 386/509/1350 106/265/1351
+f 106/265/1351 386/509/1350 387/48/1352
+f 565/610/1353 110/199/1354 576/116/1355
+f 576/116/1355 110/199/1354 384/185/1356
+f 360/556/1357 354/559/1358 594/140/1359
+f 594/140/1359 354/559/1358 596/61/1360
+f 586/209/1361 584/684/1362 175/239/1363
+f 175/239/1363 584/684/1362 34/38/1364
+f 602/387/1365 589/699/1366 369/657/1367
+f 369/657/1367 589/699/1366 124/80/1368
+f 155/94/1369 389/673/1370 158/33/1371
+f 158/33/1371 389/673/1370 156/607/1372
+f 390/26/1373 172/276/1374 156/607/1375
+f 156/607/1375 172/276/1374 158/33/1376
+f 277/151/1377 392/328/1378 159/413/1379
+f 159/413/1379 392/328/1378 391/90/1380
+f 160/74/1381 134/446/1382 391/532/1383
+f 391/532/1383 134/446/1382 159/408/1384
+f 78/609/1385 176/181/1386 58/603/1387
+f 58/603/1387 176/181/1386 64/388/1388
+f 137/637/1389 430/372/1390 136/677/1391
+f 555/530/1392 564/599/1393 102/125/1394
+f 102/125/1394 564/599/1393 386/509/1395
+f 407/500/1396 406/192/1397 401/575/1398
+f 401/575/1398 406/192/1397 400/169/1399
+f 406/192/1400 408/448/1401 400/169/1402
+f 400/169/1402 408/448/1401 402/55/1403
+f 574/414/1404 332/361/1405 577/566/1406
+f 577/566/1406 332/361/1405 394/407/1407
+f 408/448/1408 405/456/1409 402/55/1410
+f 402/55/1410 405/456/1409 399/512/1411
+f 425/269/1412 422/472/1413 408/448/1414
+f 408/448/1414 422/472/1413 405/456/1415
+f 423/577/1416 425/269/1417 406/192/1418
+f 406/192/1418 425/269/1417 408/448/1419
+f 424/433/1420 423/577/1421 407/500/1422
+f 407/500/1422 423/577/1421 406/192/1423
+f 577/566/1424 394/407/1425 578/680/1426
+f 578/680/1426 394/407/1425 399/512/1427
+f 470/329/1428 479/494/1429 526/275/1430
+f 526/275/1430 479/494/1429 535/495/1431
+f 245/469/1432 427/8/1433 403/626/1434
+f 403/626/1434 427/8/1433 414/296/1435
+f 429/126/1436 382/590/1437 90/233/1438
+f 90/233/1438 382/590/1437 271/586/1439
+f 331/212/1440 326/389/1441 411/146/1442
+f 411/146/1442 326/389/1441 410/518/1443
+f 411/146/1444 410/518/1445 409/721/1446
+f 409/721/1446 410/518/1445 416/661/1447
+f 94/723/1448 117/124/1449 286/528/1450
+f 286/528/1450 117/124/1449 417/254/1451
+f 117/124/1452 116/604/1453 417/254/1454
+f 417/254/1454 116/604/1453 289/619/1455
+f 606/27/1456 604/415/1457 398/314/1458
+f 398/314/1458 604/415/1457 393/450/1459
+f 396/711/1460 329/712/1461 411/146/1462
+f 411/146/1462 329/712/1461 331/212/1463
+f 283/175/1464 413/525/1465 275/127/1466
+f 275/127/1466 413/525/1465 381/526/1467
+f 604/415/1468 605/715/1469 393/450/1470
+f 393/450/1470 605/715/1469 325/225/1471
+f 426/671/1472 424/433/1473 415/315/1474
+f 415/315/1474 424/433/1473 407/500/1475
+f 426/671/1476 427/8/1477 417/254/1478
+f 417/254/1478 427/8/1477 286/528/1479
+f 409/721/1480 416/661/1481 415/315/1482
+f 415/315/1482 416/661/1481 414/296/1483
+f 401/575/1484 396/711/1485 409/721/1486
+f 409/721/1486 396/711/1485 411/146/1487
+f 415/315/1488 407/500/1489 409/721/1490
+f 409/721/1490 407/500/1489 401/575/1491
+f 244/421/1492 249/468/1493 286/528/1494
+f 286/528/1494 249/468/1493 250/161/1495
+f 340/22/1496 239/218/1497 323/217/1498
+f 323/217/1498 239/218/1497 237/190/1499
+f 416/661/1500 410/518/1501 236/211/1502
+f 236/211/1502 410/518/1501 240/234/1503
+f 419/121/1504 37/2/1505 40/562/1506
+f 40/562/1506 37/2/1505 53/10/1507
+f 37/2/1508 420/481/1509 53/10/1510
+f 53/10/1510 420/481/1509 36/71/1511
+f 594/140/1512 607/15/1513 360/556/1514
+f 360/556/1514 607/15/1513 367/59/1515
+f 588/25/1516 607/15/1517 420/481/1518
+f 420/481/1518 607/15/1517 36/71/1519
+f 237/190/1520 240/234/1521 326/389/1522
+f 326/389/1522 240/234/1521 410/518/1523
+f 429/179/1524 90/310/1525 379/558/1526
+f 379/558/1526 90/310/1525 89/541/1527
+f 414/296/1528 416/661/1529 403/626/1530
+f 403/626/1530 416/661/1529 236/211/1531
+f 415/315/1532 414/296/1533 426/671/1534
+f 426/671/1534 414/296/1533 427/8/1535
+f 417/254/1536 289/619/1537 426/671/1538
+f 426/671/1538 289/619/1537 424/433/1539
+f 289/619/1540 288/447/1541 424/433/1542
+f 424/433/1542 288/447/1541 423/577/1543
+f 288/447/1544 290/702/1545 423/577/1546
+f 423/577/1546 290/702/1545 425/269/1547
+f 290/702/1548 287/561/1549 425/269/1550
+f 425/269/1550 287/561/1549 422/472/1551
+f 578/680/1552 399/512/1553 579/144/1554
+f 579/144/1554 399/512/1553 405/456/1555
+f 252/208/1556 247/722/1557 291/304/1558
+f 291/304/1558 247/722/1557 242/395/1559
+f 378/630/1560 284/399/1561 381/526/1562
+f 381/526/1562 284/399/1561 275/127/1563
+f 349/716/1564 378/630/1565 282/359/1566
+f 282/359/1566 378/630/1565 279/487/1567
+f 380/46/1568 372/542/1569 379/558/1570
+f 379/558/1570 372/542/1569 119/682/1571
+f 362/294/1572 431/133/1573 428/522/1574
+f 428/522/1574 431/133/1573 238/316/1575
+f 241/119/1576 431/133/1577 432/625/1578
+f 432/625/1578 431/133/1577 362/294/1579
+f 355/24/1580 428/522/1581 339/362/1582
+f 339/362/1582 428/522/1581 340/22/1583
+f 361/529/1584 362/294/1585 355/24/1586
+f 355/24/1586 362/294/1585 428/522/1587
+f 368/224/1588 432/625/1589 361/529/1590
+f 361/529/1590 432/625/1589 362/294/1591
+f 293/381/1592 291/304/1593 368/224/1594
+f 368/224/1594 291/304/1593 432/625/1595
+f 239/218/1596 340/22/1597 238/316/1598
+f 238/316/1598 340/22/1597 428/522/1599
+f 242/395/1600 241/119/1601 291/304/1602
+f 291/304/1602 241/119/1601 432/625/1603
+f 138/473/1604 136/677/1605 430/372/1606
+f 140/113/1607 138/473/1608 430/372/1609
+f 602/387/1610 605/715/1611 430/372/1612
+f 430/372/1612 605/715/1611 140/113/1613
+f 606/27/1614 398/314/1615 603/646/1616
+f 603/646/1616 398/314/1615 404/501/1617
+f 603/646/1618 404/501/1619 608/159/1620
+f 608/159/1620 404/501/1619 421/348/1621
+f 600/410/1622 390/26/1623 608/159/1624
+f 608/159/1624 390/26/1623 434/63/1625
+f 146/444/1626 434/63/1627 156/607/1628
+f 156/607/1628 434/63/1627 390/26/1629
+f 146/444/1630 142/306/1631 434/63/1632
+f 274/654/1633 349/716/1634 273/656/1635
+f 273/656/1635 349/716/1634 281/333/1636
+f 282/359/1637 280/486/1638 349/716/1639
+f 349/716/1639 280/486/1638 281/333/1640
+f 433/60/1641 436/485/1642 256/365/1643
+f 256/365/1643 436/485/1642 257/283/1644
+f 262/193/1645 261/658/1646 435/242/1647
+f 264/83/1648 433/60/1649 256/365/1650
+f 252/208/1651 291/304/1652 264/83/1653
+f 264/83/1653 291/304/1652 433/60/1654
+f 435/242/1655 286/528/1656 262/193/1657
+f 262/193/1657 286/528/1656 250/161/1658
+f 94/723/1659 286/528/1660 435/242/1661
+f 291/304/1662 95/77/1663 433/60/1664
+f 95/77/1665 314/419/1666 433/60/1667
+f 433/60/1667 314/419/1666 436/485/1668
+f 94/723/1669 435/242/1670 314/419/1671
+f 314/419/1671 435/242/1670 436/485/1672
+f 265/70/1673 436/485/1674 261/658/1675
+f 261/658/1675 436/485/1674 435/242/1676
+f 27/578/1677 241/119/1678 26/383/1679
+f 26/383/1679 241/119/1678 242/395/1680
+f 26/383/1681 242/395/1682 25/130/1683
+f 25/130/1683 242/395/1682 243/504/1684
+f 25/130/1685 243/504/1686 24/535/1687
+f 24/535/1687 243/504/1686 244/421/1688
+f 24/535/1689 244/421/1690 23/219/1691
+f 23/219/1691 244/421/1690 245/469/1692
+f 23/219/1693 245/469/1694 22/685/1695
+f 22/685/1695 245/469/1694 403/626/1696
+f 22/685/1697 403/626/1698 32/579/1699
+f 32/579/1699 403/626/1698 236/211/1700
+f 32/579/1701 236/211/1702 28/531/1703
+f 28/531/1703 236/211/1702 240/234/1704
+f 28/531/1705 240/234/1706 31/351/1707
+f 31/351/1707 240/234/1706 237/190/1708
+f 31/351/1709 237/190/1710 29/9/1711
+f 29/9/1711 237/190/1710 239/218/1712
+f 29/9/1713 239/218/1714 30/317/1715
+f 30/317/1715 239/218/1714 238/316/1716
+f 30/317/1717 238/316/1718 21/281/1719
+f 21/281/1719 238/316/1718 431/133/1720
+f 21/281/1721 431/133/1722 27/578/1723
+f 27/578/1723 431/133/1722 241/119/1724
+f 29/9/1725 30/317/1726 445/386/1727
+f 445/386/1727 30/317/1726 446/318/1728
+f 32/579/1729 28/531/1730 448/371/1731
+f 448/371/1731 28/531/1730 444/11/1732
+f 31/351/1733 29/9/1734 447/449/1735
+f 447/449/1735 29/9/1734 445/386/1736
+f 24/535/1737 23/219/1738 440/523/1739
+f 440/523/1739 23/219/1738 439/188/1740
+f 26/383/1741 25/130/1742 442/238/1743
+f 442/238/1743 25/130/1742 441/436/1744
+f 21/281/1745 27/578/1746 437/390/1747
+f 437/390/1747 27/578/1746 443/594/1748
+f 23/219/1749 22/685/1750 439/188/1751
+f 439/188/1751 22/685/1750 438/220/1752
+f 22/685/1753 32/579/1754 438/220/1755
+f 438/220/1755 32/579/1754 448/371/1756
+f 28/531/1757 31/351/1758 444/11/1759
+f 444/11/1759 31/351/1758 447/449/1760
+f 27/578/1761 26/383/1762 443/594/1763
+f 443/594/1763 26/383/1762 442/238/1764
+f 30/317/1765 21/281/1766 446/318/1767
+f 446/318/1767 21/281/1766 437/390/1768
+f 25/130/1769 24/535/1770 441/436/1771
+f 441/436/1771 24/535/1770 440/523/1772
+f 18/396/1773 253/158/1774 19/451/1775
+f 19/451/1775 253/158/1774 254/115/1776
+f 19/451/1777 254/115/1778 20/397/1779
+f 20/397/1779 254/115/1778 255/7/1780
+f 20/397/1781 255/7/1782 16/280/1783
+f 16/280/1783 255/7/1782 251/698/1784
+f 16/280/1785 251/698/1786 15/240/1787
+f 15/240/1787 251/698/1786 250/161/1788
+f 15/240/1789 250/161/1790 14/86/1791
+f 14/86/1791 250/161/1790 249/468/1792
+f 14/86/1793 249/468/1794 13/28/1795
+f 13/28/1795 249/468/1794 248/171/1796
+f 13/28/1797 248/171/1798 11/19/1799
+f 11/19/1799 248/171/1798 246/574/1800
+f 11/19/1801 246/574/1802 12/624/1803
+f 12/624/1803 246/574/1802 247/722/1804
+f 12/624/1805 247/722/1806 17/319/1807
+f 17/319/1807 247/722/1806 252/208/1808
+f 17/319/1809 252/208/1810 18/396/1811
+f 18/396/1811 252/208/1810 253/158/1812
+f 1/274/1813 265/70/1814 5/681/1815
+f 5/681/1815 265/70/1814 261/658/1816
+f 5/681/1817 261/658/1818 4/67/1819
+f 4/67/1819 261/658/1818 262/193/1820
+f 4/67/1821 262/193/1822 7/29/1823
+f 7/29/1823 262/193/1822 259/394/1824
+f 7/29/1825 259/394/1826 8/398/1827
+f 8/398/1827 259/394/1826 258/311/1828
+f 8/398/1829 258/311/1830 6/62/1831
+f 6/62/1831 258/311/1830 260/81/1832
+f 6/62/1833 260/81/1834 3/270/1835
+f 3/270/1835 260/81/1834 263/82/1836
+f 3/270/1837 263/82/1838 2/117/1839
+f 2/117/1839 263/82/1838 264/83/1840
+f 2/117/1841 264/83/1842 10/477/1843
+f 10/477/1843 264/83/1842 256/365/1844
+f 10/477/1845 256/365/1846 9/214/1847
+f 9/214/1847 256/365/1846 257/283/1848
+f 9/214/1849 257/283/1850 1/274/1851
+f 1/274/1851 257/283/1850 265/70/1852
+f 4/67/1853 7/29/1854 452/496/1855
+f 452/496/1855 7/29/1854 455/258/1856
+f 7/29/1857 8/398/1858 455/258/1859
+f 455/258/1859 8/398/1858 456/160/1860
+f 10/477/1861 9/214/1862 458/506/1863
+f 458/506/1863 9/214/1862 457/111/1864
+f 5/681/1865 4/67/1866 453/221/1867
+f 453/221/1867 4/67/1866 452/496/1868
+f 9/214/1869 1/274/1870 457/111/1871
+f 457/111/1871 1/274/1870 449/627/1872
+f 8/398/1873 6/62/1874 456/160/1875
+f 456/160/1875 6/62/1874 454/621/1876
+f 3/270/1877 2/117/1878 451/717/1879
+f 451/717/1879 2/117/1878 450/257/1880
+f 6/62/1881 3/270/1882 454/621/1883
+f 454/621/1883 3/270/1882 451/717/1884
+f 2/117/1885 10/477/1886 450/257/1887
+f 450/257/1887 10/477/1886 458/506/1888
+f 1/274/1889 5/681/1890 449/627/1891
+f 449/627/1891 5/681/1890 453/221/1892
+f 14/86/1893 13/28/1894 462/282/1895
+f 462/282/1895 13/28/1894 461/145/1896
+f 13/28/1897 11/19/1898 461/145/1899
+f 461/145/1899 11/19/1898 459/533/1900
+f 15/240/1901 14/86/1902 463/457/1903
+f 463/457/1903 14/86/1902 462/282/1904
+f 17/319/1905 18/396/1906 465/366/1907
+f 465/366/1907 18/396/1906 466/384/1908
+f 11/19/1909 12/624/1910 459/533/1911
+f 459/533/1911 12/624/1910 460/470/1912
+f 12/624/1913 17/319/1914 460/470/1915
+f 460/470/1915 17/319/1914 465/366/1916
+f 20/397/1917 16/280/1918 468/255/1919
+f 468/255/1919 16/280/1918 464/564/1920
+f 19/451/1921 20/397/1922 467/416/1923
+f 467/416/1923 20/397/1922 468/255/1924
+f 18/396/1925 19/451/1926 466/384/1927
+f 466/384/1927 19/451/1926 467/416/1928
+f 16/280/1929 15/240/1930 464/564/1931
+f 464/564/1931 15/240/1930 463/457/1932
+f 354/559/1933 515/507/1934 338/370/1935
+f 338/370/1935 515/507/1934 514/131/1936
+f 579/144/1937 405/456/1938 580/455/1939
+f 580/455/1939 405/456/1938 422/472/1940
+f 33/363/1941 497/170/1942 367/59/1943
+f 367/59/1943 497/170/1942 517/14/1944
+f 325/225/1945 369/657/1946 513/352/1947
+f 513/352/1947 369/657/1946 518/713/1948
+f 272/135/1949 508/321/1950 412/41/1951
+f 412/41/1951 508/321/1950 523/138/1952
+f 482/581/1953 538/385/1954 483/595/1955
+f 483/595/1955 538/385/1954 539/18/1956
+f 178/326/1957 269/325/1958 504/162/1959
+f 504/162/1959 269/325/1958 507/497/1960
+f 231/143/1961 303/168/1962 506/235/1963
+f 506/235/1963 303/168/1962 510/85/1964
+f 228/458/1965 505/502/1966 300/118/1967
+f 300/118/1967 505/502/1966 509/459/1968
+f 470/453/1969 526/432/1970 473/719/1971
+f 473/719/1971 526/432/1970 529/259/1972
+f 472/139/1973 528/667/1974 478/241/1975
+f 478/241/1975 528/667/1974 534/210/1976
+f 269/325/1977 412/41/1978 507/497/1979
+f 507/497/1979 412/41/1978 523/138/1980
+f 571/134/1981 575/668/1982 479/494/1983
+f 479/494/1983 575/668/1982 487/669/1984
+f 125/37/1985 421/348/1986 501/114/1987
+f 501/114/1987 421/348/1986 524/567/1988
+f 572/498/1989 481/400/1990 556/68/1991
+f 556/68/1991 481/400/1990 477/401/1992
+f 480/349/1993 536/350/1994 496/568/1995
+f 496/568/1995 536/350/1994 552/474/1996
+f 338/370/1997 514/131/1998 324/182/1999
+f 324/182/1999 514/131/1998 512/572/2000
+f 475/353/2001 531/84/2002 480/349/2003
+f 480/349/2003 531/84/2002 536/350/2004
+f 556/68/2005 477/401/2006 569/580/2007
+f 569/580/2007 477/401/2006 476/69/2008
+f 126/305/2009 125/37/2010 502/45/2011
+f 502/45/2011 125/37/2010 501/114/2012
+f 476/69/2013 477/401/2014 532/207/2015
+f 532/207/2015 477/401/2014 533/273/2016
+f 553/647/2017 470/453/2018 555/530/2019
+f 555/530/2019 470/453/2018 473/719/2020
+f 481/400/2021 490/66/2022 537/551/2023
+f 537/551/2023 490/66/2022 546/659/2024
+f 568/347/2025 567/191/2026 475/353/2027
+f 475/353/2027 567/191/2026 474/256/2028
+f 565/610/2029 469/406/2030 566/592/2031
+f 566/592/2031 469/406/2030 472/288/2032
+f 404/501/2033 398/314/2034 522/166/2035
+f 522/166/2035 398/314/2034 521/65/2036
+f 575/668/2037 570/665/2038 487/669/2039
+f 487/669/2039 570/665/2038 478/241/2040
+f 477/401/2041 481/400/2042 533/273/2043
+f 533/273/2043 481/400/2042 537/551/2044
+f 539/18/2045 511/279/2046 527/632/2047
+f 527/632/2047 511/279/2046 509/459/2048
+f 543/670/2049 534/210/2050 523/138/2051
+f 523/138/2051 534/210/2050 507/497/2052
+f 500/503/2053 504/440/2054 525/672/2055
+f 525/672/2055 504/440/2054 528/93/2056
+f 531/84/2057 530/141/2058 502/45/2059
+f 502/45/2059 530/141/2058 505/417/2060
+f 526/432/2061 503/344/2062 529/259/2063
+f 529/259/2063 503/344/2062 498/289/2064
+f 533/273/2065 499/686/2066 532/207/2067
+f 532/207/2067 499/686/2066 506/679/2068
+f 537/551/2069 497/170/2070 533/273/2071
+f 533/273/2071 497/170/2070 499/686/2072
+f 535/495/2073 543/670/2074 508/321/2075
+f 508/321/2075 543/670/2074 523/138/2076
+f 522/166/2077 551/369/2078 524/567/2079
+f 524/567/2079 551/369/2078 552/474/2080
+f 521/65/2081 550/13/2082 522/166/2083
+f 522/166/2083 550/13/2082 551/369/2084
+f 520/452/2085 549/700/2086 521/65/2087
+f 521/65/2087 549/700/2086 550/13/2088
+f 513/352/2089 541/187/2090 520/452/2091
+f 520/452/2091 541/187/2090 549/700/2092
+f 498/289/2093 519/40/2094 529/259/2095
+f 529/259/2095 519/40/2094 548/683/2096
+f 500/503/2097 525/672/2098 518/713/2099
+f 518/713/2099 525/672/2098 547/222/2100
+f 516/342/2101 517/14/2102 545/549/2103
+f 545/549/2103 517/14/2102 546/659/2104
+f 515/507/2105 516/342/2106 544/132/2107
+f 544/132/2107 516/342/2106 545/549/2108
+f 514/131/2109 515/507/2110 542/718/2111
+f 542/718/2111 515/507/2110 544/132/2112
+f 512/572/2113 514/131/2114 540/550/2115
+f 540/550/2115 514/131/2114 542/718/2116
+f 518/713/2117 547/222/2118 513/352/2119
+f 513/352/2119 547/222/2118 541/187/2120
+f 517/14/2121 497/170/2122 546/659/2123
+f 546/659/2123 497/170/2122 537/551/2124
+f 501/114/2125 536/350/2126 502/45/2127
+f 502/45/2127 536/350/2126 531/84/2128
+f 524/567/2129 552/474/2130 501/114/2131
+f 501/114/2131 552/474/2130 536/350/2132
+f 534/210/2133 528/667/2134 507/497/2135
+f 507/497/2135 528/667/2134 504/162/2136
+f 510/85/2137 511/279/2138 538/385/2139
+f 538/385/2139 511/279/2138 539/18/2140
+f 503/42/2141 526/275/2142 508/321/2143
+f 508/321/2143 526/275/2142 535/495/2144
+f 505/502/2145 530/380/2146 509/459/2147
+f 509/459/2147 530/380/2146 527/632/2148
+f 506/235/2149 510/85/2150 532/30/2151
+f 532/30/2151 510/85/2150 538/385/2152
+f 519/40/2153 512/572/2154 548/683/2155
+f 548/683/2155 512/572/2154 540/550/2156
+f 393/450/2157 325/225/2158 520/452/2159
+f 520/452/2159 325/225/2158 513/352/2160
+f 228/643/2161 126/305/2162 505/417/2163
+f 505/417/2163 126/305/2162 502/45/2164
+f 478/241/2165 534/210/2166 487/669/2167
+f 487/669/2167 534/210/2166 543/670/2168
+f 231/223/2169 506/679/2170 80/724/2171
+f 80/724/2171 506/679/2170 499/686/2172
+f 476/720/2173 532/30/2174 482/581/2175
+f 482/581/2175 532/30/2174 538/385/2176
+f 471/102/2177 483/595/2178 527/632/2179
+f 527/632/2179 483/595/2178 539/18/2180
+f 496/568/2181 552/474/2182 495/441/2183
+f 495/441/2183 552/474/2182 551/369/2184
+f 484/517/2185 492/524/2186 540/550/2187
+f 540/550/2187 492/524/2186 548/683/2188
+f 479/494/2189 487/669/2190 535/495/2191
+f 535/495/2191 487/669/2190 543/670/2192
+f 303/168/2193 317/213/2194 510/85/2195
+f 510/85/2195 317/213/2194 511/279/2196
+f 124/80/2197 500/503/2198 369/657/2199
+f 369/657/2199 500/503/2198 518/713/2200
+f 495/441/2201 551/369/2202 494/678/2203
+f 494/678/2203 551/369/2202 550/13/2204
+f 494/678/2205 550/13/2206 493/237/2207
+f 493/237/2207 550/13/2206 549/700/2208
+f 300/118/2209 509/459/2210 317/213/2211
+f 317/213/2211 509/459/2210 511/279/2212
+f 324/182/2213 512/572/2214 371/183/2215
+f 371/183/2215 512/572/2214 519/40/2216
+f 178/687/2217 504/440/2218 124/80/2219
+f 124/80/2219 504/440/2218 500/503/2220
+f 398/314/2221 393/450/2222 521/65/2223
+f 521/65/2223 393/450/2222 520/452/2224
+f 493/237/2225 549/700/2226 485/475/2227
+f 485/475/2227 549/700/2226 541/187/2228
+f 473/719/2229 529/259/2230 492/524/2231
+f 492/524/2231 529/259/2230 548/683/2232
+f 469/406/2233 491/593/2234 525/672/2235
+f 525/672/2235 491/593/2234 547/222/2236
+f 490/66/2237 489/437/2238 546/659/2239
+f 546/659/2239 489/437/2238 545/549/2240
+f 367/59/2241 517/14/2242 360/556/2243
+f 360/556/2243 517/14/2242 516/342/2244
+f 489/437/2245 488/714/2246 545/549/2247
+f 545/549/2247 488/714/2246 544/132/2248
+f 421/348/2249 404/501/2250 524/567/2251
+f 524/567/2251 404/501/2250 522/166/2252
+f 488/714/2253 486/563/2254 544/132/2255
+f 544/132/2255 486/563/2254 542/718/2256
+f 486/563/2257 484/517/2258 542/718/2259
+f 542/718/2259 484/517/2258 540/550/2260
+f 80/724/2261 499/686/2262 33/363/2263
+f 33/363/2263 499/686/2262 497/170/2264
+f 485/475/2265 541/187/2266 491/593/2267
+f 491/593/2267 541/187/2266 547/222/2268
+f 554/247/2269 558/641/2270 471/102/2271
+f 471/102/2271 558/641/2270 483/595/2272
+f 316/343/2273 558/641/2274 301/508/2275
+f 301/508/2275 558/641/2274 554/247/2276
+f 383/277/2277 267/229/2278 575/668/2279
+f 575/668/2279 267/229/2278 570/665/2280
+f 565/610/2281 566/592/2282 110/199/2283
+f 110/199/2283 566/592/2282 183/710/2284
+f 127/122/2285 567/191/2286 111/264/2287
+f 111/264/2287 567/191/2286 568/347/2288
+f 105/284/2289 553/647/2290 102/125/2291
+f 102/125/2291 553/647/2290 555/530/2292
+f 556/68/2293 569/580/2294 104/464/2295
+f 104/464/2295 569/580/2294 103/695/2296
+f 294/636/2297 572/498/2298 104/464/2299
+f 104/464/2299 572/498/2298 556/68/2300
+f 270/583/2301 383/277/2302 571/134/2303
+f 571/134/2303 383/277/2302 575/668/2304
+f 495/441/2305 579/144/2306 496/568/2307
+f 496/568/2307 579/144/2306 580/455/2308
+f 494/678/2309 578/680/2310 495/441/2311
+f 495/441/2311 578/680/2310 579/144/2312
+f 493/237/2313 577/566/2314 494/678/2315
+f 494/678/2315 577/566/2314 578/680/2316
+f 485/475/2317 574/414/2318 493/237/2319
+f 493/237/2319 574/414/2318 577/566/2320
+f 473/719/2321 492/524/2322 555/530/2323
+f 555/530/2323 492/524/2322 564/599/2324
+f 469/406/2325 565/610/2326 491/593/2327
+f 491/593/2327 565/610/2326 576/116/2328
+f 489/437/2329 490/66/2330 562/642/2331
+f 562/642/2331 490/66/2330 563/368/2332
+f 488/714/2333 489/437/2334 561/620/2335
+f 561/620/2335 489/437/2334 562/642/2336
+f 486/563/2337 488/714/2338 560/478/2339
+f 560/478/2339 488/714/2338 561/620/2340
+f 484/517/2341 486/563/2342 559/56/2343
+f 559/56/2343 486/563/2342 560/478/2344
+f 491/593/2345 576/116/2346 485/475/2347
+f 485/475/2347 576/116/2346 574/414/2348
+f 490/66/2349 481/400/2350 563/368/2351
+f 563/368/2351 481/400/2350 572/498/2352
+f 480/349/2353 557/194/2354 475/353/2355
+f 475/353/2355 557/194/2354 568/347/2356
+f 496/568/2357 580/455/2358 480/349/2359
+f 480/349/2359 580/455/2358 557/194/2360
+f 570/665/2361 566/491/2362 478/241/2363
+f 478/241/2363 566/491/2362 472/139/2364
+f 482/581/2365 483/595/2366 573/543/2367
+f 573/543/2367 483/595/2366 558/641/2368
+f 470/329/2369 553/302/2370 479/494/2371
+f 479/494/2371 553/302/2370 571/134/2372
+f 474/251/2373 567/297/2374 471/102/2375
+f 471/102/2375 567/297/2374 554/247/2376
+f 476/720/2377 482/581/2378 569/252/2379
+f 569/252/2379 482/581/2378 573/543/2380
+f 492/524/2381 484/517/2382 564/599/2383
+f 564/599/2383 484/517/2382 559/56/2384
+f 125/37/2385 600/410/2386 421/348/2387
+f 421/348/2387 600/410/2386 608/159/2388
+f 143/91/2389 603/646/2390 434/63/2391
+f 434/63/2391 603/646/2390 608/159/2392
+f 141/418/2393 606/27/2394 143/91/2395
+f 143/91/2395 606/27/2394 603/646/2396
+f 369/657/2397 325/225/2398 602/387/2399
+f 602/387/2399 325/225/2398 605/715/2400
+f 33/363/2401 367/59/2402 588/25/2403
+f 588/25/2403 367/59/2402 607/15/2404
+f 35/519/2405 36/71/2406 594/140/2407
+f 594/140/2407 36/71/2406 607/15/2408
+f 139/402/2409 140/113/2410 604/415/2411
+f 604/415/2411 140/113/2410 605/715/2412
+f 141/418/2413 139/402/2414 606/27/2415
+f 606/27/2415 139/402/2414 604/415/2416
+f 430/372/2417 160/74/2418 602/387/2419
+f 602/387/2419 160/74/2418 589/699/2420
+f 58/603/2421 584/684/2422 78/609/2423
+f 78/609/2423 584/684/2422 586/209/2424
+f 594/140/2425 596/61/2426 35/519/2427
+f 35/519/2427 596/61/2426 56/364/2428
+f 418/320/2429 595/323/2430 392/328/2431
+f 392/328/2431 595/323/2430 601/137/2432
+f 34/38/2433 584/684/2434 371/183/2435
+f 371/183/2435 584/684/2434 599/184/2436
+f 354/559/2437 338/370/2438 596/61/2439
+f 596/61/2439 338/370/2438 597/295/2440
+f 59/435/2441 57/488/2442 598/108/2443
+f 598/108/2443 57/488/2442 597/295/2444
+f 582/480/2445 588/25/2446 82/707/2447
+f 82/707/2447 588/25/2446 420/481/2448
+f 303/168/2449 592/434/2450 317/213/2451
+f 317/213/2451 592/434/2450 593/565/2452
+f 593/565/2453 591/442/2454 317/213/2455
+f 317/213/2455 591/442/2454 300/118/2456
+f 171/307/2457 592/434/2458 81/73/2459
+f 81/73/2459 592/434/2458 581/271/2460
+f 591/442/2461 583/142/2462 300/118/2463
+f 300/118/2463 583/142/2462 228/458/2464
+f 78/608/2465 586/493/2466 276/664/2467
+f 276/664/2467 586/493/2466 590/663/2468
+f 392/328/2469 601/137/2470 391/90/2471
+f 391/90/2471 601/137/2470 585/492/2472
+f 80/724/2473 582/480/2474 231/223/2475
+f 231/223/2475 582/480/2474 581/5/2476
+f 230/330/2477 172/276/2478 583/267/2479
+f 583/267/2479 172/276/2478 587/404/2480
+f 418/320/2481 276/664/2482 595/323/2483
+f 595/323/2483 276/664/2482 590/663/2484
+f 390/26/2485 600/410/2486 172/276/2487
+f 172/276/2487 600/410/2486 587/404/2488
+f 598/108/2489 599/184/2490 59/435/2491
+f 59/435/2491 599/184/2490 60/148/2492
+f 391/532/2493 585/367/2494 160/74/2495
+f 160/74/2495 585/367/2494 589/699/2496
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_button.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_button.obj
new file mode 100644
index 0000000..2057c27
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_button.obj
@@ -0,0 +1,595 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v -0.003544 0.012417 0.078987
+v -0.003544 0.013694 0.078987
+v -0.003544 0.012417 0.078281
+v -0.003544 0.013694 0.078281
+v 0.003544 0.012417 0.078987
+v 0.003544 0.013694 0.078987
+v 0.003544 0.012417 0.078281
+v 0.003544 0.013694 0.078281
+v -0.003685 0.013694 0.078769
+v -0.003685 0.013694 0.078498
+v -0.003685 0.012417 0.078498
+v -0.003685 0.012417 0.078769
+v 0.003685 0.013694 0.078498
+v 0.003685 0.013694 0.078769
+v 0.003685 0.012417 0.078769
+v 0.003685 0.012417 0.078498
+v 0.005601 0.013400 0.078650
+v 0.004531 0.013400 0.081942
+v 0.004531 0.013400 0.075358
+v -0.001731 0.013400 0.073324
+v -0.005601 0.013400 0.078650
+v -0.001731 0.013400 0.083977
+v 0.001731 0.013400 0.083977
+v -0.004531 0.013400 0.081942
+v 0.001731 0.013400 0.073324
+v -0.004531 0.013400 0.075358
+v 0.005463 0.013400 0.078650
+v 0.004420 0.013400 0.081861
+v 0.004420 0.013400 0.075439
+v -0.001688 0.013400 0.073454
+v -0.005463 0.013400 0.078650
+v -0.001688 0.013400 0.083846
+v 0.001688 0.013400 0.083846
+v -0.004420 0.013400 0.081861
+v 0.001688 0.013400 0.073454
+v -0.004420 0.013400 0.075439
+v 0.000000 0.013400 0.078650
+v -0.003544 0.013694 0.078987
+v 0.005601 0.008414 0.078650
+v 0.004531 0.008414 0.081942
+v 0.004531 0.008414 0.075358
+v -0.001731 0.008414 0.073324
+v -0.005601 0.008414 0.078650
+v -0.001731 0.008414 0.083977
+v 0.001731 0.008414 0.083977
+v -0.004531 0.008414 0.081942
+v 0.001731 0.008414 0.073324
+v -0.004531 0.008414 0.075358
+v 0.007115 0.008414 0.078650
+v 0.005756 0.008414 0.082832
+v 0.005756 0.008414 0.074468
+v -0.002199 0.008414 0.071883
+v -0.007115 0.008414 0.078650
+v -0.002199 0.008414 0.085417
+v 0.002199 0.008414 0.085417
+v -0.005756 0.008414 0.082832
+v 0.002199 0.008414 0.071883
+v -0.005756 0.008414 0.074468
+v -0.001731 0.013280 0.083977
+v 0.001731 0.013280 0.083977
+v 0.005601 0.013280 0.078650
+v 0.004531 0.013280 0.075358
+v 0.001731 0.013280 0.073324
+v -0.004531 0.013280 0.081942
+v -0.005601 0.013280 0.078650
+v -0.004531 0.013280 0.075358
+v -0.001731 0.013280 0.073324
+v 0.004531 0.013280 0.081942
+v -0.001731 0.008498 0.083977
+v 0.005601 0.008498 0.078650
+v -0.004531 0.008498 0.081942
+v -0.005601 0.008498 0.078650
+v -0.004531 0.008498 0.075358
+v -0.001731 0.008498 0.073324
+v 0.004531 0.008498 0.081942
+v 0.001731 0.008498 0.083977
+v 0.004531 0.008498 0.075358
+v 0.001731 0.008498 0.073324
+vt 0.842894 0.803524
+vt 0.835648 0.808965
+vt 0.842327 0.831331
+vt 0.887089 0.817309
+vt 0.918325 0.883860
+vt 0.914475 0.893532
+vt 0.851925 0.804110
+vt 0.831948 0.817360
+vt 0.834920 0.826127
+vt 0.835239 0.825914
+vt 0.861947 0.818080
+vt 0.858905 0.826714
+vt 0.796511 0.818262
+vt 0.834940 0.856335
+vt 0.851468 0.832257
+vt 0.879150 0.794052
+vt 0.814609 0.794688
+vt 0.879631 0.793683
+vt 0.859802 0.855418
+vt 0.918325 0.877630
+vt 0.952893 0.877630
+vt 0.917637 0.875247
+vt 0.860005 0.855991
+vt 0.953582 0.876570
+vt 0.954157 0.893532
+vt 0.914475 0.887302
+vt 0.915739 0.887302
+vt 0.831492 0.866397
+vt 0.859621 0.809104
+vt 0.814110 0.794343
+vt 0.955480 0.893532
+vt 0.918325 0.874188
+vt 0.952893 0.874188
+vt 0.953582 0.875247
+vt 0.953582 0.886243
+vt 0.917637 0.886242
+vt 0.953582 0.884920
+vt 0.917637 0.884919
+vt 0.897742 0.817179
+vt 0.842083 0.832031
+vt 0.835347 0.808727
+vt 0.842998 0.803894
+vt 0.842194 0.831691
+vt 0.880142 0.841098
+vt 0.835113 0.855751
+vt 0.952893 0.883860
+vt 0.917637 0.876570
+vt 0.830731 0.769044
+vt 0.852058 0.803750
+vt 0.835058 0.808516
+vt 0.852169 0.803410
+vt 0.887696 0.817293
+vt 0.806054 0.847763
+vt 0.814622 0.841758
+vt 0.888895 0.846984
+vt 0.862304 0.818081
+vt 0.805357 0.788458
+vt 0.815102 0.841390
+vt 0.956743 0.887302
+vt 0.956743 0.893532
+vt 0.851358 0.831917
+vt 0.862760 0.769044
+vt 0.954157 0.887302
+vt 0.915739 0.893532
+vt 0.859312 0.779106
+vt 0.806556 0.818148
+vt 0.834631 0.826337
+vt 0.859194 0.826925
+vt 0.859332 0.809314
+vt 0.859013 0.809527
+vt 0.832305 0.817361
+vt 0.832688 0.817376
+vt 0.847126 0.817721
+vt 0.859139 0.779690
+vt 0.834450 0.780024
+vt 0.834248 0.779451
+vt 0.807163 0.818133
+vt 0.863522 0.866397
+vt 0.952893 0.893532
+vt 0.917062 0.887302
+vt 0.918325 0.887302
+vt 0.918325 0.893532
+vt 0.917062 0.893532
+vt 0.952893 0.887302
+vt 0.955480 0.887302
+vt 0.879643 0.840753
+vt 0.888198 0.787678
+vt 0.861564 0.818065
+vt 0.842784 0.803184
+vt 0.851254 0.831547
+vt 0.858604 0.826476
+vn -0.838651 0.000004 -0.544669
+vn -0.838651 0.000004 -0.544669
+vn -0.838651 0.000004 -0.544669
+vn -0.838651 0.000004 -0.544669
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.838651 0.000004 0.544669
+vn 0.838651 0.000004 0.544669
+vn 0.838651 0.000004 0.544669
+vn 0.838651 0.000004 0.544669
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.838651 -0.000004 -0.544669
+vn 0.838651 -0.000004 -0.544669
+vn 0.838651 -0.000004 -0.544669
+vn 0.838651 -0.000004 -0.544669
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn -0.838651 -0.000004 0.544669
+vn -0.838651 -0.000004 0.544669
+vn -0.838651 -0.000004 0.544669
+vn -0.838651 -0.000004 0.544669
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619120 0.643718 0.449797
+vn -0.236462 0.643710 0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236462 0.643710 -0.727821
+vn -0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.765269 0.643711 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn -0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 0.727821
+vn 0.236462 0.643710 0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236462 0.643710 0.727821
+vn 0.619120 0.643718 0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.619120 0.643718 0.449797
+vn 0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.765269 0.643711 0.000000
+vn -0.619120 0.643718 0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+vn 0.191936 0.783706 0.590733
+vn -0.191936 0.783706 0.590733
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn -0.191936 0.783706 0.590733
+vn -0.502506 0.783714 0.365076
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn -0.621130 0.783707 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 0.621130 0.783707 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 0.502506 0.783714 0.365076
+vn 0.191936 0.783706 0.590733
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.621130 0.783707 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn 0.236462 0.643710 -0.727821
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 0.619120 0.643718 0.449797
+vn 0.236462 0.643710 0.727821
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 0.765269 0.643711 0.000000
+vn 0.619120 0.643718 0.449797
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn -0.619120 0.643718 -0.449797
+vn -0.236462 0.643710 -0.727821
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.765269 0.643711 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.619120 0.643718 0.449797
+vn -0.765269 0.643711 0.000000
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.236462 0.643710 0.727821
+vn -0.619120 0.643718 0.449797
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn 0.236462 0.643710 -0.727821
+vn 0.619120 0.643718 -0.449797
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.619120 0.643718 -0.449797
+vn 0.765269 0.643711 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.236462 0.643710 0.727821
+vn -0.236462 0.643710 0.727821
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+f 10/80/1 4/81/2 11/83/3
+f 11/83/3 4/81/2 3/82/4
+f 4/81/5 8/84/6 3/82/7
+f 3/82/7 8/84/6 7/79/8
+f 14/85/9 6/59/10 15/31/11
+f 15/31/11 6/59/10 5/60/12
+f 6/46/13 2/5/14 5/21/15
+f 5/21/15 2/5/14 1/20/16
+f 11/22/17 3/32/18 16/34/19
+f 16/34/19 3/32/18 7/33/20
+f 13/35/21 8/84/22 10/36/23
+f 10/36/23 8/84/22 4/81/24
+f 6/46/25 14/37/26 2/5/27
+f 2/5/27 14/37/26 9/38/28
+f 14/37/29 13/35/30 9/38/31
+f 9/38/31 13/35/30 10/36/32
+f 1/20/33 12/47/34 5/21/35
+f 5/21/35 12/47/34 15/24/36
+f 12/47/37 11/22/38 15/24/39
+f 15/24/39 11/22/38 16/34/40
+f 8/84/41 13/63/42 7/79/43
+f 7/79/43 13/63/42 16/25/44
+f 13/63/45 14/85/46 16/25/47
+f 16/25/47 14/85/46 15/31/48
+f 2/26/49 9/27/50 1/6/51
+f 1/6/51 9/27/50 12/64/52
+f 9/27/53 10/80/54 12/64/55
+f 12/64/55 10/80/54 11/83/56
+f 19/12/57 25/61/58 29/91/59
+f 29/91/59 25/61/58 35/90/60
+f 24/41/61 22/1/62 34/2/63
+f 34/2/63 22/1/62 32/42/64
+f 25/61/65 20/43/66 35/90/67
+f 35/90/67 20/43/66 30/3/68
+f 17/11/69 19/12/70 27/88/71
+f 27/88/71 19/12/70 29/91/72
+f 20/43/73 26/9/74 30/3/75
+f 30/3/75 26/9/74 36/10/76
+f 22/1/77 23/49/78 32/42/79
+f 32/42/79 23/49/78 33/7/80
+f 23/49/81 18/69/82 33/7/83
+f 33/7/83 18/69/82 28/70/84
+f 18/69/85 17/11/86 28/70/87
+f 28/70/87 17/11/86 27/88/88
+f 21/71/89 24/41/90 31/72/91
+f 31/72/91 24/41/90 34/2/92
+f 26/9/93 21/71/94 36/10/95
+f 36/10/95 21/71/94 31/72/96
+f 33/7/97 28/70/98 37/73/99
+f 35/90/100 30/3/101 37/73/102
+f 32/42/103 33/7/104 37/73/105
+f 27/88/106 29/91/107 37/73/108
+f 29/91/109 35/90/110 37/73/111
+f 34/2/112 32/42/113 37/73/114
+f 31/72/115 34/2/116 37/73/117
+f 36/10/118 31/72/119 37/73/120
+f 30/3/121 36/10/122 37/73/123
+f 28/70/124 27/88/125 37/73/126
+f 76/74/127 69/75/128 45/65/129
+f 45/65/129 69/75/128 44/76/130
+f 77/86/131 70/4/132 41/44/133
+f 41/44/133 70/4/132 39/52/134
+f 78/19/135 77/86/136 47/23/137
+f 47/23/137 77/86/136 41/44/138
+f 69/75/139 71/17/140 44/76/141
+f 44/76/141 71/17/140 46/30/142
+f 71/17/143 72/77/144 46/30/145
+f 46/30/145 72/77/144 43/66/146
+f 72/77/147 73/58/148 43/66/149
+f 43/66/149 73/58/148 48/54/150
+f 73/58/151 74/45/152 48/54/153
+f 48/54/153 74/45/152 42/14/154
+f 70/4/155 75/16/156 39/52/157
+f 39/52/157 75/16/156 40/18/158
+f 75/16/159 76/74/160 40/18/161
+f 40/18/161 76/74/160 45/65/162
+f 74/45/163 78/19/164 42/14/165
+f 42/14/165 78/19/164 47/23/166
+f 39/52/167 40/18/168 49/39/169
+f 49/39/169 40/18/168 50/87/170
+f 40/18/171 45/65/172 50/87/173
+f 50/87/173 45/65/172 55/62/174
+f 42/14/175 47/23/176 52/28/177
+f 52/28/177 47/23/176 57/78/178
+f 45/65/179 44/76/180 55/62/181
+f 55/62/181 44/76/180 54/48/182
+f 41/44/183 39/52/184 51/55/185
+f 51/55/185 39/52/184 49/39/186
+f 47/23/187 41/44/188 57/78/189
+f 57/78/189 41/44/188 51/55/190
+f 44/76/191 46/30/192 54/48/193
+f 54/48/193 46/30/192 56/57/194
+f 46/30/195 43/66/196 56/57/197
+f 56/57/197 43/66/196 53/13/198
+f 43/66/199 48/54/200 53/13/201
+f 53/13/201 48/54/200 58/53/202
+f 48/54/203 42/14/204 58/53/205
+f 58/53/205 42/14/204 52/28/206
+f 20/43/207 25/61/208 67/40/209
+f 67/40/209 25/61/208 63/15/210
+f 18/69/211 23/49/212 68/29/213
+f 68/29/213 23/49/212 60/51/214
+f 17/11/215 18/69/216 61/56/217
+f 61/56/217 18/69/216 68/29/218
+f 26/9/219 20/43/220 66/67/221
+f 66/67/221 20/43/220 67/40/222
+f 21/71/223 26/9/224 65/8/225
+f 65/8/225 26/9/224 66/67/226
+f 24/41/227 21/71/228 64/50/229
+f 64/50/229 21/71/228 65/8/230
+f 22/1/231 24/41/232 59/89/233
+f 59/89/233 24/41/232 64/50/234
+f 25/61/235 19/12/236 63/15/237
+f 63/15/237 19/12/236 62/68/238
+f 19/12/239 17/11/240 62/68/241
+f 62/68/241 17/11/240 61/56/242
+f 23/49/243 22/1/244 60/51/245
+f 60/51/245 22/1/244 59/89/246
+f 67/40/247 63/15/248 74/45/249
+f 74/45/249 63/15/248 78/19/250
+f 68/29/251 60/51/252 75/16/253
+f 75/16/253 60/51/252 76/74/254
+f 61/56/255 68/29/256 70/4/257
+f 70/4/257 68/29/256 75/16/258
+f 66/67/259 67/40/260 73/58/261
+f 73/58/261 67/40/260 74/45/262
+f 65/8/263 66/67/264 72/77/265
+f 72/77/265 66/67/264 73/58/266
+f 64/50/267 65/8/268 71/17/269
+f 71/17/269 65/8/268 72/77/270
+f 59/89/271 64/50/272 69/75/273
+f 69/75/273 64/50/272 71/17/274
+f 63/15/275 62/68/276 78/19/277
+f 78/19/277 62/68/276 77/86/278
+f 62/68/279 61/56/280 77/86/281
+f 77/86/281 61/56/280 70/4/282
+f 60/51/283 59/89/284 76/74/285
+f 76/74/285 59/89/284 69/75/286
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_button2.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_button2.obj
new file mode 100644
index 0000000..2f3bd1e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_button2.obj
@@ -0,0 +1,794 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.001731 0.013400 0.090823
+v -0.005601 0.013400 0.096150
+v 0.001731 0.013400 0.101477
+v -0.004531 0.013400 0.099442
+v 0.004531 0.013400 0.099442
+v 0.005601 0.013400 0.096150
+v -0.001731 0.013400 0.101477
+v -0.001731 0.013400 0.090823
+v 0.004531 0.013400 0.092858
+v -0.004531 0.013400 0.092858
+v 0.001690 0.013400 0.090950
+v -0.005467 0.013400 0.096150
+v 0.001690 0.013400 0.101350
+v -0.004423 0.013400 0.099364
+v 0.004423 0.013400 0.099364
+v 0.005467 0.013400 0.096150
+v -0.001690 0.013400 0.101350
+v -0.001690 0.013400 0.090950
+v 0.004423 0.013400 0.092936
+v -0.004423 0.013400 0.092936
+v 0.000000 0.013400 0.096150
+v 0.001731 0.008414 0.090823
+v -0.005601 0.008414 0.096150
+v 0.001731 0.008414 0.101477
+v -0.004531 0.008414 0.099442
+v 0.004531 0.008414 0.099442
+v 0.005601 0.008414 0.096150
+v -0.001731 0.008414 0.101477
+v -0.001731 0.008414 0.090823
+v 0.004531 0.008414 0.092858
+v -0.004531 0.008414 0.092858
+v 0.002212 0.008414 0.089341
+v -0.007159 0.008414 0.096150
+v 0.002212 0.008414 0.102959
+v -0.005792 0.008414 0.100358
+v 0.005792 0.008414 0.100358
+v 0.007159 0.008414 0.096150
+v -0.002212 0.008414 0.102959
+v -0.002212 0.008414 0.089341
+v 0.005792 0.008414 0.091942
+v -0.005792 0.008414 0.091942
+v -0.001731 0.013209 0.101477
+v 0.001731 0.013209 0.101477
+v -0.004531 0.013209 0.099442
+v -0.005601 0.013209 0.096150
+v -0.004531 0.013209 0.092858
+v 0.004531 0.013209 0.092858
+v 0.005601 0.013209 0.096150
+v 0.004531 0.013209 0.099442
+v -0.001731 0.013209 0.090823
+v 0.001731 0.013209 0.090823
+v 0.001731 0.008535 0.101477
+v -0.005601 0.008535 0.096150
+v 0.004531 0.008535 0.099442
+v 0.001731 0.008535 0.090823
+v -0.001731 0.008535 0.101477
+v -0.004531 0.008535 0.099442
+v -0.004531 0.008535 0.092858
+v 0.004531 0.008535 0.092858
+v 0.005601 0.008535 0.096150
+v -0.001731 0.008535 0.090823
+v 0.001108 0.013694 0.092741
+v -0.003585 0.013694 0.096150
+v 0.001108 0.013694 0.099560
+v -0.002900 0.013694 0.098257
+v 0.002900 0.013694 0.098257
+v 0.003585 0.013694 0.096150
+v -0.001108 0.013694 0.099560
+v -0.001108 0.013694 0.092741
+v 0.002900 0.013694 0.094043
+v -0.002900 0.013694 0.094043
+v 0.000955 0.013694 0.093211
+v -0.003090 0.013694 0.096150
+v 0.000955 0.013694 0.099090
+v -0.002500 0.013694 0.097967
+v 0.002500 0.013694 0.097967
+v 0.003090 0.013694 0.096150
+v -0.000955 0.013694 0.099090
+v -0.000955 0.013694 0.093211
+v 0.002500 0.013694 0.094334
+v -0.002500 0.013694 0.094334
+v 0.001108 0.012417 0.092741
+v -0.003585 0.012417 0.096150
+v 0.001108 0.012417 0.099560
+v -0.002900 0.012417 0.098257
+v 0.002900 0.012417 0.098257
+v 0.003585 0.012417 0.096150
+v -0.001108 0.012417 0.099560
+v -0.001108 0.012417 0.092741
+v 0.002900 0.012417 0.094043
+v -0.002900 0.012417 0.094043
+v 0.000955 0.012417 0.093211
+v -0.003090 0.012417 0.096150
+v 0.000955 0.012417 0.099090
+v -0.002500 0.012417 0.097967
+v 0.002500 0.012417 0.097967
+v 0.003090 0.012417 0.096150
+v -0.000955 0.012417 0.099090
+v -0.000955 0.012417 0.093211
+v 0.002500 0.012417 0.094334
+v -0.002500 0.012417 0.094334
+vt 0.932232 0.937151
+vt 0.945647 0.941314
+vt 0.825593 0.923815
+vt 0.788802 0.909064
+vt 0.935598 0.930484
+vt 0.909311 0.932196
+vt 0.907009 0.929014
+vt 0.922229 0.927744
+vt 0.923371 0.959651
+vt 0.926270 0.980012
+vt 0.923981 0.964843
+vt 0.907571 0.956084
+vt 0.910064 0.946637
+vt 0.932187 0.961332
+vt 0.913791 0.964746
+vt 0.931115 0.938999
+vt 0.906593 0.976987
+vt 0.814667 0.938118
+vt 0.907941 0.945842
+vt 0.817792 0.929265
+vt 0.924220 0.967204
+vt 0.899768 0.959058
+vt 0.834177 0.952426
+vt 0.841570 0.947355
+vt 0.817336 0.900595
+vt 0.797907 0.915598
+vt 0.841862 0.947586
+vt 0.797489 0.962416
+vt 0.863053 0.962016
+vt 0.842840 0.976907
+vt 0.845500 0.938935
+vt 0.900358 0.943670
+vt 0.896353 0.942255
+vt 0.950018 0.956861
+vt 0.914874 0.948129
+vt 0.914648 0.953699
+vt 0.937578 0.926986
+vt 0.925672 0.975465
+vt 0.931991 0.947345
+vt 0.825215 0.952174
+vt 0.842325 0.930150
+vt 0.842016 0.930356
+vt 0.834916 0.924929
+vt 0.844928 0.938934
+vt 0.844556 0.938918
+vt 0.830059 0.938555
+vt 0.790654 0.938738
+vt 0.933703 0.963036
+vt 0.936911 0.954039
+vt 0.813826 0.890060
+vt 0.797202 0.915105
+vt 0.861850 0.915045
+vt 0.817977 0.976306
+vt 0.798179 0.961897
+vt 0.789797 0.938756
+vt 0.842554 0.976087
+vt 0.841940 0.901082
+vt 0.845641 0.890060
+vt 0.950018 0.939483
+vt 0.932035 0.952826
+vt 0.835221 0.924030
+vt 0.909829 0.955422
+vt 0.895358 0.960445
+vt 0.915261 0.939767
+vt 0.923140 0.936945
+vt 0.936417 0.945527
+vt 0.938885 0.954567
+vt 0.909516 0.972375
+vt 0.817813 0.946906
+vt 0.818124 0.946700
+vt 0.917914 0.958206
+vt 0.923549 0.941808
+vt 0.928754 0.943094
+vt 0.846431 0.987663
+vt 0.870407 0.938130
+vt 0.817616 0.901404
+vt 0.842181 0.900257
+vt 0.862534 0.914520
+vt 0.869546 0.938152
+vt 0.834277 0.952786
+vt 0.862338 0.961524
+vt 0.923050 0.934996
+vt 0.915227 0.962597
+vt 0.946468 0.956010
+vt 0.928741 0.957476
+vt 0.922006 0.924371
+vt 0.939379 0.968571
+vt 0.918286 0.943618
+vt 0.942241 0.971610
+vt 0.814114 0.987501
+vt 0.788568 0.968560
+vt 0.779102 0.938526
+vt 0.870794 0.908363
+vt 0.881150 0.937707
+vt 0.834453 0.953332
+vt 0.824904 0.953069
+vt 0.842788 0.929814
+vt 0.842325 0.947924
+vt 0.817349 0.947240
+vt 0.835045 0.924577
+vt 0.825883 0.924738
+vt 0.815235 0.938118
+vt 0.818251 0.929599
+vt 0.818529 0.929812
+vt 0.815603 0.938133
+vt 0.825771 0.924361
+vt 0.825084 0.952524
+vt 0.938637 0.944674
+vt 0.914016 0.937979
+vt 0.872150 0.968124
+vt 0.817725 0.977137
+vn -0.765269 0.643711 0.000000
+vn -0.619128 0.643727 0.449773
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619128 0.643727 0.449773
+vn -0.236496 0.643720 0.727801
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn -0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236496 0.643720 0.727801
+vn 0.619128 0.643727 0.449773
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236462 0.643710 -0.727821
+vn -0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.619128 0.643727 0.449773
+vn 0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236496 0.643720 0.727801
+vn 0.236496 0.643720 0.727801
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.765269 0.643711 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.191935 0.783732 0.590698
+vn -0.191935 0.783732 0.590698
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn -0.191935 0.783732 0.590698
+vn -0.502529 0.783702 0.365070
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn 0.502529 0.783702 0.365070
+vn 0.191935 0.783732 0.590698
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn -0.502529 0.783702 0.365070
+vn -0.621130 0.783707 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.621130 0.783707 0.000000
+vn 0.502529 0.783702 0.365070
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.191935 0.783732 0.590698
+vn -0.191935 0.783732 0.590698
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191935 0.783732 0.590698
+vn -0.502529 0.783702 0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502529 0.783702 0.365070
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502529 0.783702 0.365070
+vn 0.191935 0.783732 0.590698
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.502529 0.783702 0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn 0.236462 0.643710 -0.727821
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 0.765269 0.643711 0.000000
+vn 0.619128 0.643727 0.449773
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.619120 0.643718 -0.449797
+vn 0.765269 0.643711 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -0.236462 0.643710 -0.727821
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.619128 0.643727 0.449773
+vn -0.765269 0.643711 0.000000
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn 0.619128 0.643727 0.449773
+vn 0.236496 0.643720 0.727801
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn -0.236496 0.643720 0.727801
+vn -0.619128 0.643727 0.449773
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn 0.236496 0.643720 0.727801
+vn -0.236496 0.643720 0.727801
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.236462 0.643710 -0.727821
+vn 0.619120 0.643718 -0.449797
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn -0.765269 0.643711 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.587858 0.000000 -0.808964
+vn 0.587858 0.000000 -0.808964
+vn 0.587858 0.000000 -0.808964
+vn 0.587858 0.000000 -0.808964
+vn 0.951057 -0.000000 0.309014
+vn 0.951057 -0.000000 0.309014
+vn 0.951057 -0.000000 0.309014
+vn 0.951057 -0.000000 0.309014
+vn -0.587858 0.000000 -0.808964
+vn -0.587858 0.000000 -0.808964
+vn -0.587858 0.000000 -0.808964
+vn -0.587858 0.000000 -0.808964
+vn 0.951058 0.000000 -0.309013
+vn 0.951058 0.000000 -0.309013
+vn 0.951058 0.000000 -0.309013
+vn 0.951058 0.000000 -0.309013
+vn -0.587785 0.000000 -0.809017
+vn -0.587785 0.000000 -0.809017
+vn -0.587785 0.000000 -0.809017
+vn -0.587785 0.000000 -0.809017
+vn 0.951057 0.000000 -0.309014
+vn 0.951057 0.000000 -0.309014
+vn 0.951057 0.000000 -0.309014
+vn 0.951057 0.000000 -0.309014
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn -0.951057 0.000000 -0.309014
+vn -0.951057 0.000000 -0.309014
+vn -0.951057 0.000000 -0.309014
+vn -0.951057 0.000000 -0.309014
+vn 0.587785 0.000000 -0.809017
+vn 0.587785 0.000000 -0.809017
+vn 0.587785 0.000000 -0.809017
+vn 0.587785 0.000000 -0.809017
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 -0.000000 0.309012
+vn -0.951058 -0.000000 0.309012
+vn -0.951058 -0.000000 0.309012
+vn -0.951058 -0.000000 0.309012
+vn 0.587785 -0.000000 0.809018
+vn 0.587785 -0.000000 0.809018
+vn 0.587785 -0.000000 0.809018
+vn 0.587785 -0.000000 0.809018
+vn -0.587857 -0.000000 0.808965
+vn -0.587857 -0.000000 0.808965
+vn -0.587857 -0.000000 0.808965
+vn -0.587857 -0.000000 0.808965
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.587857 -0.000000 0.808965
+vn 0.587857 -0.000000 0.808965
+vn 0.587857 -0.000000 0.808965
+vn 0.587857 -0.000000 0.808965
+vn -0.587785 -0.000000 0.809018
+vn -0.587785 -0.000000 0.809018
+vn -0.587785 -0.000000 0.809018
+vn -0.587785 -0.000000 0.809018
+vn 0.951058 -0.000000 0.309012
+vn 0.951058 -0.000000 0.309012
+vn 0.951058 -0.000000 0.309012
+vn 0.951058 -0.000000 0.309012
+vn -0.951057 -0.000000 0.309014
+vn -0.951057 -0.000000 0.309014
+vn -0.951057 -0.000000 0.309014
+vn -0.951057 -0.000000 0.309014
+f 2/102/1 4/103/2 12/105/3
+f 12/105/3 4/103/2 14/104/4
+f 4/103/5 7/106/6 14/104/7
+f 14/104/7 7/106/6 17/101/8
+f 8/107/9 10/69/10 18/40/11
+f 18/40/11 10/69/10 20/70/12
+f 9/27/13 1/80/14 19/24/15
+f 19/24/15 1/80/14 11/23/16
+f 3/100/17 5/41/18 13/43/19
+f 13/43/19 5/41/18 15/42/20
+f 1/80/21 8/107/22 11/23/23
+f 11/23/23 8/107/22 18/40/24
+f 5/41/25 6/44/26 15/42/27
+f 15/42/27 6/44/26 16/45/28
+f 7/106/29 3/100/30 17/101/31
+f 17/101/31 3/100/30 13/43/32
+f 10/69/33 2/102/34 20/70/35
+f 20/70/35 2/102/34 12/105/36
+f 6/44/37 9/27/38 16/45/39
+f 16/45/39 9/27/38 19/24/40
+f 12/105/41 14/104/42 21/46/43
+f 18/40/44 20/70/45 21/46/46
+f 14/104/47 17/101/48 21/46/49
+f 13/43/50 15/42/51 21/46/52
+f 15/42/53 16/45/54 21/46/55
+f 17/101/56 13/43/57 21/46/58
+f 16/45/59 19/24/60 21/46/61
+f 19/24/62 11/23/63 21/46/64
+f 11/23/65 18/40/66 21/46/67
+f 20/70/68 12/105/69 21/46/70
+f 53/47/71 58/54/72 23/55/73
+f 23/55/73 58/54/72 31/28/74
+f 55/56/75 59/81/76 22/30/77
+f 22/30/77 59/81/76 30/29/78
+f 52/57/79 56/76/80 24/77/81
+f 24/77/81 56/76/80 28/25/82
+f 56/76/83 57/26/84 28/25/85
+f 28/25/85 57/26/84 25/51/86
+f 54/52/87 52/57/88 26/78/89
+f 26/78/89 52/57/88 24/77/90
+f 57/26/91 53/47/92 25/51/93
+f 25/51/93 53/47/92 23/55/94
+f 58/54/95 61/53/96 31/28/97
+f 31/28/97 61/53/96 29/111/98
+f 59/81/99 60/79/100 30/29/101
+f 30/29/101 60/79/100 27/75/102
+f 60/79/103 54/52/104 27/75/105
+f 27/75/105 54/52/104 26/78/106
+f 61/53/107 55/56/108 29/111/109
+f 29/111/109 55/56/108 22/30/110
+f 22/30/111 30/29/112 32/74/113
+f 32/74/113 30/29/112 40/110/114
+f 24/77/115 28/25/116 34/58/117
+f 34/58/117 28/25/116 38/50/118
+f 28/25/119 25/51/120 38/50/121
+f 38/50/121 25/51/120 35/4/122
+f 31/28/123 29/111/124 41/91/125
+f 41/91/125 29/111/124 39/90/126
+f 25/51/127 23/55/128 35/4/129
+f 35/4/129 23/55/128 33/92/130
+f 26/78/131 24/77/132 36/93/133
+f 36/93/133 24/77/132 34/58/134
+f 29/111/135 22/30/136 39/90/137
+f 39/90/137 22/30/136 32/74/138
+f 27/75/139 26/78/140 37/94/141
+f 37/94/141 26/78/140 36/93/142
+f 30/29/143 27/75/144 40/110/145
+f 40/110/145 27/75/144 37/94/146
+f 23/55/147 31/28/148 33/92/149
+f 33/92/149 31/28/148 41/91/150
+f 8/107/151 1/80/152 50/96/153
+f 50/96/153 1/80/152 51/95/154
+f 6/44/155 5/41/156 48/31/157
+f 48/31/157 5/41/156 49/97/158
+f 9/27/159 6/44/160 47/98/161
+f 47/98/161 6/44/160 48/31/162
+f 10/69/163 8/107/164 46/99/165
+f 46/99/165 8/107/164 50/96/166
+f 4/103/167 2/102/168 44/20/169
+f 44/20/169 2/102/168 45/18/170
+f 5/41/171 3/100/172 49/97/173
+f 49/97/173 3/100/172 43/61/174
+f 7/106/175 4/103/176 42/3/177
+f 42/3/177 4/103/176 44/20/178
+f 3/100/179 7/106/180 43/61/181
+f 43/61/181 7/106/180 42/3/182
+f 1/80/183 9/27/184 51/95/185
+f 51/95/185 9/27/184 47/98/186
+f 2/102/187 10/69/188 45/18/189
+f 45/18/189 10/69/188 46/99/190
+f 50/96/191 51/95/192 61/53/193
+f 61/53/193 51/95/192 55/56/194
+f 48/31/195 49/97/196 60/79/197
+f 60/79/197 49/97/196 54/52/198
+f 47/98/199 48/31/200 59/81/201
+f 59/81/201 48/31/200 60/79/202
+f 46/99/203 50/96/204 58/54/205
+f 58/54/205 50/96/204 61/53/206
+f 44/20/207 45/18/208 57/26/209
+f 57/26/209 45/18/208 53/47/210
+f 49/97/211 43/61/212 54/52/213
+f 54/52/213 43/61/212 52/57/214
+f 42/3/215 44/20/216 56/76/217
+f 56/76/217 44/20/216 57/26/218
+f 43/61/219 42/3/220 52/57/221
+f 52/57/221 42/3/220 56/76/222
+f 51/95/223 47/98/224 55/56/225
+f 55/56/225 47/98/224 59/81/226
+f 45/18/227 46/99/228 53/47/229
+f 53/47/229 46/99/228 58/54/230
+f 62/15/231 69/12/232 72/83/233
+f 72/83/233 69/12/232 79/62/234
+f 64/108/235 74/66/236 68/1/237
+f 68/1/237 74/66/236 78/16/238
+f 63/109/239 65/82/240 73/64/241
+f 73/64/241 65/82/240 75/65/242
+f 63/109/243 73/64/244 71/19/245
+f 71/19/245 73/64/244 81/13/246
+f 62/15/247 72/83/248 70/21/249
+f 70/21/249 72/83/248 80/11/250
+f 66/67/251 67/48/252 76/49/253
+f 76/49/253 67/48/252 77/14/254
+f 65/82/255 68/1/256 75/65/257
+f 75/65/257 68/1/256 78/16/258
+f 69/12/259 71/19/260 79/62/261
+f 79/62/261 71/19/260 81/13/262
+f 67/48/263 70/21/264 77/14/265
+f 77/14/265 70/21/264 80/11/266
+f 64/108/267 66/67/268 74/66/269
+f 74/66/269 66/67/268 76/49/270
+f 82/68/271 92/17/272 89/22/273
+f 89/22/273 92/17/272 99/63/274
+f 84/2/275 88/5/276 94/59/277
+f 94/59/277 88/5/276 98/37/278
+f 83/6/279 93/7/280 85/8/281
+f 85/8/281 93/7/280 95/86/282
+f 83/6/283 91/32/284 93/7/285
+f 93/7/285 91/32/284 101/33/286
+f 82/68/287 90/38/288 92/17/289
+f 92/17/289 90/38/288 100/10/290
+f 86/84/291 96/34/292 87/87/293
+f 87/87/293 96/34/292 97/89/294
+f 85/8/295 95/86/296 88/5/297
+f 88/5/297 95/86/296 98/37/298
+f 89/22/299 99/63/300 91/32/301
+f 91/32/301 99/63/300 101/33/302
+f 87/87/303 97/89/304 90/38/305
+f 90/38/305 97/89/304 100/10/306
+f 84/2/307 94/59/308 86/84/309
+f 86/84/309 94/59/308 96/34/310
+f 75/65/311 78/16/312 95/72/313
+f 95/72/313 78/16/312 98/73/314
+f 67/48/315 66/67/316 87/87/317
+f 87/87/317 66/67/316 86/84/318
+f 74/66/319 76/49/320 94/39/321
+f 94/39/321 76/49/320 96/60/322
+f 70/21/323 67/48/324 90/38/325
+f 90/38/325 67/48/324 87/87/326
+f 71/19/327 69/12/328 91/32/329
+f 91/32/329 69/12/328 89/22/330
+f 73/64/331 75/65/332 93/88/333
+f 93/88/333 75/65/332 95/72/334
+f 64/108/335 68/1/336 84/2/337
+f 84/2/337 68/1/336 88/5/338
+f 69/12/339 62/15/340 89/22/341
+f 89/22/341 62/15/340 82/68/342
+f 76/49/343 77/14/344 96/60/345
+f 96/60/345 77/14/344 97/85/346
+f 62/15/347 70/21/348 82/68/349
+f 82/68/349 70/21/348 90/38/350
+f 63/109/351 71/19/352 83/6/353
+f 83/6/353 71/19/352 91/32/354
+f 77/14/355 80/11/356 97/85/357
+f 97/85/357 80/11/356 100/9/358
+f 79/62/359 81/13/360 99/36/361
+f 99/36/361 81/13/360 101/35/362
+f 68/1/363 65/82/364 88/5/365
+f 88/5/365 65/82/364 85/8/366
+f 78/16/367 74/66/368 98/73/369
+f 98/73/369 74/66/368 94/39/370
+f 72/83/371 79/62/372 92/71/373
+f 92/71/373 79/62/372 99/36/374
+f 66/67/375 64/108/376 86/84/377
+f 86/84/377 64/108/376 84/2/378
+f 80/11/379 72/83/380 100/9/381
+f 100/9/381 72/83/380 92/71/382
+f 81/13/383 73/64/384 101/35/385
+f 101/35/385 73/64/384 93/88/386
+f 65/82/387 63/109/388 85/8/389
+f 85/8/389 63/109/388 83/6/390
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_joystick.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_joystick.obj
new file mode 100644
index 0000000..c4daae3
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_joystick.obj
@@ -0,0 +1,556 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.013683 0.008414 0.043000
+v 0.013683 0.013400 0.058800
+v 0.013683 0.008414 0.058800
+v 0.013683 0.013400 0.043000
+v -0.013683 0.013400 0.043000
+v -0.015800 0.013400 0.050900
+v 0.007900 0.013400 0.064583
+v 0.015800 0.013400 0.050900
+v -0.013683 0.013400 0.058800
+v -0.007900 0.013400 0.064583
+v -0.000000 0.013400 0.066700
+v 0.007900 0.013400 0.037217
+v 0.000000 0.013400 0.035100
+v -0.007900 0.013400 0.037217
+v 0.013247 0.013400 0.058548
+v 0.013247 0.013400 0.043252
+v -0.013247 0.013400 0.043252
+v -0.015297 0.013400 0.050900
+v 0.007648 0.013400 0.064148
+v 0.015297 0.013400 0.050900
+v -0.013247 0.013400 0.058548
+v -0.007648 0.013400 0.064148
+v -0.000000 0.013400 0.066197
+v 0.007648 0.013400 0.037652
+v 0.000000 0.013400 0.035603
+v -0.007648 0.013400 0.037652
+v 0.000000 0.013400 0.050900
+v -0.013683 0.008414 0.043000
+v -0.015800 0.008414 0.050900
+v 0.007900 0.008414 0.064583
+v 0.015800 0.008414 0.050900
+v -0.013683 0.008414 0.058800
+v -0.007900 0.008414 0.064583
+v -0.000000 0.008414 0.066700
+v 0.007900 0.008414 0.037217
+v 0.000000 0.008414 0.035100
+v -0.007900 0.008414 0.037217
+v 0.015123 0.008414 0.042169
+v 0.015123 0.008414 0.059631
+v -0.015123 0.008414 0.042169
+v -0.017462 0.008414 0.050900
+v 0.008731 0.008414 0.066023
+v 0.017462 0.008414 0.050900
+v -0.015123 0.008414 0.059631
+v -0.008731 0.008414 0.066023
+v -0.000000 0.008414 0.068362
+v 0.008731 0.008414 0.035777
+v 0.000000 0.008414 0.033438
+v -0.008731 0.008414 0.035777
+v -0.007900 0.013082 0.037217
+v 0.000000 0.013082 0.035100
+v 0.007900 0.013082 0.037217
+v -0.000000 0.013082 0.066700
+v -0.007900 0.013082 0.064583
+v -0.013683 0.013082 0.058800
+v 0.013683 0.013082 0.043000
+v 0.015800 0.013082 0.050900
+v 0.013683 0.013082 0.058800
+v 0.007900 0.013082 0.064583
+v -0.015800 0.013082 0.050900
+v -0.013683 0.013082 0.043000
+v -0.007900 0.008785 0.064583
+v 0.015800 0.008785 0.050900
+v 0.007900 0.008785 0.064583
+v -0.013683 0.008785 0.043000
+v -0.007900 0.008785 0.037217
+v 0.000000 0.008785 0.035100
+v 0.007900 0.008785 0.037217
+v -0.000000 0.008785 0.066700
+v -0.013683 0.008785 0.058800
+v 0.013683 0.008785 0.043000
+v 0.013683 0.008785 0.058800
+v -0.015800 0.008785 0.050900
+vt 0.598611 0.792613
+vt 0.581181 0.842714
+vt 0.572095 0.840361
+vt 0.583337 0.843249
+vt 0.583900 0.891691
+vt 0.740650 0.932332
+vt 0.764677 0.890275
+vt 0.764114 0.841833
+vt 0.623873 0.817102
+vt 0.649036 0.776482
+vt 0.607278 0.849202
+vt 0.609406 0.849762
+vt 0.609695 0.884296
+vt 0.626984 0.914208
+vt 0.624971 0.818153
+vt 0.626666 0.819833
+vt 0.740622 0.848600
+vt 0.740736 0.884322
+vt 0.738608 0.883762
+vt 0.738319 0.849228
+vt 0.742081 0.848175
+vt 0.605933 0.885349
+vt 0.698978 0.957041
+vt 0.724141 0.916422
+vt 0.650543 0.957262
+vt 0.691947 0.798514
+vt 0.742213 0.884683
+vt 0.697472 0.776262
+vt 0.657036 0.931225
+vt 0.691585 0.931208
+vt 0.625435 0.915771
+vt 0.747828 0.791704
+vt 0.766832 0.890810
+vt 0.766241 0.841221
+vt 0.775920 0.838541
+vt 0.775919 0.893164
+vt 0.741246 0.798397
+vt 0.607392 0.884924
+vt 0.739706 0.799996
+vt 0.723631 0.816655
+vt 0.655425 0.798733
+vt 0.650009 0.959409
+vt 0.647491 0.969131
+vt 0.648421 0.774349
+vt 0.700524 0.764393
+vt 0.674007 0.866762
+vt 0.699593 0.959175
+vt 0.742243 0.933868
+vt 0.749403 0.940910
+vt 0.702098 0.968221
+vt 0.581773 0.892303
+vt 0.606768 0.935127
+vt 0.600187 0.941819
+vt 0.572095 0.894983
+vt 0.698005 0.774114
+vt 0.607364 0.801191
+vt 0.692589 0.934791
+vt 0.656067 0.935009
+vt 0.608309 0.933528
+vt 0.605801 0.848841
+vt 0.624384 0.916869
+vt 0.692164 0.933331
+vt 0.656428 0.933533
+vt 0.655850 0.800192
+vt 0.691586 0.799991
+vt 0.690978 0.802299
+vt 0.656429 0.802316
+vt 0.723043 0.915371
+vt 0.721348 0.913691
+vt 0.722579 0.817753
+vt 0.721030 0.819316
+vt 0.645916 0.765302
+vt 0.605771 0.799655
+vn 0.552274 0.770272 0.318864
+vn 0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 0.770266 0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.378558 0.653230 -0.655732
+vn -0.655715 0.653243 -0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.655715 0.653243 -0.378566
+vn 0.378558 0.653230 -0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.378558 0.653230 -0.655732
+vn 0.000000 0.653236 -0.757154
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.653236 -0.757154
+vn -0.378558 0.653230 -0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.655715 0.653243 -0.378566
+vn -0.757167 0.653221 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.378558 0.653230 0.655732
+vn 0.655715 0.653243 0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.655715 0.653243 0.378566
+vn -0.378558 0.653230 0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.757167 0.653221 0.000000
+vn 0.655715 0.653243 -0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.653236 0.757154
+vn 0.378558 0.653230 0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.378558 0.653230 0.655732
+vn 0.000000 0.653236 0.757154
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.655715 0.653243 0.378566
+vn 0.757167 0.653221 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.757167 0.653221 0.000000
+vn -0.655715 0.653243 0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.552274 0.770272 -0.318864
+vn 0.637723 0.770266 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 0.637723
+vn -0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 -0.552274
+vn 0.552274 0.770272 -0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 0.552274
+vn -0.552274 0.770272 0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.552274 0.770272 0.318864
+vn -0.637723 0.770266 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.552274 0.770272 -0.318864
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.637723 0.770266 0.000000
+vn 0.552274 0.770272 0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 -0.637723
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.637723 0.770266 0.000000
+vn -0.552274 0.770272 -0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+vn -0.552274 0.770272 -0.318864
+vn -0.318864 0.770272 -0.552274
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn 0.637723 0.770266 0.000000
+vn 0.552274 0.770272 0.318864
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 0.770266 0.637723
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn 0.000000 0.770266 -0.637723
+vn 0.318864 0.770272 -0.552274
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.318864 0.770272 -0.552274
+vn 0.552274 0.770272 -0.318864
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn -0.318864 0.770272 0.552274
+vn -0.552274 0.770272 0.318864
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 0.770266 0.637723
+vn -0.318864 0.770272 0.552274
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn -0.552274 0.770272 0.318864
+vn -0.637723 0.770266 0.000000
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn 0.552274 0.770272 -0.318864
+vn 0.637723 0.770266 0.000000
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.552274 0.770272 0.318864
+vn 0.318864 0.770272 0.552274
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn -0.637723 0.770266 0.000000
+vn -0.552274 0.770272 -0.318864
+vn -0.757167 0.653221 0.000000
+vn -0.655715 0.653243 -0.378566
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn 0.655715 0.653243 0.378566
+vn 0.378558 0.653230 0.655732
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.655715 0.653243 -0.378566
+vn 0.757167 0.653221 0.000000
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn -0.655715 0.653243 0.378566
+vn -0.757167 0.653221 0.000000
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn 0.000000 0.653236 0.757154
+vn -0.378558 0.653230 0.655732
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn -0.378558 0.653230 0.655732
+vn -0.655715 0.653243 0.378566
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn 0.378558 0.653230 -0.655732
+vn 0.655715 0.653243 -0.378566
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.000000 0.653236 -0.757154
+vn 0.378558 0.653230 -0.655732
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn -0.378558 0.653230 -0.655732
+vn 0.000000 0.653236 -0.757154
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.378558 0.653230 0.655732
+vn 0.000000 0.653236 0.757154
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.757167 0.653221 0.000000
+vn 0.655715 0.653243 0.378566
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn -0.655715 0.653243 -0.378566
+vn -0.378558 0.653230 -0.655732
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+f 3/33/1 30/34/2 39/36/3
+f 39/36/3 30/34/2 42/35/4
+f 30/34/5 34/37/6 42/35/7
+f 42/35/7 34/37/6 46/32/8
+f 14/38/9 5/11/10 26/13/11
+f 26/13/11 5/11/10 17/12/12
+f 4/62/13 12/63/14 16/30/15
+f 16/30/15 12/63/14 24/29/16
+f 12/63/17 13/31/18 24/29/19
+f 24/29/19 13/31/18 25/14/20
+f 13/31/21 14/38/22 25/14/23
+f 25/14/23 14/38/22 26/13/24
+f 5/11/25 6/15/26 17/12/27
+f 17/12/27 6/15/26 18/16/28
+f 7/17/29 2/18/30 19/20/31
+f 19/20/31 2/18/30 15/19/32
+f 9/64/33 10/65/34 21/67/35
+f 21/67/35 10/65/34 22/66/36
+f 8/68/37 4/62/38 20/69/39
+f 20/69/39 4/62/38 16/30/40
+f 11/70/41 7/17/42 23/71/43
+f 23/71/43 7/17/42 19/20/44
+f 10/65/45 11/70/46 22/66/47
+f 22/66/47 11/70/46 23/71/48
+f 2/18/49 8/68/50 15/19/51
+f 15/19/51 8/68/50 20/69/52
+f 6/15/53 9/64/54 18/16/55
+f 18/16/55 9/64/54 21/67/56
+f 20/69/57 16/30/58 27/46/59
+f 19/20/60 15/19/61 27/46/62
+f 16/30/63 24/29/64 27/46/65
+f 22/66/66 23/71/67 27/46/68
+f 21/67/69 22/66/70 27/46/71
+f 18/16/72 21/67/73 27/46/74
+f 23/71/75 19/20/76 27/46/77
+f 26/13/78 17/12/79 27/46/80
+f 24/29/81 25/14/82 27/46/83
+f 15/19/84 20/69/85 27/46/86
+f 17/12/87 18/16/88 27/46/89
+f 25/14/90 26/13/91 27/46/92
+f 1/47/93 31/48/94 38/50/95
+f 38/50/95 31/48/94 43/49/96
+f 37/51/97 36/52/98 49/54/99
+f 49/54/99 36/52/98 48/53/100
+f 34/37/101 33/55/102 46/32/103
+f 46/32/103 33/55/102 45/45/104
+f 35/42/105 1/47/106 47/43/107
+f 47/43/107 1/47/106 38/50/108
+f 33/55/109 32/44/110 45/45/111
+f 45/45/111 32/44/110 44/72/112
+f 32/44/113 29/73/114 44/72/115
+f 44/72/115 29/73/114 41/1/116
+f 28/2/117 37/51/118 40/3/119
+f 40/3/119 37/51/118 49/54/120
+f 31/48/121 3/33/122 43/49/123
+f 43/49/123 3/33/122 39/36/124
+f 36/52/125 35/42/126 48/53/127
+f 48/53/127 35/42/126 47/43/128
+f 29/73/129 28/2/130 41/1/131
+f 41/1/131 28/2/130 40/3/132
+f 65/4/133 66/5/134 28/2/135
+f 28/2/135 66/5/134 37/51/136
+f 63/6/137 72/7/138 31/48/139
+f 31/48/139 72/7/138 3/33/140
+f 64/8/141 69/39/142 30/34/143
+f 30/34/143 69/39/142 34/37/144
+f 66/5/145 67/59/146 37/51/147
+f 37/51/147 67/59/146 36/52/148
+f 67/59/149 68/25/150 36/52/151
+f 36/52/151 68/25/150 35/42/152
+f 68/25/153 71/23/154 35/42/155
+f 35/42/155 71/23/154 1/47/156
+f 62/28/157 70/10/158 33/55/159
+f 33/55/159 70/10/158 32/44/160
+f 69/39/161 62/28/162 34/37/163
+f 34/37/163 62/28/162 33/55/164
+f 70/10/165 73/56/166 32/44/167
+f 32/44/167 73/56/166 29/73/168
+f 71/23/169 63/6/170 1/47/171
+f 1/47/171 63/6/170 31/48/172
+f 72/7/173 64/8/174 3/33/175
+f 3/33/175 64/8/174 30/34/176
+f 73/56/177 65/4/178 29/73/179
+f 29/73/179 65/4/178 28/2/180
+f 6/15/181 5/11/182 60/9/183
+f 60/9/183 5/11/182 61/60/184
+f 2/18/185 7/17/186 58/27/187
+f 58/27/187 7/17/186 59/21/188
+f 4/62/189 8/68/190 56/57/191
+f 56/57/191 8/68/190 57/24/192
+f 9/64/193 6/15/194 55/41/195
+f 55/41/195 6/15/194 60/9/196
+f 11/70/197 10/65/198 53/40/199
+f 53/40/199 10/65/198 54/26/200
+f 10/65/201 9/64/202 54/26/203
+f 54/26/203 9/64/202 55/41/204
+f 12/63/205 4/62/206 52/58/207
+f 52/58/207 4/62/206 56/57/208
+f 13/31/209 12/63/210 51/61/211
+f 51/61/211 12/63/210 52/58/212
+f 14/38/213 13/31/214 50/22/215
+f 50/22/215 13/31/214 51/61/216
+f 7/17/217 11/70/218 59/21/219
+f 59/21/219 11/70/218 53/40/220
+f 8/68/221 2/18/222 57/24/223
+f 57/24/223 2/18/222 58/27/224
+f 5/11/225 14/38/226 61/60/227
+f 61/60/227 14/38/226 50/22/228
+f 60/9/229 61/60/230 73/56/231
+f 73/56/231 61/60/230 65/4/232
+f 58/27/233 59/21/234 72/7/235
+f 72/7/235 59/21/234 64/8/236
+f 56/57/237 57/24/238 71/23/239
+f 71/23/239 57/24/238 63/6/240
+f 55/41/241 60/9/242 70/10/243
+f 70/10/243 60/9/242 73/56/244
+f 53/40/245 54/26/246 69/39/247
+f 69/39/247 54/26/246 62/28/248
+f 54/26/249 55/41/250 62/28/251
+f 62/28/251 55/41/250 70/10/252
+f 52/58/253 56/57/254 68/25/255
+f 68/25/255 56/57/254 71/23/256
+f 51/61/257 52/58/258 67/59/259
+f 67/59/259 52/58/258 68/25/260
+f 50/22/261 51/61/262 66/5/263
+f 66/5/263 51/61/262 67/59/264
+f 59/21/265 53/40/266 64/8/267
+f 64/8/267 53/40/266 69/39/268
+f 57/24/269 58/27/270 63/6/271
+f 63/6/271 58/27/270 72/7/272
+f 61/60/273 50/22/274 65/4/275
+f 65/4/275 50/22/274 66/5/276
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_trigger.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_trigger.obj
new file mode 100644
index 0000000..7b87239
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_trigger.obj
@@ -0,0 +1,1240 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.012469 0.002342 0.063363
+v 0.009351 -0.007408 0.065472
+v 0.010658 -0.006647 0.065307
+v 0.006441 -0.008510 0.065710
+v 0.003284 -0.009190 0.065857
+v 0.012469 -0.001545 0.064204
+v 0.012469 -0.003489 0.064624
+v 0.012469 0.002342 0.037855
+v 0.012469 0.002342 0.035622
+v 0.012469 0.002342 0.040088
+v 0.012469 0.002342 0.042320
+v 0.010019 -0.002861 0.035742
+v 0.010019 -0.009748 0.045459
+v 0.008153 -0.014181 0.046940
+v 0.006671 -0.015226 0.047180
+v 0.010019 -0.012511 0.046558
+v 0.010019 -0.004041 0.040214
+v 0.010019 -0.007400 0.044006
+v 0.010019 -0.005473 0.042235
+v 0.010019 -0.003159 0.038022
+v 0.004895 -0.016332 0.047274
+v 0.002649 -0.002861 0.035742
+v 0.002649 -0.009748 0.045459
+v 0.002649 -0.014181 0.046940
+v 0.002649 -0.015226 0.047180
+v 0.002649 -0.012511 0.046558
+v 0.002649 -0.004041 0.040214
+v 0.002649 -0.007400 0.044006
+v 0.002649 -0.005473 0.042235
+v 0.002649 -0.003159 0.038022
+v 0.002649 -0.016789 0.047312
+v 0.012469 -0.001216 0.035704
+v 0.012469 -0.001420 0.037969
+v 0.012469 -0.002023 0.040174
+v 0.012469 -0.003002 0.042262
+v 0.010658 -0.013756 0.048907
+v 0.003284 -0.017321 0.049383
+v 0.006441 -0.016360 0.049298
+v 0.009351 -0.014801 0.049145
+v 0.012469 -0.009331 0.047481
+v 0.012469 -0.006949 0.046118
+v 0.012469 0.002342 0.046118
+v 0.012469 -0.001298 0.035706
+v 0.011089 -0.002861 0.035742
+v 0.012469 -0.001595 0.037974
+v 0.011089 -0.003159 0.038022
+v 0.012469 -0.002483 0.040183
+v 0.011089 -0.004041 0.040214
+v 0.012469 -0.003949 0.042252
+v 0.011089 -0.005473 0.042235
+v 0.012469 -0.007147 0.045190
+v 0.011089 -0.007400 0.044006
+v 0.012469 -0.009503 0.046649
+v 0.011089 -0.009748 0.045459
+v 0.011089 -0.012511 0.046558
+v 0.012469 -0.010955 0.047226
+v 0.011466 -0.013144 0.048119
+v 0.011466 -0.006105 0.065190
+v 0.012469 -0.004454 0.064833
+v 0.007971 -0.015226 0.047180
+v 0.009351 -0.014935 0.048529
+v 0.010658 -0.013889 0.048289
+v 0.009277 -0.014181 0.046940
+v 0.003284 -0.017462 0.048733
+v 0.002990 -0.017308 0.047351
+v 0.006441 -0.016497 0.048661
+v 0.005551 -0.016526 0.047290
+v 0.012469 -0.010781 0.048032
+v 0.011466 -0.013010 0.048737
+v 0.000000 -0.003159 0.038022
+v 0.000000 -0.009748 0.045459
+v 0.000000 -0.007400 0.044006
+v 0.000000 -0.002861 0.035742
+v 0.000000 -0.009419 0.065907
+v 0.000000 -0.005473 0.042235
+v -0.012469 0.002342 0.063363
+v 0.000000 -0.014181 0.046940
+v 0.000000 -0.015226 0.047180
+v 0.000000 -0.012511 0.046558
+v 0.000000 -0.004041 0.040214
+v 0.000000 -0.016789 0.047312
+v -0.009351 -0.007408 0.065472
+v -0.010658 -0.006647 0.065307
+v -0.006441 -0.008510 0.065710
+v -0.003284 -0.009190 0.065857
+v -0.012469 -0.001545 0.064204
+v -0.012469 -0.003489 0.064624
+v -0.012469 0.002342 0.037855
+v -0.012469 0.002342 0.035622
+v -0.012469 0.002342 0.040088
+v -0.012469 0.002342 0.042320
+v -0.010019 -0.002861 0.035742
+v -0.010019 -0.009748 0.045459
+v -0.008153 -0.014181 0.046940
+v -0.006671 -0.015226 0.047180
+v -0.010019 -0.012511 0.046558
+v -0.010019 -0.004041 0.040214
+v -0.010019 -0.007400 0.044006
+v -0.010019 -0.005473 0.042235
+v -0.010019 -0.003159 0.038022
+v -0.004895 -0.016332 0.047274
+v -0.002649 -0.002861 0.035742
+v -0.002649 -0.009748 0.045459
+v -0.002649 -0.014181 0.046940
+v -0.002649 -0.015226 0.047180
+v -0.002649 -0.012511 0.046558
+v -0.002649 -0.004041 0.040214
+v -0.002649 -0.007400 0.044006
+v -0.002649 -0.005473 0.042235
+v -0.002649 -0.003159 0.038022
+v -0.002649 -0.016789 0.047312
+v -0.012469 -0.001216 0.035704
+v -0.012469 -0.001420 0.037969
+v -0.012469 -0.002023 0.040174
+v -0.012469 -0.003002 0.042262
+v -0.010658 -0.013756 0.048907
+v 0.000000 -0.017646 0.049412
+v -0.003284 -0.017321 0.049383
+v -0.006441 -0.016360 0.049298
+v -0.009351 -0.014801 0.049145
+v -0.012469 -0.009331 0.047481
+v -0.012469 -0.006949 0.046118
+v -0.012469 0.002342 0.046118
+v -0.012469 -0.001298 0.035706
+v -0.011089 -0.002861 0.035742
+v -0.012469 -0.001595 0.037974
+v -0.011089 -0.003159 0.038022
+v -0.012469 -0.002483 0.040183
+v -0.011089 -0.004041 0.040214
+v -0.012469 -0.003949 0.042252
+v -0.011089 -0.005473 0.042235
+v -0.012469 -0.007147 0.045190
+v -0.011089 -0.007400 0.044006
+v -0.012469 -0.009503 0.046649
+v -0.011089 -0.009748 0.045459
+v -0.011089 -0.012511 0.046558
+v -0.012469 -0.010955 0.047226
+v -0.011466 -0.013144 0.048119
+v -0.011466 -0.006105 0.065190
+v -0.012469 -0.004454 0.064833
+v -0.007971 -0.015226 0.047180
+v -0.009351 -0.014935 0.048529
+v -0.010658 -0.013889 0.048289
+v -0.009277 -0.014181 0.046940
+v -0.003284 -0.017462 0.048733
+v -0.002990 -0.017308 0.047351
+v 0.000000 -0.017604 0.047373
+v 0.000000 -0.017787 0.048757
+v -0.006441 -0.016497 0.048661
+v -0.005551 -0.016526 0.047290
+v -0.012469 -0.010781 0.048032
+v -0.011466 -0.013010 0.048737
+v 0.000000 -0.016909 0.052819
+v 0.003284 -0.016586 0.052783
+v 0.006441 -0.015629 0.052675
+v 0.012469 -0.008622 0.050921
+v 0.012469 -0.006181 0.049710
+v 0.012469 0.002342 0.049484
+v 0.010658 -0.013032 0.052252
+v 0.009351 -0.014078 0.052488
+v 0.011466 -0.012286 0.052084
+v 0.012469 -0.010048 0.051420
+v -0.003284 -0.016586 0.052783
+v -0.006441 -0.015629 0.052675
+v -0.012469 -0.008622 0.050921
+v -0.012469 -0.006181 0.049710
+v -0.012469 0.002342 0.049484
+v -0.010658 -0.013032 0.052252
+v -0.009351 -0.014078 0.052488
+v -0.011466 -0.012286 0.052084
+v -0.012469 -0.010048 0.051420
+vt 0.389626 0.855156
+vt 0.390521 0.876103
+vt 0.497178 0.870536
+vt 0.484333 0.855130
+vt 0.412289 0.967976
+vt 0.478403 0.774806
+vt 0.463433 0.778867
+vt 0.412380 0.809901
+vt 0.409798 0.834762
+vt 0.396714 0.803798
+vt 0.445493 0.843045
+vt 0.390505 0.834206
+vt 0.408648 0.830875
+vt 0.389815 0.844760
+vt 0.330514 0.823054
+vt 0.432027 0.965825
+vt 0.391682 0.823360
+vt 0.437068 0.901524
+vt 0.402269 0.864596
+vt 0.493086 0.959935
+vt 0.480141 0.959822
+vt 0.407446 0.893095
+vt 0.404279 0.890129
+vt 0.494272 0.915752
+vt 0.405408 0.893816
+vt 0.445499 0.867230
+vt 0.458613 0.855135
+vt 0.434528 0.906466
+vt 0.478084 0.921347
+vt 0.433534 0.865962
+vt 0.458054 0.868399
+vt 0.392761 0.817328
+vt 0.404255 0.820169
+vt 0.336182 0.937153
+vt 0.405821 0.786943
+vt 0.413215 0.855147
+vt 0.416743 0.904914
+vt 0.413479 0.906889
+vt 0.389822 0.865551
+vt 0.328339 0.855183
+vt 0.406268 0.863663
+vt 0.405868 0.923348
+vt 0.393637 0.897111
+vt 0.478510 0.773673
+vt 0.409635 0.808495
+vt 0.403113 0.836120
+vt 0.402263 0.845706
+vt 0.407910 0.840728
+vt 0.497174 0.839720
+vt 0.478060 0.788901
+vt 0.329051 0.838946
+vt 0.334987 0.787175
+vt 0.401168 0.835782
+vt 0.448778 0.784859
+vt 0.447886 0.797316
+vt 0.462582 0.792299
+vt 0.412210 0.742297
+vt 0.422076 0.845553
+vt 0.416711 0.805374
+vt 0.464373 0.798874
+vt 0.431956 0.744434
+vt 0.450212 0.930782
+vt 0.427474 0.913081
+vt 0.423235 0.915539
+vt 0.399149 0.912516
+vt 0.335447 0.927703
+vt 0.335050 0.923173
+vt 0.412698 0.863505
+vt 0.416158 0.863975
+vt 0.411799 0.881077
+vt 0.409812 0.875534
+vt 0.493410 0.773030
+vt 0.413445 0.803401
+vt 0.402794 0.825572
+vt 0.437042 0.808751
+vt 0.400328 0.864755
+vt 0.408394 0.855149
+vt 0.333617 0.909517
+vt 0.332644 0.902621
+vt 0.403126 0.874181
+vt 0.422082 0.864736
+vt 0.416711 0.855146
+vt 0.402042 0.855151
+vt 0.422934 0.899416
+vt 0.334248 0.913876
+vt 0.450172 0.779477
+vt 0.493052 0.750284
+vt 0.458048 0.841870
+vt 0.433528 0.844320
+vt 0.434498 0.803810
+vt 0.494257 0.794492
+vt 0.410463 0.825689
+vt 0.415040 0.889351
+vt 0.471407 0.855132
+vt 0.446098 0.855138
+vt 0.479212 0.914296
+vt 0.493852 0.922939
+vt 0.410483 0.884606
+vt 0.447916 0.912950
+vt 0.434120 0.855141
+vt 0.470937 0.869361
+vt 0.406318 0.889536
+vt 0.493399 0.772491
+vt 0.332599 0.807735
+vt 0.467185 0.749518
+vt 0.413259 0.817561
+vt 0.393606 0.813194
+vt 0.423197 0.794742
+vt 0.479191 0.795954
+vt 0.406262 0.846637
+vt 0.406068 0.855150
+vt 0.407893 0.847406
+vt 0.406295 0.820760
+vt 0.406924 0.838997
+vt 0.399108 0.797782
+vt 0.422669 0.855144
+vt 0.407898 0.862892
+vt 0.391706 0.886948
+vt 0.401182 0.874520
+vt 0.404771 0.884253
+vt 0.483983 0.870072
+vt 0.400087 0.855152
+vt 0.329066 0.871418
+vt 0.330545 0.887307
+vt 0.497404 0.855128
+vt 0.416152 0.846318
+vt 0.454204 0.747884
+vt 0.404751 0.826046
+vt 0.411782 0.829217
+vt 0.400321 0.845549
+vt 0.463943 0.775990
+vt 0.337941 0.751811
+vt 0.333566 0.800837
+vt 0.412692 0.846790
+vt 0.483978 0.840187
+vt 0.334192 0.796476
+vt 0.493834 0.787302
+vt 0.450179 0.803181
+vt 0.408665 0.879421
+vt 0.406935 0.871302
+vt 0.407920 0.869569
+vt 0.467237 0.960717
+vt 0.454263 0.962359
+vt 0.450205 0.907085
+vt 0.464397 0.911384
+vt 0.462610 0.917958
+vt 0.402814 0.884728
+vt 0.425386 0.895350
+vt 0.336106 0.773189
+vt 0.422907 0.810868
+vt 0.338033 0.958524
+vt 0.412410 0.900390
+vt 0.409666 0.901797
+vt 0.396751 0.906503
+vt 0.463979 0.934259
+vt 0.478433 0.935435
+vt 0.493434 0.937204
+vt 0.493422 0.937742
+vt 0.478541 0.936568
+vt 0.463468 0.931384
+vt 0.448815 0.925403
+vt 0.427439 0.797198
+vt 0.480098 0.750404
+vt 0.425362 0.814933
+vt 0.335380 0.782643
+vt 0.470932 0.840902
+vt 0.405382 0.816481
+vt 0.415019 0.820940
+vt 0.407421 0.817201
+vt 0.392788 0.892978
+vt 0.413283 0.892731
+vn 0.941739 -0.326680 -0.080053
+vn 0.945242 -0.323626 -0.042239
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.933306 -0.319627 -0.163644
+vn 0.941739 -0.326680 -0.080053
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.919246 -0.303577 -0.250656
+vn 0.933306 -0.319627 -0.163644
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.911448 -0.243205 -0.331832
+vn 0.919246 -0.303577 -0.250656
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.911448 -0.243205 -0.331832
+vn 1.000000 0.000000 0.000000
+vn 0.916678 -0.176432 -0.358571
+vn 1.000000 0.000000 0.000000
+vn 0.916678 -0.176432 -0.358571
+vn 1.000000 0.000000 0.000000
+vn 0.910980 -0.276630 -0.305928
+vn 0.979394 -0.197399 0.042666
+vn 0.973672 -0.216165 0.072360
+vn 1.000000 0.000000 0.000000
+vn 0.978343 -0.189186 0.083988
+vn 1.000000 0.000000 0.000000
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.161752 -0.860157 0.483701
+vn 0.000000 -0.869145 0.494557
+vn 0.178204 -0.920500 0.347740
+vn 0.000000 -0.934880 0.354963
+vn 0.325059 -0.831219 0.451012
+vn 0.161752 -0.860157 0.483701
+vn 0.352896 -0.876974 0.326161
+vn 0.178204 -0.920500 0.347740
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.079380 -0.996844
+vn 0.596627 -0.720598 0.353234
+vn 0.491395 -0.774189 0.398951
+vn 0.629820 -0.732455 0.258526
+vn 0.520259 -0.802134 0.293106
+vn 0.352896 -0.876974 0.326161
+vn 0.520259 -0.802134 0.293106
+vn 0.325059 -0.831219 0.451012
+vn 0.491395 -0.774189 0.398951
+vn 0.382681 -0.903004 0.195293
+vn 0.195076 -0.958627 0.207314
+vn 0.395898 -0.918084 -0.019654
+vn 0.200818 -0.979187 0.029421
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.291820 -0.619012 -0.729154
+vn 0.221873 -0.646208 -0.730197
+vn 0.000000 -0.154215 -0.988037
+vn -0.000244 -0.081790 -0.996650
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.341206 -0.695262 -0.632605
+vn 0.383839 -0.804668 -0.452965
+vn 0.395898 -0.918084 -0.019654
+vn 0.552792 -0.829295 -0.081791
+vn 0.382681 -0.903004 0.195293
+vn 0.555564 -0.812685 0.175758
+vn 0.772999 -0.620157 -0.133706
+vn 0.819595 -0.559998 0.121101
+vn 0.635399 -0.761197 -0.129796
+vn 0.661102 -0.733340 0.158607
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.154215 -0.988037
+vn -0.000244 -0.081790 -0.996650
+vn 0.000000 -0.154215 -0.988037
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.195076 -0.958627 0.207314
+vn 0.000000 -0.977405 0.211375
+vn 0.200818 -0.979187 0.029421
+vn 0.000000 -0.999153 0.041139
+vn 0.221873 -0.646208 -0.730197
+vn 0.120368 -0.692302 -0.711498
+vn -0.000244 -0.081790 -0.996650
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.333728 -0.575562 -0.746561
+vn 0.341206 -0.695262 -0.632605
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.412992 -0.879180 -0.237657
+vn 0.422817 -0.898585 -0.117347
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.383839 -0.804668 -0.452965
+vn 0.412992 -0.879180 -0.237657
+vn 0.457368 -0.496250 -0.737936
+vn 0.313953 -0.551668 -0.772720
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.457368 -0.496250 -0.737936
+vn 0.350609 -0.424894 -0.834589
+vn 0.635399 -0.761197 -0.129796
+vn 0.661102 -0.733340 0.158607
+vn 0.552792 -0.829295 -0.081791
+vn 0.555564 -0.812685 0.175758
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.291820 -0.619012 -0.729154
+vn 0.313953 -0.551668 -0.772720
+vn 0.120368 -0.692302 -0.711498
+vn 0.000000 -0.707900 -0.706313
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.079380 -0.996844
+vn 0.788994 -0.557075 0.259142
+vn 0.596627 -0.720598 0.353234
+vn 0.791884 -0.577089 0.199718
+vn 0.629820 -0.732455 0.258526
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.350609 -0.424894 -0.834589
+vn 0.333728 -0.575562 -0.746561
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.457368 -0.496250 -0.737936
+vn 0.910980 -0.276630 -0.305928
+vn 0.772999 -0.620157 -0.133706
+vn 0.313953 -0.551668 -0.772720
+vn 0.457368 -0.496250 -0.737936
+vn 0.635399 -0.761197 -0.129796
+vn 0.772999 -0.620157 -0.133706
+vn 0.945242 -0.323626 -0.042239
+vn 0.941739 -0.326680 -0.080053
+vn 0.422817 -0.898585 -0.117347
+vn 0.412992 -0.879180 -0.237657
+vn 0.941739 -0.326680 -0.080053
+vn 0.933306 -0.319627 -0.163644
+vn 0.412992 -0.879180 -0.237657
+vn 0.383839 -0.804668 -0.452965
+vn 0.933306 -0.319627 -0.163644
+vn 0.919246 -0.303577 -0.250656
+vn 0.383839 -0.804668 -0.452965
+vn 0.341206 -0.695262 -0.632605
+vn 0.919246 -0.303577 -0.250656
+vn 0.911448 -0.243205 -0.331832
+vn 0.341206 -0.695262 -0.632605
+vn 0.333728 -0.575562 -0.746561
+vn 0.916678 -0.176432 -0.358571
+vn 0.350609 -0.424894 -0.834589
+vn 0.911448 -0.243205 -0.331832
+vn 0.333728 -0.575562 -0.746561
+vn 0.916678 -0.176432 -0.358571
+vn 0.910980 -0.276630 -0.305928
+vn 0.350609 -0.424894 -0.834589
+vn 0.457368 -0.496250 -0.737936
+vn 0.819595 -0.559998 0.121101
+vn 0.772999 -0.620157 -0.133706
+vn 0.979394 -0.197399 0.042666
+vn 0.910980 -0.276630 -0.305928
+vn 0.552792 -0.829295 -0.081791
+vn 0.395898 -0.918084 -0.019654
+vn 0.291820 -0.619012 -0.729154
+vn 0.221873 -0.646208 -0.730197
+vn 0.395898 -0.918084 -0.019654
+vn 0.200818 -0.979187 0.029421
+vn 0.221873 -0.646208 -0.730197
+vn 0.120368 -0.692302 -0.711498
+vn 0.200818 -0.979187 0.029421
+vn 0.000000 -0.999153 0.041139
+vn 0.120368 -0.692302 -0.711498
+vn 0.000000 -0.707900 -0.706313
+vn 0.291820 -0.619012 -0.729154
+vn 0.313953 -0.551668 -0.772720
+vn 0.552792 -0.829295 -0.081791
+vn 0.635399 -0.761197 -0.129796
+vn 0.791884 -0.577089 0.199718
+vn 0.819595 -0.559998 0.121101
+vn 0.973672 -0.216165 0.072360
+vn 0.979394 -0.197399 0.042666
+vn -0.941739 -0.326680 -0.080053
+vn -1.000000 0.000000 0.000000
+vn -0.945242 -0.323626 -0.042239
+vn -1.000000 0.000000 0.000000
+vn -0.933306 -0.319627 -0.163644
+vn -1.000000 0.000000 0.000000
+vn -0.941739 -0.326680 -0.080053
+vn -1.000000 0.000000 0.000000
+vn -0.919246 -0.303577 -0.250656
+vn -1.000000 0.000000 0.000000
+vn -0.933306 -0.319627 -0.163644
+vn -1.000000 0.000000 0.000000
+vn -0.911448 -0.243205 -0.331832
+vn -1.000000 0.000000 0.000000
+vn -0.919246 -0.303577 -0.250656
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.916678 -0.176432 -0.358571
+vn -0.911448 -0.243205 -0.331832
+vn -0.979394 -0.197399 0.042666
+vn -1.000000 0.000000 0.000000
+vn -0.910980 -0.276630 -0.305928
+vn -0.916678 -0.176432 -0.358571
+vn -0.973672 -0.216165 0.072360
+vn -0.978343 -0.189186 0.083988
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.934880 0.354963
+vn 0.000000 -0.869145 0.494557
+vn -0.178204 -0.920500 0.347740
+vn -0.161752 -0.860157 0.483701
+vn -0.178204 -0.920500 0.347740
+vn -0.161752 -0.860157 0.483701
+vn -0.352896 -0.876974 0.326161
+vn -0.325059 -0.831219 0.451012
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.079380 -0.996844
+vn -0.520259 -0.802134 0.293106
+vn -0.491395 -0.774189 0.398951
+vn -0.629820 -0.732455 0.258526
+vn -0.596627 -0.720598 0.353234
+vn -0.352896 -0.876974 0.326161
+vn -0.325059 -0.831219 0.451012
+vn -0.520259 -0.802134 0.293106
+vn -0.491395 -0.774189 0.398951
+vn -0.200818 -0.979187 0.029421
+vn -0.195076 -0.958627 0.207314
+vn -0.395898 -0.918084 -0.019654
+vn -0.382681 -0.903004 0.195293
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.291820 -0.619012 -0.729154
+vn 0.000000 -0.154215 -0.988037
+vn -0.221873 -0.646208 -0.730197
+vn 0.000244 -0.081790 -0.996650
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn 0.000000 -0.750417 -0.660964
+vn -0.341206 -0.695262 -0.632605
+vn 0.000000 -0.877817 -0.478995
+vn -0.383839 -0.804668 -0.452965
+vn -0.395898 -0.918084 -0.019654
+vn -0.382681 -0.903004 0.195293
+vn -0.552792 -0.829295 -0.081791
+vn -0.555564 -0.812685 0.175758
+vn -0.772999 -0.620157 -0.133706
+vn -0.635399 -0.761197 -0.129796
+vn -0.819595 -0.559998 0.121101
+vn -0.661102 -0.733340 0.158607
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000244 -0.081790 -0.996650
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.999153 0.041139
+vn 0.000000 -0.977405 0.211375
+vn -0.200818 -0.979187 0.029421
+vn -0.195076 -0.958627 0.207314
+vn -0.221873 -0.646208 -0.730197
+vn 0.000244 -0.081790 -0.996650
+vn -0.120368 -0.692302 -0.711498
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.604128 -0.796887
+vn -0.333728 -0.575562 -0.746561
+vn 0.000000 -0.750417 -0.660964
+vn -0.341206 -0.695262 -0.632605
+vn 0.000000 -0.967377 -0.253342
+vn -0.412992 -0.879180 -0.237657
+vn 0.000000 -0.991581 -0.129491
+vn -0.422817 -0.898585 -0.117347
+vn 0.000000 -0.877817 -0.478995
+vn -0.383839 -0.804668 -0.452965
+vn 0.000000 -0.967377 -0.253342
+vn -0.412992 -0.879180 -0.237657
+vn -0.457368 -0.496250 -0.737936
+vn 0.000000 -0.297194 -0.954817
+vn -0.313953 -0.551668 -0.772720
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn -0.457368 -0.496250 -0.737936
+vn 0.000000 -0.449666 -0.893197
+vn -0.350609 -0.424894 -0.834589
+vn -0.635399 -0.761197 -0.129796
+vn -0.552792 -0.829295 -0.081791
+vn -0.661102 -0.733340 0.158607
+vn -0.555564 -0.812685 0.175758
+vn 0.000000 -0.154215 -0.988037
+vn -0.291820 -0.619012 -0.729154
+vn 0.000000 -0.223005 -0.974817
+vn -0.313953 -0.551668 -0.772720
+vn -0.120368 -0.692302 -0.711498
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.707900 -0.706313
+vn 0.000000 -0.079380 -0.996844
+vn -0.629820 -0.732455 0.258526
+vn -0.596627 -0.720598 0.353234
+vn -0.791884 -0.577089 0.199718
+vn -0.788994 -0.557075 0.259142
+vn 0.000000 -0.449666 -0.893197
+vn -0.350609 -0.424894 -0.834589
+vn 0.000000 -0.604128 -0.796887
+vn -0.333728 -0.575562 -0.746561
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.457368 -0.496250 -0.737936
+vn -0.772999 -0.620157 -0.133706
+vn -0.910980 -0.276630 -0.305928
+vn -0.313953 -0.551668 -0.772720
+vn -0.635399 -0.761197 -0.129796
+vn -0.457368 -0.496250 -0.737936
+vn -0.772999 -0.620157 -0.133706
+vn -0.945242 -0.323626 -0.042239
+vn -0.422817 -0.898585 -0.117347
+vn -0.941739 -0.326680 -0.080053
+vn -0.412992 -0.879180 -0.237657
+vn -0.941739 -0.326680 -0.080053
+vn -0.412992 -0.879180 -0.237657
+vn -0.933306 -0.319627 -0.163644
+vn -0.383839 -0.804668 -0.452965
+vn -0.933306 -0.319627 -0.163644
+vn -0.383839 -0.804668 -0.452965
+vn -0.919246 -0.303577 -0.250656
+vn -0.341206 -0.695262 -0.632605
+vn -0.919246 -0.303577 -0.250656
+vn -0.341206 -0.695262 -0.632605
+vn -0.911448 -0.243205 -0.331832
+vn -0.333728 -0.575562 -0.746561
+vn -0.333728 -0.575562 -0.746561
+vn -0.350609 -0.424894 -0.834589
+vn -0.911448 -0.243205 -0.331832
+vn -0.916678 -0.176432 -0.358571
+vn -0.916678 -0.176432 -0.358571
+vn -0.350609 -0.424894 -0.834589
+vn -0.910980 -0.276630 -0.305928
+vn -0.457368 -0.496250 -0.737936
+vn -0.819595 -0.559998 0.121101
+vn -0.979394 -0.197399 0.042666
+vn -0.772999 -0.620157 -0.133706
+vn -0.910980 -0.276630 -0.305928
+vn -0.552792 -0.829295 -0.081791
+vn -0.291820 -0.619012 -0.729154
+vn -0.395898 -0.918084 -0.019654
+vn -0.221873 -0.646208 -0.730197
+vn -0.395898 -0.918084 -0.019654
+vn -0.221873 -0.646208 -0.730197
+vn -0.200818 -0.979187 0.029421
+vn -0.120368 -0.692302 -0.711498
+vn -0.200818 -0.979187 0.029421
+vn -0.120368 -0.692302 -0.711498
+vn 0.000000 -0.999153 0.041139
+vn 0.000000 -0.707900 -0.706313
+vn -0.291820 -0.619012 -0.729154
+vn -0.552792 -0.829295 -0.081791
+vn -0.313953 -0.551668 -0.772720
+vn -0.635399 -0.761197 -0.129796
+vn -0.791884 -0.577089 0.199718
+vn -0.973672 -0.216165 0.072360
+vn -0.819595 -0.559998 0.121101
+vn -0.979394 -0.197399 0.042666
+vn -0.978343 -0.189186 0.083988
+vn -0.973672 -0.216165 0.072360
+vn -0.788994 -0.557075 0.259142
+vn -0.791884 -0.577089 0.199718
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.819595 -0.559998 0.121101
+vn -0.661102 -0.733340 0.158607
+vn -0.791884 -0.577089 0.199718
+vn -0.629820 -0.732455 0.258526
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.382681 -0.903004 0.195293
+vn -0.352896 -0.876974 0.326161
+vn -0.555564 -0.812685 0.175758
+vn -0.520259 -0.802134 0.293106
+vn -0.661102 -0.733340 0.158607
+vn -0.555564 -0.812685 0.175758
+vn -0.629820 -0.732455 0.258526
+vn -0.520259 -0.802134 0.293106
+vn -0.195076 -0.958627 0.207314
+vn -0.178204 -0.920500 0.347740
+vn -0.382681 -0.903004 0.195293
+vn -0.352896 -0.876974 0.326161
+vn 0.000000 -0.977405 0.211375
+vn 0.000000 -0.934880 0.354963
+vn -0.195076 -0.958627 0.207314
+vn -0.178204 -0.920500 0.347740
+vn -0.973672 -0.216165 0.072360
+vn -1.000000 0.000000 0.000000
+vn -0.979394 -0.197399 0.042666
+vn -1.000000 0.000000 0.000000
+vn 0.791884 -0.577089 0.199718
+vn 0.973672 -0.216165 0.072360
+vn 0.788994 -0.557075 0.259142
+vn 0.978343 -0.189186 0.083988
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.819595 -0.559998 0.121101
+vn 0.791884 -0.577089 0.199718
+vn 0.661102 -0.733340 0.158607
+vn 0.629820 -0.732455 0.258526
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.382681 -0.903004 0.195293
+vn 0.555564 -0.812685 0.175758
+vn 0.352896 -0.876974 0.326161
+vn 0.520259 -0.802134 0.293106
+vn 0.661102 -0.733340 0.158607
+vn 0.629820 -0.732455 0.258526
+vn 0.555564 -0.812685 0.175758
+vn 0.520259 -0.802134 0.293106
+vn 0.352896 -0.876974 0.326161
+vn 0.178204 -0.920500 0.347740
+vn 0.382681 -0.903004 0.195293
+vn 0.195076 -0.958627 0.207314
+vn 0.178204 -0.920500 0.347740
+vn 0.000000 -0.934880 0.354963
+vn 0.195076 -0.958627 0.207314
+vn 0.000000 -0.977405 0.211375
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.979394 -0.197399 0.042666
+vn 0.973672 -0.216165 0.072360
+f 45/156/1 43/157/2 33/159/3
+f 33/159/3 43/157/2 32/158/4
+f 47/160/5 45/156/6 34/155/7
+f 34/155/7 45/156/6 33/159/8
+f 49/161/9 47/160/10 35/62/11
+f 35/62/11 47/160/10 34/155/12
+f 51/63/13 49/161/14 41/64/15
+f 41/64/15 49/161/14 35/62/16
+f 51/63/17 41/64/18 53/37/19
+f 53/37/19 41/64/18 40/38/20
+f 53/37/21 40/38/22 56/152/23
+f 56/152/23 40/38/22 68/153/24
+f 162/154/25 156/65/26 59/67/27
+f 59/67/27 156/65/26 7/66/28
+f 25/68/29 24/69/30 15/71/31
+f 15/71/31 24/69/30 14/70/32
+f 5/123/33 74/40/34 154/39/35
+f 154/39/35 74/40/34 153/1/36
+f 4/124/37 5/123/38 155/2/39
+f 155/2/39 5/123/38 154/39/40
+f 26/81/41 24/69/42 79/116/43
+f 79/116/43 24/69/42 77/82/44
+f 25/68/45 31/117/46 78/36/47
+f 78/36/47 31/117/46 81/77/48
+f 3/78/49 2/79/50 159/170/51
+f 159/170/51 2/79/50 160/118/52
+f 155/2/53 160/118/54 4/124/55
+f 4/124/55 160/118/54 2/79/56
+f 38/119/57 37/76/58 66/80/59
+f 66/80/59 37/76/58 64/19/60
+f 33/159/61 32/158/62 8/21/63
+f 8/21/63 32/158/62 9/20/64
+f 157/42/65 6/34/66 156/65/67
+f 156/65/67 6/34/66 7/66/68
+f 60/139/69 67/140/70 15/71/71
+f 15/71/71 67/140/70 21/141/72
+f 35/62/73 34/155/74 11/143/75
+f 11/143/75 34/155/74 10/142/76
+f 34/155/77 33/159/78 10/142/79
+f 10/142/79 33/159/78 8/21/80
+f 19/144/81 17/145/82 50/99/83
+f 50/99/83 17/145/82 48/146/84
+f 66/80/85 61/120/86 38/119/87
+f 38/119/87 61/120/86 39/147/88
+f 57/22/89 69/25/90 62/102/91
+f 62/102/91 69/25/90 36/23/92
+f 26/81/93 23/30/94 16/93/95
+f 16/93/95 23/30/94 13/148/96
+f 23/30/97 28/26/98 13/148/99
+f 13/148/99 28/26/98 18/18/100
+f 28/26/101 29/31/102 18/18/103
+f 18/18/103 29/31/102 19/144/104
+f 29/31/105 27/101/106 19/144/107
+f 19/144/107 27/101/106 17/145/108
+f 27/101/109 30/121/110 17/145/111
+f 17/145/111 30/121/110 20/96/112
+f 30/121/113 22/3/114 20/96/115
+f 20/96/115 22/3/114 12/24/116
+f 70/4/117 73/125/118 30/121/119
+f 30/121/119 73/125/118 22/3/120
+f 80/94/121 70/4/122 27/101/123
+f 27/101/123 70/4/122 30/121/124
+f 75/27/125 80/94/126 29/31/127
+f 29/31/127 80/94/126 27/101/128
+f 72/95/129 75/27/130 28/26/131
+f 28/26/131 75/27/130 29/31/132
+f 71/100/133 72/95/134 23/30/135
+f 23/30/135 72/95/134 28/26/136
+f 79/116/137 71/100/138 26/81/139
+f 26/81/139 71/100/138 23/30/140
+f 15/71/141 21/141/142 25/68/143
+f 25/68/143 21/141/142 31/117/144
+f 16/93/145 14/70/146 26/81/147
+f 26/81/147 14/70/146 24/69/148
+f 78/36/149 77/82/150 25/68/151
+f 25/68/151 77/82/150 24/69/152
+f 37/76/153 117/122/154 64/19/155
+f 64/19/155 117/122/154 148/83/156
+f 67/140/157 65/41/158 21/141/159
+f 21/141/159 65/41/158 31/117/160
+f 18/18/161 19/144/162 52/28/163
+f 52/28/163 19/144/162 50/99/164
+f 20/96/165 12/24/166 46/29/167
+f 46/29/167 12/24/166 44/97/168
+f 17/145/169 20/96/170 48/146/171
+f 48/146/171 20/96/170 46/29/172
+f 55/171/173 63/98/174 16/93/175
+f 16/93/175 63/98/174 14/70/176
+f 16/93/177 13/148/178 55/171/179
+f 55/171/179 13/148/178 54/84/180
+f 62/102/181 36/23/182 61/120/183
+f 61/120/183 36/23/182 39/147/184
+f 15/71/185 14/70/186 60/139/187
+f 60/139/187 14/70/186 63/98/188
+f 65/41/189 147/111/190 31/117/191
+f 31/117/191 147/111/190 81/77/192
+f 58/85/193 3/78/194 161/43/195
+f 161/43/195 3/78/194 159/170/196
+f 13/148/197 18/18/198 54/84/199
+f 54/84/199 18/18/198 52/28/200
+f 11/143/201 42/16/202 35/62/203
+f 35/62/203 42/16/202 41/64/204
+f 41/64/205 42/16/206 157/42/207
+f 157/42/207 42/16/206 158/5/208
+f 55/171/209 56/152/210 57/22/211
+f 63/98/212 55/171/213 62/102/214
+f 62/102/214 55/171/213 57/22/215
+f 43/157/216 45/156/217 44/97/218
+f 44/97/218 45/156/217 46/29/219
+f 45/156/220 47/160/221 46/29/222
+f 46/29/222 47/160/221 48/146/223
+f 47/160/224 49/161/225 48/146/226
+f 48/146/226 49/161/225 50/99/227
+f 49/161/228 51/63/229 50/99/230
+f 50/99/230 51/63/229 52/28/231
+f 53/37/232 54/84/233 51/63/234
+f 51/63/234 54/84/233 52/28/235
+f 53/37/236 56/152/237 54/84/238
+f 54/84/238 56/152/237 55/171/239
+f 69/25/240 57/22/241 68/153/242
+f 68/153/242 57/22/241 56/152/243
+f 61/120/244 66/80/245 60/139/246
+f 60/139/246 66/80/245 67/140/247
+f 66/80/248 64/19/249 67/140/250
+f 67/140/250 64/19/249 65/41/251
+f 64/19/252 148/83/253 65/41/254
+f 65/41/254 148/83/253 147/111/255
+f 60/139/256 63/98/257 61/120/258
+f 61/120/258 63/98/257 62/102/259
+f 161/43/260 69/25/261 162/154/262
+f 162/154/262 69/25/261 68/153/263
+f 126/6/264 113/44/265 124/72/266
+f 124/72/266 113/44/265 112/103/267
+f 128/7/268 114/131/269 126/6/270
+f 126/6/270 114/131/269 113/44/271
+f 130/54/272 115/86/273 128/7/274
+f 128/7/274 115/86/273 114/131/275
+f 132/162/276 122/108/277 130/54/278
+f 130/54/278 122/108/277 115/86/279
+f 121/73/280 122/108/281 134/59/282
+f 134/59/282 122/108/281 132/162/283
+f 151/45/284 121/73/285 137/8/286
+f 137/8/286 121/73/285 134/59/287
+f 171/10/288 140/52/289 165/115/290
+f 165/115/290 140/52/289 87/165/291
+f 105/134/292 95/9/293 104/126/294
+f 104/126/294 95/9/293 94/129/295
+f 153/1/296 74/40/297 163/14/298
+f 163/14/298 74/40/297 85/51/299
+f 163/14/300 85/51/301 164/12/302
+f 164/12/302 85/51/301 84/15/303
+f 106/58/304 79/116/305 104/126/306
+f 104/126/306 79/116/305 77/82/307
+f 105/134/308 78/36/309 111/112/310
+f 111/112/310 78/36/309 81/77/311
+f 169/17/312 82/104/313 168/32/314
+f 168/32/314 82/104/313 83/133/315
+f 164/12/316 84/15/317 169/17/318
+f 169/17/318 84/15/317 82/104/319
+f 145/47/320 118/130/321 149/46/322
+f 149/46/322 118/130/321 119/53/323
+f 113/44/324 88/163/325 112/103/326
+f 112/103/326 88/163/325 89/87/327
+f 87/165/328 86/149/329 165/115/330
+f 165/115/330 86/149/329 166/35/331
+f 141/13/332 95/9/333 150/114/334
+f 150/114/334 95/9/333 101/48/335
+f 115/86/336 91/127/337 114/131/338
+f 114/131/338 91/127/337 90/105/339
+f 114/131/340 90/105/341 113/44/342
+f 113/44/342 90/105/341 88/163/343
+f 99/138/344 131/55/345 97/60/346
+f 97/60/346 131/55/345 129/56/347
+f 149/46/348 119/53/349 142/128/350
+f 142/128/350 119/53/349 120/74/351
+f 138/169/352 143/113/353 152/167/354
+f 152/167/354 143/113/353 116/33/355
+f 106/58/356 96/168/357 103/89/358
+f 103/89/358 96/168/357 93/164/359
+f 103/89/360 93/164/361 108/11/362
+f 108/11/362 93/164/361 98/75/363
+f 108/11/364 98/75/365 109/88/366
+f 109/88/366 98/75/365 99/138/367
+f 109/88/368 99/138/369 107/166/370
+f 107/166/370 99/138/369 97/60/371
+f 107/166/372 97/60/373 110/135/374
+f 110/135/374 97/60/373 100/109/375
+f 110/135/376 100/109/377 102/49/378
+f 102/49/378 100/109/377 92/91/379
+f 70/4/380 110/135/381 73/125/382
+f 73/125/382 110/135/381 102/49/383
+f 80/94/384 107/166/385 70/4/386
+f 70/4/386 107/166/385 110/135/387
+f 75/27/388 109/88/389 80/94/390
+f 80/94/390 109/88/389 107/166/391
+f 72/95/392 108/11/393 75/27/394
+f 75/27/394 108/11/393 109/88/395
+f 71/100/396 103/89/397 72/95/398
+f 72/95/398 103/89/397 108/11/399
+f 79/116/400 106/58/401 71/100/402
+f 71/100/402 106/58/401 103/89/403
+f 95/9/404 105/134/405 101/48/406
+f 101/48/406 105/134/405 111/112/407
+f 96/168/408 106/58/409 94/129/410
+f 94/129/410 106/58/409 104/126/411
+f 78/36/412 105/134/413 77/82/414
+f 77/82/414 105/134/413 104/126/415
+f 148/83/416 117/122/417 145/47/418
+f 145/47/418 117/122/417 118/130/419
+f 150/114/420 101/48/421 146/110/422
+f 146/110/422 101/48/421 111/112/423
+f 98/75/424 133/90/425 99/138/426
+f 99/138/426 133/90/425 131/55/427
+f 100/109/428 127/50/429 92/91/430
+f 92/91/430 127/50/429 125/137/431
+f 97/60/432 129/56/433 100/109/434
+f 100/109/434 129/56/433 127/50/435
+f 136/106/436 96/168/437 144/92/438
+f 144/92/438 96/168/437 94/129/439
+f 96/168/440 136/106/441 93/164/442
+f 93/164/442 136/106/441 135/150/443
+f 143/113/444 142/128/445 116/33/446
+f 116/33/446 142/128/445 120/74/447
+f 95/9/448 141/13/449 94/129/450
+f 94/129/450 141/13/449 144/92/451
+f 146/110/452 111/112/453 147/111/454
+f 147/111/454 111/112/453 81/77/455
+f 168/32/456 83/133/457 170/107/458
+f 170/107/458 83/133/457 139/136/459
+f 93/164/460 135/150/461 98/75/462
+f 98/75/462 135/150/461 133/90/463
+f 91/127/464 115/86/465 123/61/466
+f 123/61/466 115/86/465 122/108/467
+f 167/57/468 123/61/469 166/35/470
+f 166/35/470 123/61/469 122/108/471
+f 136/106/472 138/169/473 137/8/474
+f 144/92/475 143/113/476 136/106/477
+f 136/106/477 143/113/476 138/169/478
+f 124/72/479 125/137/480 126/6/481
+f 126/6/481 125/137/480 127/50/482
+f 126/6/483 127/50/484 128/7/485
+f 128/7/485 127/50/484 129/56/486
+f 128/7/487 129/56/488 130/54/489
+f 130/54/489 129/56/488 131/55/490
+f 130/54/491 131/55/492 132/162/493
+f 132/162/493 131/55/492 133/90/494
+f 133/90/495 135/150/496 132/162/497
+f 132/162/497 135/150/496 134/59/498
+f 134/59/499 135/150/500 137/8/501
+f 137/8/501 135/150/500 136/106/502
+f 152/167/503 151/45/504 138/169/505
+f 138/169/505 151/45/504 137/8/506
+f 142/128/507 141/13/508 149/46/509
+f 149/46/509 141/13/508 150/114/510
+f 149/46/511 150/114/512 145/47/513
+f 145/47/513 150/114/512 146/110/514
+f 145/47/515 146/110/516 148/83/517
+f 148/83/517 146/110/516 147/111/518
+f 141/13/519 142/128/520 144/92/521
+f 144/92/521 142/128/520 143/113/522
+f 170/107/523 171/10/524 152/167/525
+f 152/167/525 171/10/524 151/45/526
+f 140/52/527 171/10/528 139/136/529
+f 139/136/529 171/10/528 170/107/530
+f 76/132/531 167/57/532 86/149/533
+f 86/149/533 167/57/532 166/35/534
+f 152/167/535 116/33/536 170/107/537
+f 170/107/537 116/33/536 168/32/538
+f 165/115/539 166/35/540 121/73/541
+f 121/73/541 166/35/540 122/108/542
+f 119/53/543 164/12/544 120/74/545
+f 120/74/545 164/12/544 169/17/546
+f 116/33/547 120/74/548 168/32/549
+f 168/32/549 120/74/548 169/17/550
+f 118/130/551 163/14/552 119/53/553
+f 119/53/553 163/14/552 164/12/554
+f 117/122/555 153/1/556 118/130/557
+f 118/130/557 153/1/556 163/14/558
+f 171/10/559 165/115/560 151/45/561
+f 151/45/561 165/115/560 121/73/562
+f 161/43/563 162/154/564 58/85/565
+f 58/85/565 162/154/564 59/67/566
+f 157/42/567 158/5/568 6/34/569
+f 6/34/569 158/5/568 1/151/570
+f 69/25/571 161/43/572 36/23/573
+f 36/23/573 161/43/572 159/170/574
+f 41/64/575 157/42/576 40/38/577
+f 40/38/577 157/42/576 156/65/578
+f 38/119/579 39/147/580 155/2/581
+f 155/2/581 39/147/580 160/118/582
+f 36/23/583 159/170/584 39/147/585
+f 39/147/585 159/170/584 160/118/586
+f 155/2/587 154/39/588 38/119/589
+f 38/119/589 154/39/588 37/76/590
+f 154/39/591 153/1/592 37/76/593
+f 37/76/593 153/1/592 117/122/594
+f 40/38/595 156/65/596 68/153/597
+f 68/153/597 156/65/596 162/154/598
diff --git a/libs/vr/libdvrgraphics/assets/laser.obj b/libs/vr/libdvrgraphics/assets/laser.obj
new file mode 100644
index 0000000..32737e4
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/laser.obj
@@ -0,0 +1,28 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v -0.010000 -0.000000 -1.000000
+v 0.010000 -0.000000 -1.000000
+v -0.010000 -0.000000 0.000000
+v 0.010000 -0.000000 0.000000
+v 0.000000 0.010000 -1.000000
+v 0.000000 -0.010000 -1.000000
+v 0.000000 0.010000 -0.000000
+v 0.000000 -0.010000 0.000000
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+f 1/1/1 2/2/2 4/4/3 3/3/4
+f 5/5/5 6/6/6 8/7/7 7/8/8
diff --git a/libs/vr/libdvrgraphics/assets/laser.png b/libs/vr/libdvrgraphics/assets/laser.png
new file mode 100644
index 0000000..a96c68d
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/laser.png
Binary files differ
diff --git a/libs/vr/libdvrgraphics/blur.cpp b/libs/vr/libdvrgraphics/blur.cpp
new file mode 100644
index 0000000..7365b0e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/blur.cpp
@@ -0,0 +1,247 @@
+#include "include/private/dvr/graphics/blur.h"
+
+// clang-format off
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+// clang-format on
+#include <hardware/gralloc.h>
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/egl_image.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+#define POSITION_ATTR 0
+#define OFFSET_BINDING 0
+#define SAMPLER_BINDING 1
+
+namespace {
+
+std::string screen_space_vert_shader = SHADER0([]() { // NOLINT
+ layout(location = 0) in vec4 position_uv;
+ out vec2 texCoords;
+
+ void main() {
+ gl_Position = vec4(position_uv.xy, 0.0, 1.0);
+ texCoords = position_uv.zw;
+ }
+});
+
+std::string kawase_blur_frag_shader = SHADER0([]() { // NOLINT
+ precision mediump float;
+ layout(location = 0) uniform vec2 uSampleOffsets[4];
+ layout(binding = 1) uniform APP_SAMPLER_2D uTexture;
+ in vec2 texCoords;
+ out vec4 color;
+
+ void main() {
+ vec2 tc = texCoords;
+ color = texture(uTexture, tc + uSampleOffsets[0]);
+ color += texture(uTexture, tc + uSampleOffsets[1]);
+ color += texture(uTexture, tc + uSampleOffsets[2]);
+ color += texture(uTexture, tc + uSampleOffsets[3]);
+ color *= (1.0 / 4.0);
+ }
+});
+
+constexpr int g_num_samples = 4;
+
+// Modified kernel patterns originally based on:
+// https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms
+// The modification is left and right rotations of the 3rd and 4th patterns.
+const android::dvr::vec2 g_blur_samples[][g_num_samples] = {
+ {{0.5f, 0.5f}, {-0.5f, 0.5f}, {0.5f, -0.5f}, {-0.5f, -0.5f}},
+ {{1.5f, 1.5f}, {-1.5f, 1.5f}, {1.5f, -1.5f}, {-1.5f, -1.5f}},
+ {{2.5f, 1.5f}, {-1.5f, 2.5f}, {1.5f, -2.5f}, {-2.5f, -1.5f}},
+ {{2.5f, 3.5f}, {-3.5f, 2.5f}, {3.5f, -2.5f}, {-2.5f, -3.5f}},
+ // Last pass disabled, because it is more blur than we need.
+ // {{3.5f, 3.5f}, {-3.5f, 3.5f}, {3.5f, -3.5f}, {-3.5f, -3.5f}},
+};
+
+} // namespace
+
+namespace android {
+namespace dvr {
+
+Blur::Blur(int w, int h, GLuint source_texture, GLint source_texture_target,
+ GLint target_texture_target, bool is_external, EGLDisplay display,
+ int num_blur_outputs)
+ : display_(display),
+ target_texture_target_(target_texture_target),
+ width_(w),
+ height_(h),
+ fbo_q_free_(1 + num_blur_outputs) {
+ CHECK(num_blur_outputs > 0);
+ source_fbo_ =
+ CreateFbo(w, h, source_texture, source_texture_target, is_external);
+ fbo_half_ = CreateFbo(w / 2, h / 2, 0, target_texture_target, is_external);
+ // Create the quarter res fbos.
+ for (size_t i = 0; i < fbo_q_free_.GetCapacity(); ++i)
+ fbo_q_.push_back(
+ CreateFbo(w / 4, h / 4, 0, target_texture_target, is_external));
+ scale_ = 1.0f;
+}
+
+Blur::~Blur() {
+ glFinish();
+ glDeleteFramebuffers(1, &source_fbo_.fbo);
+ glDeleteFramebuffers(1, &fbo_half_.fbo);
+ // Note: source_fbo_.texture is not deleted because it was created externally.
+ glDeleteTextures(1, &fbo_half_.texture);
+ if (fbo_half_.egl_image)
+ eglDestroyImageKHR(display_, fbo_half_.egl_image);
+ for (const auto& fbo : fbo_q_) {
+ glDeleteFramebuffers(1, &fbo.fbo);
+ glDeleteTextures(1, &fbo.texture);
+ if (fbo.egl_image)
+ eglDestroyImageKHR(display_, fbo.egl_image);
+ }
+ CHECK_GL();
+}
+
+void Blur::StartFrame() {
+ fbo_q_free_.Clear();
+ for (const auto& fbo : fbo_q_)
+ fbo_q_free_.Append(fbo);
+}
+
+GLuint Blur::DrawBlur(GLuint source_texture) {
+ CHECK(fbo_q_free_.GetSize() >= 2);
+
+ // Downsample to half w x half h.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, source_fbo_.fbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_half_.fbo);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ target_texture_target_, source_texture, 0);
+ glBlitFramebuffer(0, 0, width_, height_, 0, 0, width_ / 2, height_ / 2,
+ GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ CHECK_GL();
+
+ // Downsample to quarter w x quarter h.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_half_.fbo);
+ Fbo fbo_out = fbo_q_free_.Front();
+ fbo_q_free_.PopFront();
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_out.fbo);
+ glBlitFramebuffer(0, 0, width_ / 2, height_ / 2, 0, 0, width_ / 4,
+ height_ / 4, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ CHECK_GL();
+
+ // Blur shader is initialized statically to share between multiple blur
+ // instances.
+ static ShaderProgram kawase_prog[2];
+ int prog_index = (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) ? 1 : 0;
+ if (!kawase_prog[prog_index].IsUsable()) {
+ std::string prefix = "#version 310 es\n";
+ if (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) {
+ prefix += "#extension GL_OES_EGL_image_external_essl3 : require\n";
+ prefix += "#define APP_SAMPLER_2D samplerExternalOES\n";
+ } else {
+ prefix += "#define APP_SAMPLER_2D sampler2D\n";
+ }
+ std::string vert = prefix + screen_space_vert_shader;
+ std::string frag = prefix + kawase_blur_frag_shader;
+ kawase_prog[prog_index].Link(vert, frag);
+ CHECK_GL();
+ }
+
+ int blur_w = width_ / 4;
+ int blur_h = height_ / 4;
+ float pix_w = 1.0f / static_cast<float>(blur_w);
+ float pix_h = 1.0f / static_cast<float>(blur_h);
+ vec2 pixel_size(pix_w, pix_h);
+ constexpr int num_passes = sizeof(g_blur_samples) / sizeof(g_blur_samples[0]);
+ vec2 blur_offsets[num_passes][g_num_samples];
+ for (int i = 0; i < num_passes; ++i) {
+ for (int dir = 0; dir < g_num_samples; ++dir) {
+ blur_offsets[i][dir] = pixel_size.array() *
+ g_blur_samples[i][dir].array() * scale_;
+ }
+ }
+
+ kawase_prog[prog_index].Use();
+
+ vec4 screen_tri_strip[4] = {vec4(-1, 1, 0, 1), vec4(-1, -1, 0, 0),
+ vec4(1, 1, 1, 1), vec4(1, -1, 1, 0)};
+
+ glViewport(0, 0, blur_w, blur_h);
+ glVertexAttribPointer(POSITION_ATTR, 4, GL_FLOAT, GL_FALSE, sizeof(vec4),
+ screen_tri_strip);
+ glEnableVertexAttribArray(POSITION_ATTR);
+ CHECK_GL();
+
+ // Ping-pong between fbos from fbo_q_free_ to compute the passes.
+ Fbo fbo_in = fbo_out;
+ for (int i = 0; i < num_passes; ++i) {
+ fbo_out = fbo_q_free_.Front();
+ fbo_q_free_.PopFront();
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_out.fbo);
+ glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
+ glBindTexture(target_texture_target_, fbo_in.texture);
+ glUniform2fv(OFFSET_BINDING, 4, &blur_offsets[i][0][0]);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ CHECK_GL();
+ // Put fbo_in back into the free fbo pool.
+ fbo_q_free_.Append(fbo_in);
+ // Next iteration's in buffer is this iteration's out buffer.
+ fbo_in = fbo_out;
+ }
+ glDisableVertexAttribArray(POSITION_ATTR);
+ glBindTexture(target_texture_target_, 0);
+ glUseProgram(0);
+ glActiveTexture(GL_TEXTURE0);
+ CHECK_GL();
+ // fbo_out remains out of the fbo_q_free_ list, since the application will be
+ // using it as a texture.
+ return fbo_out.texture;
+}
+
+Blur::Fbo Blur::CreateFbo(int w, int h, GLuint source_texture, GLint tex_target,
+ bool is_external) {
+ Fbo fbo;
+ glGenFramebuffers(1, &fbo.fbo);
+ if (source_texture) {
+ fbo.texture = source_texture;
+ } else {
+ glGenTextures(1, &fbo.texture);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
+ CHECK_GL();
+
+ if (!source_texture) {
+ glBindTexture(tex_target, fbo.texture);
+ glTexParameteri(tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (is_external) {
+ fbo.egl_image =
+ CreateEglImage(display_, w, h, HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER);
+ glEGLImageTargetTexture2DOES(tex_target, fbo.egl_image);
+ } else {
+ glTexImage2D(tex_target, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ nullptr);
+ }
+ }
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_target,
+ fbo.texture, 0);
+ CHECK_GL();
+ CHECK_GL_FBO();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ return fbo;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrgraphics/debug_text.cpp b/libs/vr/libdvrgraphics/debug_text.cpp
new file mode 100644
index 0000000..1875b14
--- /dev/null
+++ b/libs/vr/libdvrgraphics/debug_text.cpp
@@ -0,0 +1,186 @@
+#include "include/private/dvr/graphics/debug_text.h"
+
+#include <algorithm>
+
+#include <private/dvr/debug.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+// 658x11 alpha texture with ascii characters starting with !: "!"#$%&'("...
+// Each character is 7x11 pixels, monospace.
+// clang-format off
+const uint8_t ascii_texture[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0x92,0x49,0x92,0x00,0x00,0x24,0xdb,0xff,0xff,0xdb,0x00,0x49,0xff,0x49,0x00,0x24,0xb6,0x00,0x00,0x6d,0xff,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0xff,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0x24,0xb6,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x6d,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0x24,0xdb,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0x6d,0x00,0x00,0x00,0x6d,0xff,0xff,0x92,0x00,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x24,0xb6,0xff,0xff,0xb6,0x00,0x00,0xff,0xff,0xdb,0x6d,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x24,0xb6,0xff,0xff,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x6d,0x6d,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x49,0xff,0x00,0x00,0xff,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x24,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x24,0xb6,0xff,0xff,0xdb,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x92,0x6d,0x00,0x00,0xdb,0x6d,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0xff,0x24,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xb6,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x24,0xb6,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0xff,0x49,0x00,0x49,0xff,0x00,0x00,0xb6,0x6d,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x92,0x00,0x24,0xff,0x00,0x00,0x24,0xdb,0x24,0x00,0xdb,0x6d,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x49,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xb6,0x00,0x92,0xff,0x00,0x00,0xff,0xdb,0x00,0x00,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xdb,0x24,0x00,0x00,0xdb,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0x6d,0x92,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0xb6,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x49,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x24,0xb6,0x00,0x00,0x00,0xb6,0x49,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0x92,0xb6,0x00,0x00,0x00,0x00,0xdb,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x24,0xb6,0xb6,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x24,0xb6,0xb6,0x24,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0xb6,0x6d,0x6d,0xff,0x92,0xdb,0x00,0x00,0x00,0xb6,0x92,0xb6,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x6d,0x6d,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xb6,0x49,0xb6,0xff,0x00,0x00,0xff,0xb6,0x49,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x92,0x00,0x92,0x6d,0x00,0x00,0x92,0x24,0x00,0x00,0xdb,0x00,0x00,0x00,0xb6,0x6d,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0xff,0xff,0xdb,0x49,0x00,0x00,0xff,0x6d,0xff,0xdb,0x24,0x00,0x00,0x00,0x92,0xff,0xff,0xdb,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xb6,0xff,0xdb,0x24,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x49,0xff,0xdb,0xb6,0xff,0x00,0x00,0xff,0x00,0x92,0xb6,0x24,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xb6,0x6d,0xdb,0x49,0x00,0x00,0xff,0x00,0x92,0xb6,0x24,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0xff,0x6d,0xff,0xdb,0x24,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xff,0x49,0xff,0xdb,0x00,0x00,0x49,0xdb,0xff,0xff,0xdb,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x24,0xdb,0xff,0x49,0x00,0x00,0x49,0xff,0x49,0xb6,0x24,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0x49,0xff,0x49,0xb6,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0xb6,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xdb,0x24,0x00,0x00,0xff,0x92,0xff,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xdb,0xff,0xdb,0x00,0x00,0x00,0xdb,0x6d,0x00,0x92,0xff,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xff,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0xdb,0x00,0xdb,0xdb,0x49,0xff,0x00,0x00,0x00,0xff,0x24,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x24,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x6d,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x6d,0xff,0x6d,0xff,0x00,0x00,0xff,0x24,0xdb,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0x92,0x00,0x00,0x00,0x24,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0xdb,0x24,0x00,0x00,0x6d,0x6d,0xff,0x24,0xb6,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x92,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0xff,0x92,0x00,0x49,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x24,0xff,0x00,0x00,0x00,0xff,0xb6,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x49,0xff,0x49,0xdb,0x00,0x00,0xff,0xb6,0x00,0x49,0xdb,0x00,0x00,0xb6,0x92,0x00,0x92,0xb6,0x00,0x00,0xff,0x6d,0x00,0x92,0xb6,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0xb6,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x6d,0x92,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0x00,0xdb,0x24,0xdb,0x00,0x00,0x00,0x6d,0x6d,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xb6,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0xdb,0x49,0x00,0x00,0x00,0x24,0xb6,0x49,0xff,0x49,0x49,0xdb,0x49,0xdb,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0xff,0x00,0xb6,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x24,0x92,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0xff,0x92,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0xb6,0x6d,0x00,0x6d,0xb6,0x00,0x00,0x24,0xdb,0xff,0x92,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0xb6,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x24,0xb6,0xdb,0x49,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0xff,0x00,0xff,0xdb,0x24,0xff,0x00,0x00,0x24,0xdb,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x92,0xb6,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x6d,0xb6,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0xff,0x24,0xff,0x00,0x00,0xff,0x00,0xb6,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x24,0xff,0x00,0x00,0x00,0x49,0xb6,0xff,0x6d,0x92,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x92,0xdb,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x24,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0xdb,0x24,0x00,0x00,0xb6,0x49,0x92,0x49,0xb6,0x00,0x00,0x00,0x49,0xff,0x49,0x00,0x00,0x00,0x00,0xdb,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x49,0xb6,0x92,0x24,0x00,0x92,0x00
+ ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x24,0xb6,0xff,0x00,0x00,0x24,0xb6,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0xff,0x6d,0xb6,0x00,0x00,0x6d,0xff,0xff,0xff,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x49,0xb6,0xff,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xb6,0xb6,0x00,0x00,0x00,0x00,0xdb,0xdb,0xdb,0x6d,0x00,0x00,0x00,0xb6,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x92,0x24,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x92,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x6d,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x49,0xdb,0x00,0x00,0x00,0x92,0x92,0xff,0xdb,0x92,0x00,0x00,0x00,0x49,0xff,0x49,0x00,0x00,0x00,0x00,0xb6,0x49,0xdb,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x92,0x00,0x24,0x92,0xb6,0x49,0x00
+ ,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x6d,0xdb,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0xb6,0xdb,0x6d,0x00,0x24,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x6d,0xb6,0x00,0x00,0x6d,0x92,0x00,0x24,0xdb,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x24,0xdb,0x49,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xdb,0x49,0xb6,0xdb,0xdb,0x00,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xff,0x00,0x00,0x92,0xdb,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x49,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0xdb,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0xb6,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x6d,0xdb,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x00,0xff,0x92,0xff,0x49,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x92,0xff,0x00,0x00,0xff,0x92,0x00,0x49,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x6d,0xb6,0x00,0x00,0xff,0x6d,0x00,0x92,0xb6,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0xff,0x49,0x00,0xb6,0xff,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x6d,0xff,0xb6,0xff,0x49,0x00,0x00,0x00,0xdb,0x24,0xdb,0x00,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0xb6,0x24,0x00,0x49,0xff,0x49,0x24,0xb6,0xff,0xdb,0x49,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xdb,0x00,0x00,0x00,0x00,0xdb,0xdb,0xff,0x49,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0xff,0xff,0xff,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xff,0xff,0xff,0xb6,0x00,0x00,0x00,0x24,0xb6,0xff,0xff,0xb6,0x00,0x00,0xff,0xff,0xdb,0x6d,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0x92,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0xdb,0xff,0xdb,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x49,0x92,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xdb,0xff,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0x6d,0x92,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0xdb,0x49,0xff,0x49,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0xdb,0x6d,0xff,0x00,0x00,0xff,0x6d,0xff,0xdb,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0xb6,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0x6d,0xff,0xdb,0x00,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0x00,0x00,0x49,0xb6,0x92,0x24,0xff,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0xff,0x24,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x92,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+// clang-format on
+
+constexpr char kTextureFirstChar = '!';
+constexpr char kTextureLastChar = '~';
+constexpr int kTextureTotalChars = kTextureLastChar - kTextureFirstChar + 1;
+constexpr int kCharWidth = 7;
+constexpr int kCharHeight = 11;
+constexpr int kTextureWidth = kTextureTotalChars * kCharWidth;
+constexpr int kTextureHeight = kCharHeight;
+
+std::string vert_shader = SHADER0([]() { // NOLINT
+ layout(location = 0) in vec2 position;
+ layout(location = 1) in vec2 uv;
+
+ out vec2 texCoords;
+
+ void main() {
+ gl_Position = vec4(position, 0.0, 1.0f);
+ texCoords = vec2(uv.x, 1.0 - uv.y);
+ }
+});
+
+// Uniform locations used in the following shader
+#define UNIFORM_LOCATION_DIGITS 0
+#define UNIFORM_LOCATION_COLOR 1
+
+std::string frag_shader = SHADER0([]() { // NOLINT
+ precision mediump float;
+
+ out vec4 color;
+ in vec2 texCoords;
+ layout(location = 0) uniform sampler2D digitsTexture;
+ layout(location = 1) uniform vec4 mixColor;
+
+ void main() {
+ float alpha = texture(digitsTexture, texCoords).r;
+ color = vec4(mixColor.rgb, alpha * mixColor.a);
+ }
+});
+
+} // anonymous namespace
+
+void DebugText::SetViewportSize(int viewport_width, int viewport_height) {
+ pixel_size_screen_space_ = vec2(2.0f / static_cast<float>(viewport_width),
+ 2.0f / static_cast<float>(viewport_height));
+}
+
+DebugText::DebugText(int max_digits, int viewport_width, int viewport_height) {
+ max_digits_ = max_digits;
+ SetViewportSize(viewport_width, viewport_height);
+
+ shader_.Link(vert_shader, frag_shader);
+ shader_.Use();
+ glUniform1i(UNIFORM_LOCATION_DIGITS, 0);
+
+ // Num quads * 6 vertices per quad.
+ mesh_.SetVertices(max_digits * 6, nullptr, GL_TRIANGLES, GL_DYNAMIC_DRAW);
+
+ glGenTextures(1, &texture_);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, kTextureWidth, kTextureHeight, 0,
+ GL_RED, GL_UNSIGNED_BYTE, ascii_texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ CHECK_GL();
+}
+
+DebugText::~DebugText() {}
+
+void DebugText::Draw(float x, float y, float scale, float r, float g, float b,
+ float a, const char* str, float stereo_offset,
+ uint8_t axis) {
+ assert(axis < 2);
+ shader_.Use();
+ glUniform4f(UNIFORM_LOCATION_COLOR, r, g, b, a);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+ CHECK_GL();
+
+ float px = x;
+ float py = y;
+ float x_advance = scale * static_cast<float>(kCharWidth);
+ float y_advance = 0.0f;
+ float x_height = 0.0f;
+ float y_height = scale * static_cast<float>(kCharHeight);
+ if (axis) {
+ std::swap(x_advance, y_advance);
+ std::swap(x_height, y_height);
+ }
+ x_advance *= pixel_size_screen_space_[0];
+ x_height *= pixel_size_screen_space_[0];
+ y_advance *= pixel_size_screen_space_[1];
+ y_height *= pixel_size_screen_space_[1];
+ int max_digits = stereo_offset != 0.0f ? max_digits_ / 2 : max_digits_;
+ int len = std::min(max_digits, static_cast<int>(strlen(str)));
+
+ int num_quads = stereo_offset != 0.0f ? len * 2 : len;
+ std::tuple<vec2, vec2>* vbo =
+ mesh_.Map(GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT, num_quads * 6);
+
+ int v = 0;
+ for (int i = 0; i < len; ++i) {
+ char digit = str[i];
+ if (digit == '\n') {
+ if (axis == 0) {
+ py += y_height;
+ px = x;
+ } else {
+ px -= x_height;
+ py = y;
+ }
+ continue;
+ }
+ if (digit < kTextureFirstChar || digit > kTextureLastChar) {
+ px += x_advance;
+ py += y_advance;
+ continue;
+ }
+
+ int tex_digit = digit - kTextureFirstChar;
+
+ // Add screenspace tri vertices in CCW order starting with bottom left.
+ float tx =
+ static_cast<float>(tex_digit) / static_cast<float>(kTextureTotalChars);
+ float tx2 = tx + 1.0f / static_cast<float>(kTextureTotalChars);
+ vbo[v * 6 + 0] = {vec2(px, py), vec2(tx, 0.0f)};
+ vbo[v * 6 + 1] = {vec2(px + x_advance, py + y_advance), vec2(tx2, 0.0f)};
+ vbo[v * 6 + 2] = {
+ vec2(px + x_advance + x_height, py + y_advance + y_height),
+ vec2(tx2, 1.0f)};
+ vbo[v * 6 + 3] = vbo[v * 6 + 0];
+ vbo[v * 6 + 4] = vbo[v * 6 + 2];
+ vbo[v * 6 + 5] = {vec2(px + x_height, py + y_height), vec2(tx, 1.0f)};
+ px += x_advance;
+ py += y_advance;
+ ++v;
+ }
+
+ if (stereo_offset != 0.0f) {
+ int num_chars = v;
+ for (int i = 0; i < num_chars; ++i) {
+ for (int j = 0; j < 6; ++j) {
+ vbo[v * 6 + j] = vbo[i * 6 + j];
+ // The 0th tuple element is the vertex position vec2.
+ std::get<0>(vbo[v * 6 + j])[axis] += stereo_offset;
+ }
+ ++v;
+ }
+ }
+
+ mesh_.Unmap();
+
+ mesh_.Draw(v * 6);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrgraphics/egl_image.cpp b/libs/vr/libdvrgraphics/egl_image.cpp
new file mode 100644
index 0000000..26d68cd
--- /dev/null
+++ b/libs/vr/libdvrgraphics/egl_image.cpp
@@ -0,0 +1,22 @@
+#include "include/private/dvr/graphics/egl_image.h"
+
+#include <hardware/gralloc.h>
+
+#include <memory>
+
+#include <private/dvr/native_buffer.h>
+
+namespace android {
+namespace dvr {
+
+EGLImageKHR CreateEglImage(EGLDisplay dpy, int width, int height, int format,
+ int usage) {
+ auto image = std::make_shared<IonBuffer>(width, height, format, usage);
+
+ return eglCreateImageKHR(
+ dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ static_cast<ANativeWindowBuffer*>(new NativeBuffer(image)), nullptr);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrgraphics/gpu_profiler.cpp b/libs/vr/libdvrgraphics/gpu_profiler.cpp
new file mode 100644
index 0000000..d252a34
--- /dev/null
+++ b/libs/vr/libdvrgraphics/gpu_profiler.cpp
@@ -0,0 +1,248 @@
+#include "include/private/dvr/graphics/gpu_profiler.h"
+
+#include <cutils/log.h>
+
+#include <private/dvr/clock_ns.h>
+
+namespace android {
+namespace dvr {
+
+static int64_t AdjustTimerQueryToNs(int64_t gpu_time) { return gpu_time; }
+
+void GpuProfiler::TimerData::reset() {
+ total_elapsed_ns = 0;
+ num_events = 0;
+}
+
+void GpuProfiler::TimerData::print(const char* name) const {
+ ALOGI("GPU_TIME[%s]: %f ms", name,
+ (float)((double)total_elapsed_ns / 1000000.0 / (double)num_events));
+}
+
+// Enter a scope, records the timestamp for later matching with leave.
+void GpuProfiler::TimerData::enter(int64_t timestamp_ns) {
+ enter_timestamp_ns = timestamp_ns;
+}
+
+// Compute the elapsed time for the scope.
+void GpuProfiler::TimerData::leave(int64_t timestamp_ns, const char* name,
+ std::weak_ptr<int64_t> duration_ns,
+ int print_period) {
+ int64_t elapsed = timestamp_ns - enter_timestamp_ns;
+ if (elapsed > 1000 * 1000 * 1000) {
+ // More than one second, drop it as invalid data.
+ return;
+ }
+ if (auto out_ns = duration_ns.lock()) {
+ *out_ns = elapsed;
+ }
+ total_elapsed_ns += elapsed;
+ if (print_period > 0 && ++num_events >= print_period) {
+ print(name);
+ reset();
+ }
+}
+
+GpuProfiler* GpuProfiler::Get() {
+ static GpuProfiler* profiler = new GpuProfiler();
+ return profiler;
+}
+
+GpuProfiler::GpuProfiler()
+ : enable_gpu_tracing_(true),
+ sync_with_cpu_time_(false),
+ gl_timer_offset_ns_(0) {
+ SyncGlTimebase();
+}
+
+GpuProfiler::~GpuProfiler() {}
+
+bool GpuProfiler::IsGpuProfilingSupported() const {
+ // TODO(jbates) check for GL_EXT_disjoint_timer_query
+ return true;
+}
+
+GLuint GpuProfiler::TryAllocateGlQueryId() {
+ GLuint query_id = 0;
+ if (gl_timer_query_id_pool_.empty()) {
+ glGenQueries(1, &query_id);
+ } else {
+ query_id = gl_timer_query_id_pool_.top();
+ gl_timer_query_id_pool_.pop();
+ }
+ return query_id;
+}
+
+void GpuProfiler::EnterGlScope(const char* scope_name) {
+ GLuint query_id = TryAllocateGlQueryId();
+ if (query_id != 0) {
+ glQueryCounter(query_id, GL_TIMESTAMP_EXT);
+ pending_gpu_queries_.push_back(
+ GpuTimerQuery(GetSystemClockNs(), scope_name, std::weak_ptr<int64_t>(),
+ -1, query_id, GpuTimerQuery::kQueryBeginScope));
+ }
+}
+
+void GpuProfiler::LeaveGlScope(const char* scope_name,
+ std::weak_ptr<int64_t> duration_ns,
+ int print_period) {
+ GLuint query_id = TryAllocateGlQueryId();
+ if (query_id != 0) {
+ glQueryCounter(query_id, GL_TIMESTAMP_EXT);
+ pending_gpu_queries_.push_back(
+ GpuTimerQuery(GetSystemClockNs(), scope_name, duration_ns, print_period,
+ query_id, GpuTimerQuery::kQueryEndScope));
+ }
+}
+
+void GpuProfiler::SyncGlTimebase() {
+ if (!sync_with_cpu_time_) {
+ return;
+ }
+
+ // Clear disjoint error status.
+ // This error status indicates that we need to ignore the result of the
+ // timer query because of some kind of disjoint GPU event such as heat
+ // throttling.
+ GLint disjoint = 0;
+ glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
+
+ // Try to get the current GL timestamp. Since the GPU can supposedly fail to
+ // produce a timestamp occasionally we try a few times before giving up.
+ int attempts_remaining = 3;
+ do {
+ GLint64 gl_timestamp = 0;
+ glGetInteger64v(GL_TIMESTAMP_EXT, &gl_timestamp);
+ gl_timestamp = AdjustTimerQueryToNs(gl_timestamp);
+
+ // Now get the CPU timebase.
+ int64_t cpu_timebase_ns = static_cast<int64_t>(GetSystemClockNs());
+
+ disjoint = 0;
+ glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
+ if (!disjoint) {
+ gl_timer_offset_ns_ = cpu_timebase_ns - gl_timestamp;
+ break;
+ }
+ ALOGW("WARNING: Skipping disjoint GPU timestamp");
+ } while (--attempts_remaining > 0);
+
+ if (attempts_remaining == 0) {
+ ALOGE("ERROR: Failed to sync GL timebase due to disjoint results\n");
+ gl_timer_offset_ns_ = 0;
+ }
+}
+
+void GpuProfiler::QueryFrameBegin() {
+ GLuint begin_frame_id = TryAllocateGlQueryId();
+ if (begin_frame_id != 0) {
+ glQueryCounter(begin_frame_id, GL_TIMESTAMP_EXT);
+ pending_gpu_queries_.push_back(
+ GpuTimerQuery(GetSystemClockNs(), 0, std::weak_ptr<int64_t>(), -1,
+ begin_frame_id, GpuTimerQuery::kQueryBeginFrame));
+ }
+}
+
+void GpuProfiler::PollGlTimerQueries() {
+ if (!enabled()) {
+ return;
+ }
+
+#ifdef ENABLE_DISJOINT_TIMER_IGNORING
+ bool has_checked_disjoint = false;
+ bool was_disjoint = false;
+#endif
+ for (;;) {
+ if (pending_gpu_queries_.empty()) {
+ // No queries pending.
+ return;
+ }
+
+ GpuTimerQuery query = pending_gpu_queries_.front();
+
+ GLint available = 0;
+ glGetQueryObjectiv(query.query_id, GL_QUERY_RESULT_AVAILABLE_EXT,
+ &available);
+ if (!available) {
+ // No queries available.
+ return;
+ }
+
+ // Found an available query, remove it from pending queue.
+ pending_gpu_queries_.pop_front();
+ gl_timer_query_id_pool_.push(query.query_id);
+
+#ifdef ENABLE_DISJOINT_TIMER_IGNORING
+ if (!has_checked_disjoint) {
+ // Check if we need to ignore the result of the timer query because
+ // of some kind of disjoint GPU event such as heat throttling.
+ // If so, we ignore all events that are available during this loop.
+ has_checked_disjoint = true;
+ GLint disjoint_occurred = 0;
+ glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_occurred);
+ was_disjoint = !!disjoint_occurred;
+ if (was_disjoint) {
+ ALOGW("Skipping disjoint GPU events");
+ }
+ }
+
+ if (was_disjoint) {
+ continue;
+ }
+#endif
+
+ GLint64 timestamp_ns = 0;
+ glGetQueryObjecti64v(query.query_id, GL_QUERY_RESULT_EXT, ×tamp_ns);
+ timestamp_ns = AdjustTimerQueryToNs(timestamp_ns);
+
+ int64_t adjusted_timestamp_ns;
+
+ if (sync_with_cpu_time_) {
+ adjusted_timestamp_ns = timestamp_ns + gl_timer_offset_ns_;
+
+ if (query.type == GpuTimerQuery::kQueryBeginFrame ||
+ query.type == GpuTimerQuery::kQueryBeginScope) {
+ if (adjusted_timestamp_ns < query.timestamp_ns) {
+ // GPU clock is behind, adjust our offset to correct it.
+ gl_timer_offset_ns_ += query.timestamp_ns - adjusted_timestamp_ns;
+ adjusted_timestamp_ns = query.timestamp_ns;
+ }
+ }
+ } else {
+ adjusted_timestamp_ns = timestamp_ns;
+ }
+
+ switch (query.type) {
+ case GpuTimerQuery::kQueryBeginFrame:
+ break;
+ case GpuTimerQuery::kQueryBeginScope:
+ events_[query.scope_name].enter(adjusted_timestamp_ns);
+ break;
+ case GpuTimerQuery::kQueryEndScope:
+ events_[query.scope_name].leave(adjusted_timestamp_ns, query.scope_name,
+ query.duration_ns, query.print_period);
+ break;
+ }
+ }
+}
+
+void GpuProfiler::FinishGlTimerQueries() {
+ if (!enabled()) {
+ return;
+ }
+
+ glFlush();
+ PollGlTimerQueries();
+ int max_iterations = 100;
+ while (!pending_gpu_queries_.empty()) {
+ if (--max_iterations <= 0) {
+ ALOGE("Error: GL timer queries failed to finish.");
+ break;
+ }
+ PollGlTimerQueries();
+ usleep(1000);
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/blur.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/blur.h
new file mode 100644
index 0000000..c1c2b91
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/blur.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_GRAPHICS_BLUR_H_
+#define ANDROID_DVR_GRAPHICS_BLUR_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <private/dvr/ring_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class Blur {
+ public:
+ // Construct a blur kernel for GL that works on source textures of the given
+ // size. The given source_texture is configured for linear filtering.
+ // |source_texture_target| is for |source_texture| while
+ // |target_texture_target| is used for all the intermediate and output
+ // buffers.
+ // |num_blur_outputs| determines how many blurs this instance can be used for
+ // in a single frame.
+ Blur(int w, int h, GLuint source_texture, GLint source_texture_target,
+ GLint target_texture_target, bool is_external, EGLDisplay display,
+ int num_blur_outputs);
+ ~Blur();
+
+ // Place all output textures back into the FBO pool for a new frame.
+ // Call this at the start of each frame before doing one or more blurs.
+ void StartFrame();
+
+ // Draw a multipass blur from the given source_texture. The resulting texture
+ // is returned. The given source_texture is configured for linear filtering.
+ // A segfault will occur if the application calls DrawBlur more times than
+ // |num_blur_outputs| without calling StartFrame.
+ // It is up to the calling code to change the framebuffer after this method.
+ GLuint DrawBlur(GLuint source_texture);
+
+ float width() const { return width_; }
+ float height() const { return height_; }
+ float scale() const { return scale_; }
+
+ // Set the scale of the blur, usually between 0 and 1. This is only useful for
+ // animation.
+ // At the steady state, the scale should be set to 1. To change the steady
+ // state blur appearance, the kernel patterns in DrawBlur should be modified
+ // instead of using scale.
+ void set_scale(float scale) { scale_ = scale; }
+
+ // Animate the blur by |delta|. Clamp the result between |low| and |high|.
+ // Recommended range is between 0 and 1, but other values will also work.
+ void animate(float delta, float low, float high) {
+ scale_ += delta;
+ scale_ = std::min(high, std::max(low, scale_));
+ }
+
+ private:
+ struct Fbo {
+ Fbo() : fbo(0), renderbuffer(0), texture(0), egl_image(0) {}
+ GLuint fbo;
+ GLuint renderbuffer;
+ GLuint texture;
+ EGLImageKHR egl_image;
+ };
+
+ Fbo CreateFbo(int w, int h, GLuint source_texture, GLint tex_target,
+ bool is_external);
+
+ // EGL display for when target texture format is EGL image.
+ EGLDisplay display_;
+ GLint target_texture_target_;
+ int width_;
+ int height_;
+ Fbo source_fbo_;
+ Fbo fbo_half_;
+ std::vector<Fbo> fbo_q_;
+ RingBuffer<Fbo> fbo_q_free_;
+ float scale_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_BLUR_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/debug_text.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/debug_text.h
new file mode 100644
index 0000000..bbe891b
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/debug_text.h
@@ -0,0 +1,44 @@
+#ifndef ANDROID_DVR_GRAPHICS_FPS_GRAPH_H
+#define ANDROID_DVR_GRAPHICS_FPS_GRAPH_H
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+namespace android {
+namespace dvr {
+
+// Debug text class that draws small text with Open GL.
+class DebugText {
+ public:
+ DebugText(int max_digits, int viewport_width, int viewport_height);
+ ~DebugText();
+
+ void SetViewportSize(int viewport_width, int viewport_height);
+
+ // Draw text at given screen-space location, scale and color.
+ // A |scale| of 1.0 means 1:1 pixel mapping with current viewport size.
+ // If |stereo_offset| is not zero, the string will be rendered again
+ // with the given offset for stereo rendering. The stereo axis can be on
+ // screenspace x or y axis, which is given by |axis| as 0 or 1,
+ // respectively. |axis| also determines the direction that text is rendered.
+ void Draw(float x, float y, float scale, float r, float g, float b, float a,
+ const char* str, float stereo_offset, uint8_t axis);
+
+ // Helper that draws green text at render target resolution.
+ void Draw(float x, float y, const char* str, float stereo_offset,
+ uint8_t axis) {
+ Draw(x, y, 1.0f, 0, 1, 0, 1, str, stereo_offset, axis);
+ }
+
+ private:
+ int max_digits_;
+ vec2 pixel_size_screen_space_;
+ ShaderProgram shader_;
+ GLuint texture_;
+ Mesh<vec2, vec2> mesh_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_FPS_GRAPH_H
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/egl_image.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/egl_image.h
new file mode 100644
index 0000000..59de61e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/egl_image.h
@@ -0,0 +1,21 @@
+#ifndef ANDROID_DVR_GRAPHICS_EGL_IMAGE_H_
+#define ANDROID_DVR_GRAPHICS_EGL_IMAGE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+namespace android {
+namespace dvr {
+
+// Create an EGLImage with texture storage defined by the given format and
+// usage flags.
+// For example, to create an RGBA texture for rendering to, specify:
+// format = HAL_PIXEL_FORMAT_RGBA_8888;
+// usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER;
+EGLImageKHR CreateEglImage(EGLDisplay dpy, int width, int height, int format,
+ int usage);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_EGL_IMAGE_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h
new file mode 100644
index 0000000..2905d00
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h
@@ -0,0 +1,215 @@
+#ifndef ANDROID_DVR_GPU_PROFILER_H_
+#define ANDROID_DVR_GPU_PROFILER_H_
+
+// This file contains classes and macros related to run-time performance
+// profiling of GPU processing.
+
+#include <deque>
+#include <map>
+#include <memory>
+#include <stack>
+#include <vector>
+
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+namespace android {
+namespace dvr {
+
+// While enabled, GL commands will be submitted each frame to query timestamps
+// of GPU workloads that have been traced using the ION_PROFILE_GPU macro
+// defined below.
+//
+// Basic workflow:
+// - have the app framework call PollGlTimerQueries at the start of each frame.
+// - place ION_PROFILE_GPU("MyGlWorkload") at the start of code scopes where
+// GL draw commands are performed that you want to trace.
+class GpuProfiler {
+ public:
+ // Gets the GpuProfiler singleton instance.
+ static GpuProfiler* Get();
+
+ GpuProfiler();
+ ~GpuProfiler();
+
+ bool IsGpuProfilingSupported() const;
+
+ // Enables runtime GPU tracing. While enabled, GL commands will be submitted
+ // each frame to query timestamps of GPU workloads that have been traced using
+ // one of the TRACE_GPU* macros defined below.
+ void SetEnableGpuTracing(bool enabled) { enable_gpu_tracing_ = enabled; }
+
+ bool enabled() const { return enable_gpu_tracing_; }
+
+ // Attempt to keep the GPU times in sync with CPU times.
+ void SetEnableSyncCpuTime(bool enabled) { sync_with_cpu_time_ = enabled; }
+
+ // When sync cpu time is enabled because of mobile GPU timer query issues,
+ // it can sometimes help to put a beginning timer query at the start of the
+ // frame to sync the CPU time when GPU work begins.
+ void QueryFrameBegin();
+
+ // Polls (non-blocking) for completed GL timer query data and adds events into
+ // the trace buffer. Must call once close to the start of each frame.
+ void PollGlTimerQueries();
+
+ // Call glFinish and process all pending timer queries.
+ void FinishGlTimerQueries();
+
+ // Records the beginning of a scoped GL trace event.
+ void EnterGlScope(const char* scope_name);
+
+ // Records the end of a scoped GL trace event.
+ void LeaveGlScope(const char* scope_name, std::weak_ptr<int64_t> duration_ns,
+ int print_period);
+
+ private:
+ // Data to queue the pending GPU timer queries that need to be polled
+ // for completion.
+ struct GpuTimerQuery {
+ enum QueryType {
+ kQueryBeginFrame,
+ kQueryBeginScope,
+ kQueryEndScope,
+ };
+
+ // scope_id is only required for kQueryBeginScope query types.
+ GpuTimerQuery(int64_t timestamp_ns, const char* scope_name,
+ std::weak_ptr<int64_t> duration_ns, int print_period,
+ GLuint query_id, QueryType type)
+ : timestamp_ns(timestamp_ns),
+ scope_name(scope_name),
+ duration_ns(duration_ns),
+ print_period(print_period),
+ query_id(query_id),
+ type(type) {}
+
+ int64_t timestamp_ns;
+ const char* scope_name;
+ std::weak_ptr<int64_t> duration_ns;
+ int print_period;
+ GLuint query_id;
+ QueryType type;
+ };
+
+ // Struct that tracks timing data for a particular trace scope.
+ struct TimerData {
+ void reset();
+
+ // Print the profiling data.
+ void print(const char* name) const;
+
+ // Enter a scope, records the timestamp for later matching with leave.
+ void enter(int64_t timestamp_ns);
+
+ // Compute the elapsed time for the scope.
+ void leave(int64_t timestamp_ns, const char* name,
+ std::weak_ptr<int64_t> duration_ns, int print_period);
+
+ int64_t total_elapsed_ns = 0;
+ int64_t enter_timestamp_ns = 0;
+ int num_events = 0;
+ };
+
+ // Synchronises the GL timebase with the CallTraceManager timebase.
+ void SyncGlTimebase();
+
+ // Returns a GL timer query ID if possible. Otherwise returns 0.
+ GLuint TryAllocateGlQueryId();
+
+ // Setting for enabling GPU tracing.
+ bool enable_gpu_tracing_;
+
+ // Setting for synchronizing GPU timestamps with CPU time.
+ bool sync_with_cpu_time_;
+
+ // Nanosecond offset to the GL timebase to compute the CallTraceManager time.
+ int64_t gl_timer_offset_ns_;
+
+ std::map<const char*, TimerData> events_;
+
+ // For GPU event TraceRecords, this tracks the pending queries that will
+ // be asynchronously polled (in order) and then added to the TraceRecorder
+ // buffer with the GPU timestamps.
+ std::deque<GpuTimerQuery> pending_gpu_queries_;
+
+ // Available ids for use with GLTimerQuery as needed. This will generally
+ // reach a steady state after a few frames. Always push and pop from the back
+ // to avoid shifting the vector.
+ std::stack<GLuint, std::vector<GLuint> > gl_timer_query_id_pool_;
+};
+
+// Traces the GPU start and end times of the GL commands submitted in the
+// same scope. Typically used via the TRACE_GPU macro.
+class ScopedGlTracer {
+ public:
+ ScopedGlTracer(const char* name, std::weak_ptr<int64_t> duration_ns,
+ int print_period, bool finish)
+ : name_(name),
+ duration_ns_(duration_ns),
+ print_period_(print_period),
+ is_finish_(finish) {
+ GpuProfiler* profiler = GpuProfiler::Get();
+ if (profiler->enabled()) {
+ profiler->EnterGlScope(name);
+ }
+ }
+
+ ~ScopedGlTracer() {
+ GpuProfiler* profiler = GpuProfiler::Get();
+ if (profiler->enabled()) {
+ profiler->LeaveGlScope(name_, duration_ns_, print_period_);
+ if (is_finish_) {
+ GpuProfiler::Get()->FinishGlTimerQueries();
+ }
+ }
+ }
+
+ private:
+ const char* name_;
+ std::weak_ptr<int64_t> duration_ns_;
+ int print_period_;
+ bool is_finish_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#define PROFILING_PASTE1(x, y) x##y
+#define PROFILING_PASTE2(x, y) PROFILING_PASTE1(x, y)
+#define PROFILING_PASTE3(x) PROFILING_PASTE2(x, __LINE__)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. Specify the number of frames
+// to wait before printing an average result in the num_frames_period argument.
+#define TRACE_GPU_PRINT(group_name, num_frames_period) \
+ (void)group_name " must be a literal string."; \
+ android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+ group_name, std::weak_ptr<int64_t>(), num_frames_period, false)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. The duration parameter
+// is a weak_ptr to a int64_t that will receive duration values asynchronously
+// during calls to PollGlTimerQueries.
+#define TRACE_GPU(group_name, duration_ns_weak_ptr) \
+ (void)group_name " must be a literal string."; \
+ android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+ group_name, duration_ns_weak_ptr, -1, false)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. Specify the number of frames
+// to wait before printing an average result in the num_frames_period argument.
+#define TRACE_GPU_PRINT_FINISH(group_name) \
+ (void)group_name " must be a literal string."; \
+ android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+ group_name, std::weak_ptr<int64_t>(), 1, true)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. The duration parameter
+// is a weak_ptr to a int64_t that will receive duration values asynchronously
+// during calls to PollGlTimerQueries.
+#define TRACE_GPU_FINISH(group_name, duration_ns_weak_ptr) \
+ (void)group_name " must be a literal string."; \
+ android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+ group_name, duration_ns_weak_ptr, -1, true)
+
+#endif // ANDROID_DVR_GPU_PROFILER_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/indexed_mesh.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/indexed_mesh.h
new file mode 100644
index 0000000..7e74a75
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/indexed_mesh.h
@@ -0,0 +1,154 @@
+#ifndef ANDROID_DVR_GRAPHICS_INDEXED_MESH_H_
+#define ANDROID_DVR_GRAPHICS_INDEXED_MESH_H_
+
+#include <private/dvr/graphics/vertex_attributes.h>
+#include <private/dvr/types.h>
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <tuple>
+
+namespace android {
+namespace dvr {
+
+namespace Details {
+
+// We can have 16 and 32bit indices.
+template <typename T>
+GLenum GetIndexType();
+template <>
+inline GLenum GetIndexType<uint16_t>() {
+ return GL_UNSIGNED_SHORT;
+}
+template <>
+inline GLenum GetIndexType<uint32_t>() {
+ return GL_UNSIGNED_INT;
+}
+
+} // namespace Details
+
+template <typename INDEX_TYPE, typename... Attributes>
+class IndexedMesh {
+ public:
+ static const int attribute_size = sizeof(std::tuple<Attributes...>);
+
+ IndexedMesh() {}
+ IndexedMesh(INDEX_TYPE number_of_vertices, const void* vertices,
+ INDEX_TYPE number_of_indices, const void* indices) {
+ SetVertices(number_of_vertices, vertices, number_of_indices, indices);
+ }
+
+ IndexedMesh(INDEX_TYPE number_of_vertices, const void* vertices,
+ INDEX_TYPE number_of_indices, const void* indices,
+ GLenum element_type) {
+ SetVertices(number_of_vertices, vertices, number_of_indices, indices,
+ element_type);
+ }
+
+ IndexedMesh(IndexedMesh&& to_move) { Swap(to_move); }
+
+ ~IndexedMesh() { DeleteGLData(); }
+
+ IndexedMesh& operator=(IndexedMesh&& to_move) {
+ Swap(to_move);
+ return *this;
+ }
+
+ operator bool() const { return mesh_vbo_ != 0; }
+
+ void Swap(IndexedMesh& to_swap) {
+ std::swap(mesh_vbo_, to_swap.mesh_vbo_);
+ std::swap(mesh_vao_, to_swap.mesh_vao_);
+ std::swap(mesh_ibo_, to_swap.mesh_ibo_);
+ std::swap(number_of_indices_, to_swap.number_of_indices_);
+ std::swap(element_type_, to_swap.element_type_);
+ }
+
+ void Draw() {
+ if (!mesh_vbo_)
+ return;
+
+ glBindVertexArray(mesh_vao_);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_);
+
+ glDrawElements(element_type_, number_of_indices_,
+ Details::GetIndexType<INDEX_TYPE>(), nullptr);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ }
+
+ void SetVertices(INDEX_TYPE number_of_vertices, const void* vertices,
+ INDEX_TYPE number_of_indices, const void* indices,
+ GLenum element_type) {
+ element_type_ = element_type;
+ SetVertices(number_of_vertices, vertices, number_of_indices, indices);
+ }
+
+ void SetVertices(INDEX_TYPE number_of_vertices, const void* vertices,
+ INDEX_TYPE number_of_indices, const void* indices) {
+ DeleteGLData();
+ number_of_indices_ = number_of_indices;
+ glGenBuffers(1, &mesh_vbo_);
+ glGenVertexArrays(1, &mesh_vao_);
+ glGenBuffers(1, &mesh_ibo_);
+ glBindVertexArray(mesh_vao_);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_);
+ glBufferData(GL_ARRAY_BUFFER, attribute_size * number_of_vertices, vertices,
+ GL_STATIC_DRAW);
+
+ SetupAttributes();
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(INDEX_TYPE) * number_of_indices_, indices,
+ GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ }
+
+ size_t GetAttributesSize() const { return attribute_size; }
+
+ private:
+ IndexedMesh(const IndexedMesh&) = delete;
+ IndexedMesh& operator=(const IndexedMesh&) = delete;
+
+ void DeleteGLData() {
+ if (mesh_vbo_) {
+ glDeleteBuffers(1, &mesh_vbo_);
+ glDeleteVertexArrays(1, &mesh_vao_);
+ glDeleteBuffers(1, &mesh_ibo_);
+ mesh_vbo_ = 0;
+ mesh_vao_ = 0;
+ mesh_ibo_ = 0;
+ number_of_indices_ = 0;
+ }
+ }
+
+ void SetupAttributes() {
+ const auto size = std::tuple_size<std::tuple<Attributes...>>::value;
+ Details::VertexAttribHelper<size - 1, Attributes...>{}();
+ }
+
+ private:
+ GLuint mesh_vbo_ = 0;
+ GLuint mesh_vao_ = 0;
+ GLuint mesh_ibo_ = 0;
+ INDEX_TYPE number_of_indices_ = 0;
+
+ GLenum element_type_ = GL_TRIANGLES;
+};
+
+template <typename... Attributes>
+using Indexed16Mesh = IndexedMesh<uint16_t, Attributes...>;
+
+template <typename... Attributes>
+using Indexed32Mesh = IndexedMesh<uint32_t, Attributes...>;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_INDEXED_MESH_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/mesh.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/mesh.h
new file mode 100644
index 0000000..45bc108
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/mesh.h
@@ -0,0 +1,128 @@
+#ifndef ANDROID_DVR_GRAPHICS_MESH_H_
+#define ANDROID_DVR_GRAPHICS_MESH_H_
+
+#include <private/dvr/graphics/vertex_attributes.h>
+#include <private/dvr/types.h>
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <tuple>
+
+namespace android {
+namespace dvr {
+
+template <typename... Attributes>
+class Mesh {
+ public:
+ static const int attribute_size = sizeof(std::tuple<Attributes...>);
+
+ Mesh() {}
+
+ Mesh(uint32_t number_of_vertices, const void* vertices) {
+ SetVertices(number_of_vertices, vertices);
+ }
+
+ Mesh(uint32_t number_of_vertices, const void* vertices, GLenum element_type) {
+ SetVertices(number_of_vertices, vertices, element_type);
+ }
+
+ Mesh(Mesh&& to_move) { Swap(to_move); }
+
+ ~Mesh() { DeleteGLData(); }
+
+ Mesh& operator=(const Mesh&& to_move) {
+ Swap(to_move);
+ return *this;
+ }
+
+ operator bool() const { return mesh_vbo_ != 0; }
+
+ void Swap(Mesh& to_swap) {
+ std::swap(mesh_vbo_, to_swap.mesh_vbo_);
+ std::swap(mesh_vao_, to_swap.mesh_vao_);
+ std::swap(number_of_vertices_, to_swap.number_of_vertices_);
+ std::swap(element_type_, to_swap.element_type_);
+ }
+
+ void Draw(uint32_t number_of_vertices) {
+ if (!mesh_vbo_)
+ return;
+
+ glBindVertexArray(mesh_vao_);
+ glDrawArrays(element_type_, 0, number_of_vertices);
+ glBindVertexArray(0);
+ }
+
+ void Draw() { Draw(number_of_vertices_); }
+
+ void SetVertices(uint32_t number_of_vertices, const void* vertices,
+ GLenum element_type, GLenum usage) {
+ DeleteGLData();
+ element_type_ = element_type;
+ number_of_vertices_ = number_of_vertices;
+ glGenBuffers(1, &mesh_vbo_);
+ glGenVertexArrays(1, &mesh_vao_);
+ glBindVertexArray(mesh_vao_);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_);
+ glBufferData(GL_ARRAY_BUFFER, attribute_size * number_of_vertices, vertices,
+ usage);
+
+ SetupAttributes();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ }
+
+ void SetVertices(uint32_t number_of_vertices, const void* vertices) {
+ SetVertices(number_of_vertices, vertices, element_type_, GL_STATIC_DRAW);
+ }
+
+ void SetVertices(uint32_t number_of_vertices, const void* vertices,
+ GLenum element_type) {
+ SetVertices(number_of_vertices, vertices, element_type, GL_STATIC_DRAW);
+ }
+
+ std::tuple<Attributes...>* Map(GLbitfield access, int num_vertices) {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_);
+ void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0,
+ attribute_size * num_vertices, access);
+ return static_cast<std::tuple<Attributes...>*>(ptr);
+ }
+
+ void Unmap() {
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ private:
+ Mesh(const Mesh&) = delete;
+ Mesh& operator=(const Mesh&) = delete;
+
+ void DeleteGLData() {
+ if (mesh_vbo_) {
+ glDeleteBuffers(1, &mesh_vbo_);
+ glDeleteVertexArrays(1, &mesh_vao_);
+ mesh_vbo_ = 0;
+ mesh_vao_ = 0;
+ number_of_vertices_ = 0;
+ }
+ }
+
+ void SetupAttributes() {
+ const auto size = std::tuple_size<std::tuple<Attributes...>>::value;
+ Details::VertexAttribHelper<size - 1, Attributes...>{}();
+ }
+
+ private:
+ GLuint mesh_vbo_ = 0;
+ GLuint mesh_vao_ = 0;
+ uint32_t number_of_vertices_ = 0;
+
+ GLenum element_type_ = GL_TRIANGLES;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_MESH_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/shader_program.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/shader_program.h
new file mode 100644
index 0000000..4218a73
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/shader_program.h
@@ -0,0 +1,75 @@
+#ifndef ANDROID_DVR_SHADER_PROGRAM_H_
+#define ANDROID_DVR_SHADER_PROGRAM_H_
+
+#include <EGL/egl.h>
+#include <GLES3/gl31.h>
+#include <sys/cdefs.h>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// Helper function that allows you to write a shader as a Lambda. This allows
+// an IDE to syntax highlight the contents of a shader, as well as preventing
+// quotations on each line. Usage: std::string vs = SHADER0([]() { ... });
+template <size_t size>
+std::string StripLambda(const char (&shader)[size]) {
+ return std::string(shader + 6, shader + size - 2);
+}
+
+#define SHADER0(Src) ::android::dvr::StripLambda(#Src)
+
+// Helper function that takes a shader source string containing %0, %1, %n,
+// tokens and replaces them with replacements[0], replacements[1],
+// replacements[n]. For example:
+// shader = "{
+// uniform vec2 %0;
+// %1
+// ...
+// %0.x = 1.0; ...
+// %1(%0);
+// }"
+// -> %0 = "myVarName", %1 = "void f(vec2 v) { ... }"
+std::string ComposeShader(const std::string& shader_code,
+ const std::vector<std::string>& replacements);
+
+class ShaderProgram {
+ public:
+ ShaderProgram();
+ ShaderProgram(const std::string& vertext_source,
+ const std::string& fragment_source);
+ ShaderProgram(ShaderProgram&&);
+ ~ShaderProgram();
+
+ ShaderProgram& operator=(ShaderProgram&&);
+
+ void Link(const std::string& vertext_source,
+ const std::string& fragment_source);
+
+ void Link(const std::string& compute_source);
+
+ void Use() const;
+
+ GLuint GetProgram() const { return program_; }
+ GLuint GetUniformLocation(const GLchar* name) const {
+ return glGetUniformLocation(program_, name);
+ }
+ GLuint GetAttribLocation(const GLchar* name) const {
+ return glGetAttribLocation(program_, name);
+ }
+
+ bool IsUsable() const { return program_ != 0; }
+ explicit operator bool() const { return IsUsable(); }
+
+ private:
+ ShaderProgram(const ShaderProgram&) = delete;
+ ShaderProgram& operator=(const ShaderProgram&) = delete;
+
+ GLuint program_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SHADER_PROGRAM_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/timer_query.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/timer_query.h
new file mode 100644
index 0000000..11d4d01
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/timer_query.h
@@ -0,0 +1,52 @@
+#ifndef ANDROID_DVR_GRAPHICS_TIMER_QUERY_H_
+#define ANDROID_DVR_GRAPHICS_TIMER_QUERY_H_
+
+#include <GLES3/gl3.h>
+
+namespace android {
+namespace dvr {
+
+// Class used to asynchronously query time between draw calls on gpu.
+class TimerQuery {
+ public:
+ TimerQuery();
+ ~TimerQuery();
+
+ // Marks the start of the timer on gpu.
+ void Begin();
+
+ // Marks the end of the timer on gpu.
+ void End();
+
+ // Gets the time that has passed from call to Begin to End.
+ // Should be called only after the frame has been presented (after the call to
+ // swapbuffers).
+ double GetTimeInMS();
+
+ private:
+ // Generates OpenGL query object.
+ void Init();
+ // Deletes OpenGL query object.
+ void Delete();
+
+ GLuint query_ = 0;
+
+ friend class SyncTimerQuery;
+};
+
+// Simplification of TimerQuery that allows to synchronously query time used
+// for draw calls on gpu by doing glFlush and stalling cpu.
+class SyncTimerQuery {
+ public:
+ SyncTimerQuery();
+
+ double FlushAndGetTimeInMS(); // Note: This WILL cause a glFlush()
+
+ private:
+ TimerQuery timer_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_TIMER_QUERY_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/vertex_attributes.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vertex_attributes.h
new file mode 100644
index 0000000..dac5b64
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vertex_attributes.h
@@ -0,0 +1,64 @@
+#ifndef ANDROID_DVR_GRAPHICS_VERTEX_ATTRIBUTES_H_
+#define ANDROID_DVR_GRAPHICS_VERTEX_ATTRIBUTES_H_
+
+#include <private/dvr/types.h>
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <tuple>
+
+namespace android {
+namespace dvr {
+
+namespace Details {
+
+// Set up the vertex attributes by iterating over the variadic template
+// parameters. The supported attributes are the GetSize and GetType
+// specializations.
+// clang-format off
+template<typename T> GLint GetSize();
+template<> inline GLint GetSize<vec2>() { return 2; }
+template<> inline GLint GetSize<vec3>() { return 3; }
+template<> inline GLint GetSize<vec4>() { return 4; }
+
+template<typename T> GLenum GetType();
+template<> inline GLenum GetType<vec2>() { return GL_FLOAT; }
+template<> inline GLenum GetType<vec3>() { return GL_FLOAT; }
+template<> inline GLenum GetType<vec4>() { return GL_FLOAT; }
+// clang-format on
+
+template <typename T>
+void VertexAttrib(GLuint index, GLsizei stride, const GLvoid* pointer) {
+ glVertexAttribPointer(index, GetSize<T>(), GetType<T>(), GL_FALSE, stride,
+ pointer);
+ glEnableVertexAttribArray(index);
+}
+
+// Recursion variadic template parameter iterator.
+template <int index, typename... Ts>
+struct VertexAttribHelper {
+ using tuple = std::tuple<Ts...>;
+ size_t operator()() {
+ size_t offset = VertexAttribHelper<index - 1, Ts...>{}();
+ using type = typename std::tuple_element<index, tuple>::type;
+ VertexAttrib<type>(index, sizeof(tuple), reinterpret_cast<void*>(offset));
+ return offset + sizeof(type);
+ }
+};
+
+// Recursion stop point.
+template <typename... Ts>
+struct VertexAttribHelper<0, Ts...> {
+ using tuple = std::tuple<Ts...>;
+ size_t operator()() {
+ using type = typename std::tuple_element<0, tuple>::type;
+ VertexAttrib<type>(0, sizeof(tuple), nullptr);
+ return sizeof(type);
+ }
+};
+} // namespace Details
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_GRAPHICS_VERTEX_ATTRIBUTES_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/vr_gl_extensions.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vr_gl_extensions.h
new file mode 100644
index 0000000..9635dbb
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vr_gl_extensions.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_VR_GL_EXTENSIONS_H_
+#define ANDROID_DVR_VR_GL_EXTENSIONS_H_
+
+// clang-format off
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl3ext.h>
+// clang-format on
+
+// GL_EXT_disjoint_timer_query API function declarations
+extern PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64v;
+extern PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv;
+extern PFNGLQUERYCOUNTEREXTPROC glQueryCounter;
+
+// EXT_buffer_storage:
+extern PFNGLBUFFERSTORAGEEXTPROC glBufferStorage;
+
+typedef void(GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(
+ GLenum target, GLenum attachment, GLuint texture, GLint level,
+ GLint baseViewIndex, GLsizei numViews);
+typedef void(GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR)(
+ GLenum target, GLenum attachement, GLuint texture, GLint level,
+ GLsizei samples, GLint baseViewIndex, GLsizei numViews);
+
+extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR glFramebufferTextureMultiview;
+extern PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR
+ glFramebufferTextureMultisampleMultiview;
+
+// QCOM_gralloc_buffer_data and QCOM_shared_buffer
+typedef void(GL_APIENTRY* PFNGLGRALLOCBUFFERDATAQCOM)(GLenum target,
+ GLsizeiptr sizeInBytes,
+ GLvoid* hostPtr,
+ GLint fd);
+typedef void(GL_APIENTRY* PFNGLSHAREDBUFFERCREATEQCOM)(GLsizeiptr sizeInBytes,
+ GLint* outFd);
+typedef void(GL_APIENTRY* PFNGLSHAREDBUFFERDESTROYQCOM)(GLint fd);
+typedef void(GL_APIENTRY* PFNGLSHAREDBUFFERBINDQCOM)(GLenum target,
+ GLsizeiptr sizeInBytes,
+ GLint fd);
+
+extern PFNGLGRALLOCBUFFERDATAQCOM glGrallocBufferDataQCOM;
+extern PFNGLSHAREDBUFFERCREATEQCOM glCreateSharedBufferQCOM;
+extern PFNGLSHAREDBUFFERDESTROYQCOM glDestroySharedBufferQCOM;
+extern PFNGLSHAREDBUFFERBINDQCOM glBindSharedBufferQCOM;
+
+extern "C" void load_gl_extensions();
+
+#endif // ANDROID_DVR_VR_GL_EXTENSIONS_H_
diff --git a/libs/vr/libdvrgraphics/shader_program.cpp b/libs/vr/libdvrgraphics/shader_program.cpp
new file mode 100644
index 0000000..bf36eff
--- /dev/null
+++ b/libs/vr/libdvrgraphics/shader_program.cpp
@@ -0,0 +1,165 @@
+#include "include/private/dvr/graphics/shader_program.h"
+
+#include <regex>
+#include <sstream>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+namespace {
+
+static bool CompileShader(GLuint shader, const std::string& shader_string) {
+ std::string prefix = "";
+ if (!base::StartsWith(shader_string, "#version",
+ base::CompareCase::SENSITIVE)) {
+ prefix = "#version 310 es\n";
+ }
+ std::string string_with_prefix = prefix + shader_string;
+ const char* shader_str[] = {string_with_prefix.data()};
+ glShaderSource(shader, 1, shader_str, nullptr);
+ glCompileShader(shader);
+
+ GLint success;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ GLchar infoLog[512];
+ glGetShaderInfoLog(shader, 512, nullptr, infoLog);
+ LOG(ERROR) << "Shader Failed to compile: " << *shader_str << " -- "
+ << infoLog;
+ return false;
+ }
+ return true;
+}
+
+static bool LinkProgram(GLuint program, GLuint vertex_shader,
+ GLuint fragment_shader) {
+ glAttachShader(program, vertex_shader);
+ glAttachShader(program, fragment_shader);
+ glLinkProgram(program);
+
+ // Check for linking errors
+ GLint success;
+ glGetProgramiv(program, GL_LINK_STATUS, &success);
+ if (!success) {
+ GLchar infoLog[512];
+ glGetProgramInfoLog(program, 512, nullptr, infoLog);
+ LOG(ERROR) << "Shader failed to link: " << infoLog;
+ return false;
+ }
+
+ return true;
+}
+
+static bool LinkProgram(GLuint program, GLuint compute_shader) {
+ glAttachShader(program, compute_shader);
+ glLinkProgram(program);
+
+ // Check for linking errors
+ GLint success;
+ glGetProgramiv(program, GL_LINK_STATUS, &success);
+ if (!success) {
+ GLchar infoLog[512];
+ glGetProgramInfoLog(program, 512, nullptr, infoLog);
+ LOG(ERROR) << "Shader failed to link: " << infoLog;
+ return false;
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+ShaderProgram::ShaderProgram() : program_(0) {}
+
+ShaderProgram::ShaderProgram(const std::string& vertext_source,
+ const std::string& fragment_source)
+ : program_(0) {
+ Link(vertext_source, fragment_source);
+}
+
+ShaderProgram::ShaderProgram(ShaderProgram&& to_move) {
+ std::swap(program_, to_move.program_);
+}
+
+ShaderProgram::~ShaderProgram() {
+ if (program_)
+ glDeleteProgram(program_);
+}
+
+ShaderProgram& ShaderProgram::operator=(ShaderProgram&& to_move) {
+ std::swap(program_, to_move.program_);
+ return *this;
+}
+
+void ShaderProgram::Link(const std::string& vertext_source,
+ const std::string& fragment_source) {
+ if (program_)
+ glDeleteProgram(program_);
+ program_ = glCreateProgram();
+ GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ bool success = CompileShader(vertex_shader, vertext_source) &&
+ CompileShader(fragment_shader, fragment_source) &&
+ LinkProgram(program_, vertex_shader, fragment_shader);
+
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+
+ if (!success) {
+ glDeleteProgram(program_);
+ program_ = 0;
+ }
+}
+
+void ShaderProgram::Link(const std::string& compute_source) {
+ if (program_)
+ glDeleteProgram(program_);
+ program_ = glCreateProgram();
+ GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
+
+ bool success =
+ CompileShader(shader, compute_source) && LinkProgram(program_, shader);
+
+ glDeleteShader(shader);
+
+ if (!success) {
+ glDeleteProgram(program_);
+ program_ = 0;
+ }
+}
+
+void ShaderProgram::Use() const { glUseProgram(program_); }
+
+std::string ComposeShader(const std::string& shader_code,
+ const std::vector<std::string>& variables) {
+ std::stringstream result_stream;
+ std::regex expression("%([0-9]*)");
+ using reg_iter = std::regex_token_iterator<std::string::const_iterator>;
+ reg_iter rend;
+ // match the string and number (drop the %)
+ std::vector<int> submatches = {-1, 1};
+ reg_iter reg(shader_code.begin(), shader_code.end(), expression, submatches);
+ bool is_even = true;
+ while (reg != rend) {
+ if (is_even) {
+ // even entries is the code between the %n's
+ result_stream << *reg;
+ } else {
+ // odd entries are the index into the passed in variables.
+ size_t i = static_cast<size_t>(std::stoi(*reg));
+ if (i < variables.size()) {
+ result_stream << variables[i];
+ }
+ }
+ is_even = !is_even;
+ ++reg;
+ }
+ return result_stream.str();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrgraphics/timer_query.cpp b/libs/vr/libdvrgraphics/timer_query.cpp
new file mode 100644
index 0000000..dcc6216
--- /dev/null
+++ b/libs/vr/libdvrgraphics/timer_query.cpp
@@ -0,0 +1,65 @@
+#include "include/private/dvr/graphics/timer_query.h"
+
+#include <GLES2/gl2ext.h>
+#include <base/logging.h>
+
+namespace android {
+namespace dvr {
+
+TimerQuery::TimerQuery() {}
+
+TimerQuery::~TimerQuery() { Delete(); }
+
+void TimerQuery::Init() { glGenQueriesEXT(1, &query_); }
+
+void TimerQuery::Delete() {
+ if (query_) {
+ glDeleteQueriesEXT(1, &query_);
+ query_ = 0;
+ }
+}
+
+void TimerQuery::Begin() {
+ if (query_ == 0) {
+ Init();
+ }
+ glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query_);
+}
+
+void TimerQuery::End() { glEndQueryEXT(GL_TIME_ELAPSED_EXT); }
+
+double TimerQuery::GetTimeInMS() {
+ GLuint64 elapsed_time = 0;
+ glGetQueryObjectui64vEXT(query_, GL_QUERY_RESULT, &elapsed_time);
+ return static_cast<double>(elapsed_time) / 1000000.0;
+}
+
+SyncTimerQuery::SyncTimerQuery() { timer_.Begin(); }
+
+double SyncTimerQuery::FlushAndGetTimeInMS() {
+ if (timer_.query_ == 0) {
+ LOG(ERROR) << "Error: Only call FlushAndGetTimeInMS() once.";
+ return 0.0;
+ }
+ timer_.End();
+ glFlush();
+ GLint done = 0;
+ while (!done) {
+ glGetQueryObjectivEXT(timer_.query_, GL_QUERY_RESULT_AVAILABLE, &done);
+ }
+
+ GLint disjoint_occurred = 0;
+ glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_occurred);
+ if (disjoint_occurred) {
+ LOG(ERROR) << "Disjoint occurred.";
+ timer_.Delete();
+ return 0.0;
+ }
+
+ double elapsed_time = timer_.GetTimeInMS();
+ timer_.Delete();
+ return elapsed_time;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrgraphics/vr_gl_extensions.cpp b/libs/vr/libdvrgraphics/vr_gl_extensions.cpp
new file mode 100644
index 0000000..2c5a698
--- /dev/null
+++ b/libs/vr/libdvrgraphics/vr_gl_extensions.cpp
@@ -0,0 +1,44 @@
+#include "include/private/dvr/graphics/vr_gl_extensions.h"
+
+PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64v = NULL;
+PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv = NULL;
+PFNGLQUERYCOUNTEREXTPROC glQueryCounter = NULL;
+PFNGLBUFFERSTORAGEEXTPROC glBufferStorage = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR glFramebufferTextureMultiview = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR
+glFramebufferTextureMultisampleMultiview = NULL;
+
+PFNGLSHAREDBUFFERCREATEQCOM glCreateSharedBufferQCOM = NULL;
+PFNGLSHAREDBUFFERDESTROYQCOM glDestroySharedBufferQCOM = NULL;
+PFNGLSHAREDBUFFERBINDQCOM glBindSharedBufferQCOM = NULL;
+PFNGLGRALLOCBUFFERDATAQCOM glGrallocBufferDataQCOM = NULL;
+
+extern "C" void load_gl_extensions() {
+ if (glGetQueryObjecti64v) {
+ return;
+ }
+ glGetQueryObjecti64v = reinterpret_cast<PFNGLGETQUERYOBJECTI64VEXTPROC>(
+ eglGetProcAddress("glGetQueryObjecti64vEXT"));
+ glGetQueryObjectiv = reinterpret_cast<PFNGLGETQUERYOBJECTIVEXTPROC>(
+ eglGetProcAddress("glGetQueryObjectivEXT"));
+ glQueryCounter = reinterpret_cast<PFNGLQUERYCOUNTEREXTPROC>(
+ eglGetProcAddress("glQueryCounterEXT"));
+ glBufferStorage = reinterpret_cast<PFNGLBUFFERSTORAGEEXTPROC>(
+ eglGetProcAddress("glBufferStorageEXT"));
+
+ glFramebufferTextureMultiview =
+ reinterpret_cast<PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR>(
+ eglGetProcAddress("glFramebufferTextureMultiviewOVR"));
+ glFramebufferTextureMultisampleMultiview =
+ reinterpret_cast<PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR>(
+ eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"));
+
+ glGrallocBufferDataQCOM = reinterpret_cast<PFNGLGRALLOCBUFFERDATAQCOM>(
+ eglGetProcAddress("glGrallocBufferDataQCOM"));
+ glCreateSharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERCREATEQCOM>(
+ eglGetProcAddress("glCreateSharedBufferQCOM"));
+ glBindSharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERBINDQCOM>(
+ eglGetProcAddress("glBindSharedBufferQCOM"));
+ glDestroySharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERDESTROYQCOM>(
+ eglGetProcAddress("glDestroySharedBufferQCOM"));
+}
diff --git a/libs/vr/libeds/Android.mk b/libs/vr/libeds/Android.mk
new file mode 100644
index 0000000..0345f6d
--- /dev/null
+++ b/libs/vr/libeds/Android.mk
@@ -0,0 +1,89 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ eds.cpp \
+ eds_mesh.cpp \
+ composite_hmd.cpp \
+ cpu_thread_pose_updater.cpp \
+ display_metrics.cpp \
+ distortion_renderer.cpp \
+ lucid_metrics.cpp \
+ lucid_pose_tracker.cpp \
+ lookup_radial_distortion.cpp \
+ polynomial_radial_distortion.cpp
+
+includeFiles += \
+ $(LOCAL_PATH)/include
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ liblog \
+ libEGL \
+ libGLESv1_CM \
+ libGLESv2 \
+ libvulkan
+
+staticLibraries := \
+ libchrome \
+ libdisplay \
+ libdvrcommon \
+ libdvrgraphics \
+ libsensor \
+ libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -Wno-unused-parameter
+# Enable debug options below to show GL errors and use gdb.
+# LOCAL_CFLAGS += -UNDEBUG -DDEBUG -O0 -g
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libeds
+include $(BUILD_STATIC_LIBRARY)
+
+
+testFiles := \
+ tests/eds_app_tests.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := eds_app_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+ $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libhardware \
+ libsync \
+ $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock_main \
+ libgmock \
+ libdisplay \
+ libeds \
+ libbufferhub \
+ $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libeds/composite_hmd.cpp b/libs/vr/libeds/composite_hmd.cpp
new file mode 100644
index 0000000..d29cd65
--- /dev/null
+++ b/libs/vr/libeds/composite_hmd.cpp
@@ -0,0 +1,256 @@
+#include "include/private/dvr/composite_hmd.h"
+
+#include <base/logging.h>
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+CompositeHmd::CompositeHmd(const HeadMountMetrics& head_mount_metrics,
+ const DisplayMetrics& display_metrics)
+ : head_mount_metrics_(head_mount_metrics),
+ display_metrics_(display_metrics) {
+ MetricsChanged();
+}
+
+float CompositeHmd::GetTargetFrameDuration() const {
+ return display_metrics_.GetFrameDurationSeconds();
+}
+
+vec2 CompositeHmd::ComputeDistortedPoint(EyeType eye, vec2 position,
+ RgbColorChannel channel) const {
+ position = TransformPoint(eye_tan_angle_from_norm_screen_matrix_[eye], position);
+ vec2 distorted =
+ head_mount_metrics_.GetColorChannelDistortion(channel).Distort(position);
+ return TransformPoint(eye_norm_texture_from_tan_angle_matrix_[eye], distorted);
+}
+
+vec2 CompositeHmd::ComputeInverseDistortedPoint(EyeType eye, vec2 position,
+ RgbColorChannel channel) const {
+ position = TransformPoint(eye_norm_texture_from_tan_angle_inv_matrix_[eye], position);
+ vec2 distorted =
+ head_mount_metrics_.GetColorChannelDistortion(channel).DistortInverse(
+ position);
+ return TransformPoint(eye_tan_angle_from_norm_screen_inv_matrix_[eye], distorted);
+}
+
+void CompositeHmd::ComputeDistortedVertex(EyeType eye, vec2 uv_in,
+ vec2* vertex_out,
+ vec2* uv_out) const {
+ // The mesh vertices holds the shape of the distortion.
+ vec2 vertex_position = ComputeInverseDistortedPoint(eye, uv_in, kRed);
+ *vertex_out = vec2(vertex_position.x() - 0.5f, vertex_position.y() - 0.5f);
+
+ if (uv_out) {
+ // Compute the texture coordinate for each vertex coordinate.
+ // Red's is the inverse of the inverse, skip the calculation and use uv_in.
+ uv_out[kRed] = uv_in;
+ uv_out[kGreen] = ComputeDistortedPoint(eye, vertex_position, kGreen);
+ uv_out[kBlue] = ComputeDistortedPoint(eye, vertex_position, kBlue);
+ }
+}
+
+vec2i CompositeHmd::GetRecommendedRenderTargetSize() const {
+ return recommended_render_target_size_;
+}
+
+Range2i CompositeHmd::GetDisplayRange() const { return display_range_; }
+
+mat4 CompositeHmd::GetEyeFromHeadMatrix(EyeType eye) const {
+ return eye_from_head_matrix_[eye];
+}
+
+FieldOfView CompositeHmd::GetEyeFov(EyeType eye) const { return eye_fov_[eye]; }
+
+Range2i CompositeHmd::GetEyeViewportBounds(EyeType eye) const {
+ return eye_viewport_range_[eye];
+}
+
+void CompositeHmd::SetHeadMountMetrics(
+ const HeadMountMetrics& head_mount_metrics) {
+ // Use the assignement operator to do memberwise copy.
+ head_mount_metrics_ = head_mount_metrics;
+ MetricsChanged();
+}
+
+const HeadMountMetrics& CompositeHmd::GetHeadMountMetrics() const {
+ return head_mount_metrics_;
+}
+
+void CompositeHmd::SetDisplayMetrics(const DisplayMetrics& display_metrics) {
+ // Use the assignment operator to do memberwise copy.
+ display_metrics_ = display_metrics;
+ MetricsChanged();
+}
+
+const DisplayMetrics& CompositeHmd::GetDisplayMetrics() const {
+ return display_metrics_;
+}
+
+void CompositeHmd::MetricsChanged() {
+ // Abbreviations in variable names:
+ // "vp": viewport
+ // "ta": tan-angle
+ const HeadMountMetrics& mount = head_mount_metrics_;
+ DisplayMetrics display = display_metrics_;
+
+ if (display.IsPortrait()) {
+ // If we're in portrait mode, toggle the orientation so that all
+ // calculations are done in landscape mode.
+ display.ToggleOrientation();
+ }
+
+ float display_width_meters = display.GetSizeMeters()[0];
+ float display_height_meters = display.GetSizeMeters()[1];
+
+ vec2 pixels_per_meter = vec2(1.0f / display.GetMetersPerPixel()[0],
+ 1.0f / display.GetMetersPerPixel()[1]);
+
+ // virtual_eye_to_screen_dist is the distance from the screen to the eye
+ // after it has been projected through the lens. This would normally be
+ // slightly different from the distance to the actual eye.
+ float virtual_eye_to_screen_dist = mount.GetVirtualEyeToScreenDistance();
+ float meters_per_tan_angle = virtual_eye_to_screen_dist;
+ vec2 pixels_per_tan_angle = pixels_per_meter * meters_per_tan_angle;
+
+ CHECK_NE(0.0f, display_width_meters);
+ CHECK_NE(0.0f, display_height_meters);
+ CHECK_NE(0.0f, virtual_eye_to_screen_dist);
+
+ // Height of lenses from the bottom of the screen.
+ float lens_y_center = 0;
+ float bottom_dist = 0;
+ float top_dist = 0;
+
+ // bottom_display_dist and top_display_dist represent the distance from the
+ // lens center to the edge of the display.
+ float bottom_display_dist = 0;
+ float top_display_dist = 0;
+ switch (mount.GetVerticalAlignment()) {
+ case HeadMountMetrics::kBottom:
+ lens_y_center =
+ mount.GetTrayToLensDistance() - display.GetBorderSizeMeters();
+ bottom_dist = lens_y_center;
+ top_dist = lens_y_center;
+ bottom_display_dist = lens_y_center;
+ top_display_dist = display_height_meters - lens_y_center;
+ break;
+ case HeadMountMetrics::kCenter:
+ // TODO(hendrikw): This should respect the border size, but since we
+ // currently hard code the border size, it would break
+ // the distortion on some devices. Revisit when border
+ // size is fixed.
+ lens_y_center = display_height_meters * 0.5f;
+ bottom_dist = lens_y_center;
+ top_dist = lens_y_center;
+ bottom_display_dist = lens_y_center;
+ top_display_dist = lens_y_center;
+ break;
+ case HeadMountMetrics::kTop:
+ lens_y_center = display_height_meters - (mount.GetTrayToLensDistance() -
+ display.GetBorderSizeMeters());
+ bottom_dist =
+ mount.GetTrayToLensDistance() - display.GetBorderSizeMeters();
+ top_dist = bottom_dist;
+ bottom_display_dist = lens_y_center;
+ top_display_dist = display_height_meters - lens_y_center;
+ break;
+ }
+
+ float inner_dist = mount.GetScreenCenterToLensDistance();
+ float outer_dist = display_width_meters * 0.5f - inner_dist;
+
+ // We don't take chromatic aberration into account yet for computing FOV,
+ // viewport, etc, so we only use the green channel for now. Note the actual
+ // Distort function *does* implement chromatic aberration.
+ const ColorChannelDistortion& distortion =
+ mount.GetColorChannelDistortion(kGreen);
+
+ vec2 outer_point(outer_dist / virtual_eye_to_screen_dist, 0.0f);
+ vec2 inner_point(inner_dist / virtual_eye_to_screen_dist, 0.0f);
+ vec2 bottom_point(0.0f, bottom_dist / virtual_eye_to_screen_dist);
+ vec2 top_point(0.0f, top_dist / virtual_eye_to_screen_dist);
+
+ float outer_angle = atanf(distortion.Distort(outer_point)[0]);
+ float inner_angle = atanf(distortion.Distort(inner_point)[0]);
+ float bottom_angle = atanf(distortion.Distort(bottom_point)[1]);
+ float top_angle = atanf(distortion.Distort(top_point)[1]);
+
+ for (EyeType eye : {kLeftEye, kRightEye}) {
+ const FieldOfView max_fov = mount.GetEyeMaxFov(eye);
+ float left_angle = (eye == kLeftEye) ? outer_angle : inner_angle;
+ float right_angle = (eye == kLeftEye) ? inner_angle : outer_angle;
+
+ eye_fov_[eye] = FieldOfView(std::min(left_angle, max_fov.GetLeft()),
+ std::min(right_angle, max_fov.GetRight()),
+ std::min(bottom_angle, max_fov.GetBottom()),
+ std::min(top_angle, max_fov.GetTop()));
+
+ vec2 texture_vp_ta_p1 =
+ vec2(-tanf(eye_fov_[eye].GetLeft()), -tanf(eye_fov_[eye].GetBottom()));
+ vec2 texture_vp_ta_p2 =
+ vec2(tanf(eye_fov_[eye].GetRight()), tanf(eye_fov_[eye].GetTop()));
+ vec2 texture_vp_size_ta = texture_vp_ta_p2 - texture_vp_ta_p1;
+
+ vec2 texture_vp_sizef_pixels =
+ texture_vp_size_ta.array() * pixels_per_tan_angle.array();
+
+ vec2i texture_vp_size_pixels =
+ vec2i(static_cast<int32_t>(roundf(texture_vp_sizef_pixels[0])),
+ static_cast<int32_t>(roundf(texture_vp_sizef_pixels[1])));
+ int vp_start_x =
+ (eye == kLeftEye) ? 0 : eye_viewport_range_[kLeftEye].p2[0];
+
+ eye_viewport_range_[eye] =
+ Range2i::FromSize(vec2i(vp_start_x, 0), texture_vp_size_pixels);
+ float left_dist = (eye == kLeftEye) ? outer_dist : inner_dist;
+ float right_dist = (eye == kLeftEye) ? inner_dist : outer_dist;
+ vec2 screen_ta_p1(-left_dist / virtual_eye_to_screen_dist,
+ -bottom_display_dist / virtual_eye_to_screen_dist);
+ vec2 screen_ta_p2(right_dist / virtual_eye_to_screen_dist,
+ top_display_dist / virtual_eye_to_screen_dist);
+ vec2 screen_ta_size = screen_ta_p2 - screen_ta_p1;
+
+ // Align the tan angle coordinates to the nearest pixel. This will ensure
+ // that the optical center doesn't straddle multiple pixels.
+ // TODO(hendrikw): verify that this works correctly for Daydream View.
+ vec2 tan_angle_per_pixel(screen_ta_size.array() /
+ texture_vp_size_pixels.cast<float>().array());
+ vec2 pixel_p1(screen_ta_p1.array() / tan_angle_per_pixel.array());
+ vec2 pixel_shift(roundf(pixel_p1.x()) - pixel_p1.x(),
+ roundf(pixel_p1.y()) - pixel_p1.y());
+ screen_ta_p1 +=
+ (tan_angle_per_pixel.array() * pixel_shift.array()).matrix();
+ screen_ta_p2 +=
+ (tan_angle_per_pixel.array() * pixel_shift.array()).matrix();
+
+ // Calculate the transformations needed for the distortions.
+ eye_tan_angle_from_norm_screen_matrix_[eye] =
+ TranslationMatrix(vec2(screen_ta_p1)) *
+ ScaleMatrix(screen_ta_size);
+ eye_tan_angle_from_norm_screen_inv_matrix_[eye] =
+ eye_tan_angle_from_norm_screen_matrix_[eye].inverse();
+
+ eye_norm_texture_from_tan_angle_inv_matrix_[eye] =
+ TranslationMatrix(texture_vp_ta_p1) *
+ ScaleMatrix(texture_vp_size_ta);
+ eye_norm_texture_from_tan_angle_matrix_[eye] =
+ eye_norm_texture_from_tan_angle_inv_matrix_[eye].inverse();
+ }
+ vec2i left_vp_size = eye_viewport_range_[kLeftEye].GetSize();
+ vec2i right_vp_size = eye_viewport_range_[kRightEye].GetSize();
+
+ recommended_render_target_size_ =
+ vec2i(left_vp_size[0] + right_vp_size[0],
+ std::max(left_vp_size[1], right_vp_size[1]));
+
+ display_range_ = Range2i::FromSize(vec2i(0, 0), display.GetSizePixels());
+
+ eye_from_head_matrix_[kLeftEye] = Eigen::Translation3f(
+ vec3(mount.GetScreenCenterToLensDistance(), 0.0f, 0.0f));
+ eye_from_head_matrix_[kRightEye] = Eigen::Translation3f(
+ vec3(-mount.GetScreenCenterToLensDistance(), 0.0f, 0.0f));
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/cpu_thread_pose_updater.cpp b/libs/vr/libeds/cpu_thread_pose_updater.cpp
new file mode 100644
index 0000000..5b8a734
--- /dev/null
+++ b/libs/vr/libeds/cpu_thread_pose_updater.cpp
@@ -0,0 +1,86 @@
+#include "include/private/dvr/cpu_thread_pose_updater.h"
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#define ATRACE_TAG ATRACE_TAG_INPUT
+#include <utils/Trace.h>
+
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+
+namespace android {
+namespace dvr {
+
+CpuThreadPoseUpdater::CpuThreadPoseUpdater()
+ : stop_request_(false), update_period_us_(0), count_(0) {}
+
+CpuThreadPoseUpdater::~CpuThreadPoseUpdater() { StopAndJoin(); }
+
+void CpuThreadPoseUpdater::Start(volatile RawPosePair* mapped_pose_buffer,
+ int period_us) {
+ mapped_pose_buffer_ = mapped_pose_buffer;
+ update_period_us_ = period_us;
+ stop_request_ = false;
+
+ // First buffer is odd (starts at 1), second is even (starts at 2).
+ count_ = 0;
+ mapped_pose_buffer_->pose1.Reset(++count_);
+ mapped_pose_buffer_->pose2.Reset(++count_);
+
+ update_thread_ = std::thread(&CpuThreadPoseUpdater::UpdateThread, this);
+}
+
+void CpuThreadPoseUpdater::StopAndJoin() {
+ stop_request_ = true;
+ if (update_thread_.joinable()) {
+ update_thread_.join();
+ }
+}
+
+void CpuThreadPoseUpdater::UpdateThread() {
+ prctl(PR_SET_NAME, reinterpret_cast<intptr_t>("CpuPoseUpdater"),
+ 0, 0, 0);
+
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+ for (;;) {
+ if (stop_request_) {
+ break;
+ }
+
+ ++count_;
+
+ // Choose the writable pose based on whether count is odd or even.
+ volatile RawPose* out_pose = nullptr;
+ if (count_ & 1) {
+ out_pose = &mapped_pose_buffer_->pose1;
+ } else {
+ out_pose = &mapped_pose_buffer_->pose2;
+ }
+
+ {
+ ATRACE_NAME("GetPose");
+ Posef pose = pose_tracker_.GetPose(GetSystemClockNs());
+ out_pose->qx = pose.GetRotation().x();
+ out_pose->qy = pose.GetRotation().y();
+ out_pose->qz = pose.GetRotation().z();
+ out_pose->qw = pose.GetRotation().w();
+ out_pose->px = pose.GetPosition()[0];
+ out_pose->py = pose.GetPosition()[1];
+ out_pose->pz = pose.GetPosition()[2];
+ // Atomically store the count so that it hits memory last:
+ out_pose->count.store(count_, std::memory_order_release);
+ }
+
+ // Sleep to simulate the IMU update process.
+ usleep(update_period_us_);
+ // TODO(jbates) sleep_for returns immediately, we need to fix our toolchain!
+ // int64_t c1 = GetSystemClockNs();
+ // std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ // int64_t c2 = GetSystemClockNs();
+ // fprintf(stderr, "%lld us\n", (long long)(c2 - c1) / 1000);
+ }
+}
+
+} // namesapce dvr
+} // namesapce android
diff --git a/libs/vr/libeds/display_metrics.cpp b/libs/vr/libeds/display_metrics.cpp
new file mode 100644
index 0000000..e129395
--- /dev/null
+++ b/libs/vr/libeds/display_metrics.cpp
@@ -0,0 +1,30 @@
+#include "include/private/dvr/display_metrics.h"
+
+namespace android {
+namespace dvr {
+
+DisplayMetrics::DisplayMetrics(vec2i size_pixels, vec2 meters_per_pixel,
+ float border_size_meters,
+ float frame_duration_seconds,
+ DisplayOrientation orientation)
+ : size_pixels_(size_pixels),
+ meters_per_pixel_(meters_per_pixel),
+ border_size_meters_(border_size_meters),
+ frame_duration_seconds_(frame_duration_seconds),
+ orientation_(orientation) {}
+
+void DisplayMetrics::ToggleOrientation() {
+ std::swap(size_pixels_[0], size_pixels_[1]);
+ std::swap(meters_per_pixel_[0], meters_per_pixel_[1]);
+ if (orientation_ == DisplayOrientation::kPortrait)
+ orientation_ = DisplayOrientation::kLandscape;
+ else
+ orientation_ = DisplayOrientation::kPortrait;
+}
+
+DisplayMetrics::DisplayMetrics()
+ : DisplayMetrics(vec2i(0, 0), vec2(0.0f, 0.0f), 0.0f, 0.0f,
+ DisplayOrientation::kLandscape) {}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/distortion_renderer.cpp b/libs/vr/libeds/distortion_renderer.cpp
new file mode 100644
index 0000000..a19843f
--- /dev/null
+++ b/libs/vr/libeds/distortion_renderer.cpp
@@ -0,0 +1,793 @@
+#include "include/private/dvr/distortion_renderer.h"
+
+#include <float.h>
+
+#include <string>
+
+#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <base/logging.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/ortho.h>
+#include <private/dvr/sensor_constants.h>
+
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+
+#define POSITION_ATTR 0
+#define VIEWPORT_COORD_R_ATTR 1
+#define VIEWPORT_COORD_G_ATTR 2
+#define VIEWPORT_COORD_B_ATTR 3
+
+// Pose data uniform buffer bindings. Must be sequential.
+#define POSE_BINDING 0
+#define POSE_BINDING2 1
+
+// Texture unit bindings. Must be sequential.
+// Things break if we start at binding 0 (samples come back black).
+#define SAMPLER_BINDING 1
+#define SAMPLER_BINDING2 2
+
+#define GLSL_VIGNETTE_FUNC \
+ "float vignette(vec2 texCoords) {\n" \
+ " const float fadeDist = 0.01;\n" \
+ " const float fadeDistInv = 1.0 / fadeDist;\n" \
+ " const float inset = 0.02;\n" \
+ " vec2 lowEdge = vec2(inset - fadeDist);\n" \
+ " vec2 highEdge = vec2(1.0 - inset + fadeDist);\n" \
+ " vec2 vignetteMin = " \
+ " clamp(-fadeDistInv * (lowEdge - texCoords), 0.0, 1.0);\n" \
+ " vec2 vignetteMax = " \
+ " clamp(fadeDistInv * (highEdge - texCoords), 0.0, 1.0);\n" \
+ " vec2 vignette = vignetteMin * vignetteMax;\n" \
+ " return vignette.x * vignette.y;\n" \
+ "}\n"
+
+namespace {
+
+// If enabled, the pixel shader will blend by reading back the current pixel
+// from the framebuffer.
+// TODO(jbates) With framebuffer read coherency disabled, this seems to perform
+// well enough. That requires a GL extension, so for now we disable this path.
+constexpr bool kUseFramebufferReadback = false;
+
+static const char* kVertexShaderChromaticAberrationString =
+ "uniform mat4 uProjectionMatrix;\n"
+ "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+ "uniform LateLatchData {\n"
+ " mat4 uTexFromRecommendedViewportMatrix;\n"
+ "};\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ "layout(binding = " STRINGIFY(POSE_BINDING2) ", std140)\n"
+ "uniform LateLatchData2 {\n"
+ " mat4 uTexFromRecommendedViewportMatrix2;\n"
+ "};\n"
+ "#endif\n"
+ "uniform vec4 uTexXMinMax;\n"
+ "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+ "layout(location = " STRINGIFY(VIEWPORT_COORD_R_ATTR)
+ ") in vec2 aViewportCoordsR;\n"
+ "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+ ") in vec2 aViewportCoordsG;\n"
+ "layout(location = " STRINGIFY(VIEWPORT_COORD_B_ATTR)
+ ") in vec2 aViewportCoordsB;\n"
+ "mediump out vec4 vTexCoordsRG;\n"
+ "mediump out vec2 vTexCoordsB;\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ "mediump out vec4 vTexCoordsRG2;\n"
+ "mediump out vec2 vTexCoordsB2;\n"
+ "#endif\n"
+ "mediump out vec3 vVignette;\n"
+ "\n" GLSL_VIGNETTE_FUNC
+ "void main(void) {\n"
+ " vVignette.r = vignette(aViewportCoordsR);\n"
+ " vVignette.g = vignette(aViewportCoordsG);\n"
+ " vVignette.b = vignette(aViewportCoordsB);\n"
+ " vec4 redTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+ " vec4(aViewportCoordsR, 0., 1.));\n"
+ " vec4 greenTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+ " vec4(aViewportCoordsG, 0., 1.));\n"
+ " vec4 blueTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+ " vec4(aViewportCoordsB, 0., 1.));\n"
+ " vTexCoordsRG.xy = redTexCoords.xy / redTexCoords.w;\n"
+ " vTexCoordsRG.zw = greenTexCoords.xy / greenTexCoords.w;\n"
+ " vTexCoordsB = blueTexCoords.xy / blueTexCoords.w;\n"
+ " vTexCoordsRG.x = clamp(vTexCoordsRG.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+ " vTexCoordsRG.z = clamp(vTexCoordsRG.z, uTexXMinMax.x, uTexXMinMax.y);\n"
+ " vTexCoordsB.x = clamp(vTexCoordsB.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ " redTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+ " vec4(aViewportCoordsR, 0., 1.));\n"
+ " greenTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+ " vec4(aViewportCoordsG, 0., 1.));\n"
+ " blueTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+ " vec4(aViewportCoordsB, 0., 1.));\n"
+ " vTexCoordsRG2.xy = redTexCoords.xy / redTexCoords.w;\n"
+ " vTexCoordsRG2.zw = greenTexCoords.xy / greenTexCoords.w;\n"
+ " vTexCoordsB2 = blueTexCoords.xy / blueTexCoords.w;\n"
+ " vTexCoordsRG2.x = clamp(vTexCoordsRG2.x,\n"
+ " uTexXMinMax.z, uTexXMinMax.w);\n"
+ " vTexCoordsRG2.z = clamp(vTexCoordsRG2.z, uTexXMinMax.z,\n"
+ " uTexXMinMax.w);\n"
+ " vTexCoordsB2.x = clamp(vTexCoordsB2.x, uTexXMinMax.z, uTexXMinMax.w);\n"
+ "#endif\n"
+ " gl_Position = uProjectionMatrix * vec4(aPosition, 0., 1.);\n"
+ "}\n";
+
+static const char* kFragmentShaderChromaticAberrationString =
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ " \n"
+ "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+ "uniform sampler2D uDistortionTexture; \n"
+ "mediump in vec4 vTexCoordsRG;\n"
+ "mediump in vec2 vTexCoordsB;\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ "layout(binding = " STRINGIFY(SAMPLER_BINDING2) ")\n"
+ "uniform sampler2D uDistortionTexture2; \n"
+ "mediump in vec4 vTexCoordsRG2;\n"
+ "mediump in vec2 vTexCoordsB2;\n"
+ "#endif\n"
+ "mediump in vec3 vVignette;\n"
+ "#ifdef BLEND_WITH_PREVIOUS_LAYER \n"
+ "inout vec4 fragColor; \n"
+ "#else \n"
+ "out vec4 fragColor; \n"
+ "#endif \n"
+ " \n"
+ "void main(void) { \n"
+ " vec4 ra = texture(uDistortionTexture, vTexCoordsRG.xy); \n"
+ " vec4 ga = texture(uDistortionTexture, vTexCoordsRG.zw); \n"
+ " vec4 ba = texture(uDistortionTexture, vTexCoordsB); \n"
+ "#ifdef BLEND_WITH_PREVIOUS_LAYER \n"
+ " vec3 alpha1 = vec3(ra.a, ga.a, ba.a); \n"
+ " vec3 color = (vec3(1.0) - alpha1) * fragColor.rgb + \n"
+ " alpha1 * vec3(ra.r, ga.g, ba.b); \n"
+ "#else // BLEND_WITH_PREVIOUS_LAYER \n"
+ " vec3 color = vec3(ra.r, ga.g, ba.b); \n"
+ "#endif // BLEND_WITH_PREVIOUS_LAYER \n"
+ "#ifdef COMPOSITE_LAYER_2 \n"
+ " // Alpha blend layer 2 onto layer 1. \n"
+ " vec4 ra2 = texture(uDistortionTexture2, vTexCoordsRG2.xy); \n"
+ " vec4 ga2 = texture(uDistortionTexture2, vTexCoordsRG2.zw); \n"
+ " vec4 ba2 = texture(uDistortionTexture2, vTexCoordsB2); \n"
+ " vec3 color2 = vec3(ra2.r, ga2.g, ba2.b); \n"
+ " vec3 alpha2 = vec3(ra2.a, ga2.a, ba2.a); \n"
+ " color = (vec3(1.0) - alpha2) * color + alpha2 * color2; \n"
+ "#endif \n"
+ "#ifdef ALPHA_VIGNETTE\n"
+ " fragColor = vec4(color, vVignette.b * ga.a); \n"
+ "#else // ALPHA_VIGNETTE\n"
+ " fragColor = vec4(vVignette.rgb * color, ga.a); \n"
+ "#endif // ALPHA_VIGNETTE\n"
+ "} \n";
+
+static const char* kVertexShaderNoChromaticAberrationString =
+ "uniform mat4 uProjectionMatrix;\n"
+ "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+ "uniform LateLatchData {\n"
+ " mat4 uTexFromRecommendedViewportMatrix;\n"
+ "};\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ "layout(binding = " STRINGIFY(POSE_BINDING2) ", std140)\n"
+ "uniform LateLatchData2 {\n"
+ " mat4 uTexFromRecommendedViewportMatrix2;\n"
+ "};\n"
+ "#endif\n"
+ "uniform vec4 uTexXMinMax;\n"
+ "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+ "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+ ") in vec2 aViewportCoords;\n"
+ "mediump out vec2 vTexCoords;\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ "mediump out vec2 vTexCoords2;\n"
+ "#endif\n"
+ "mediump out vec3 vVignette;\n"
+ "\n" GLSL_VIGNETTE_FUNC
+ "void main(void) {\n"
+ " float fVignette = vignette(aViewportCoords);\n"
+ " vVignette = vec3(fVignette, fVignette, fVignette);\n"
+ " vec4 texCoords = (uTexFromRecommendedViewportMatrix * \n"
+ " vec4(aViewportCoords, 0., 1.));\n"
+ " vTexCoords = texCoords.xy / texCoords.w;\n"
+ " vTexCoords.x = clamp(vTexCoords.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ " texCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+ " vec4(aViewportCoords, 0., 1.));\n"
+ " vTexCoords2 = texCoords.xy / texCoords.w;\n"
+ " vTexCoords2.x = clamp(vTexCoords2.x, uTexXMinMax.z, uTexXMinMax.w);\n"
+ "#endif\n"
+ " gl_Position = uProjectionMatrix * vec4(aPosition, 0., 1.);\n"
+ "}\n";
+
+static const char* kFragmentShaderNoChromaticAberrationString =
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ " \n"
+ "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+ "uniform sampler2D uDistortionTexture; \n"
+ "mediump in vec2 vTexCoords;\n"
+ "#ifdef COMPOSITE_LAYER_2\n"
+ "layout(binding = " STRINGIFY(SAMPLER_BINDING2) ")\n"
+ "uniform sampler2D uDistortionTexture2; \n"
+ "mediump in vec2 vTexCoords2;\n"
+ "#endif\n"
+ "mediump in vec3 vVignette;\n"
+ "out vec4 fragColor;\n"
+ " \n"
+ "void main(void) { \n"
+ " vec4 color = texture(uDistortionTexture, vTexCoords); \n"
+ "#ifdef COMPOSITE_LAYER_2 \n"
+ " // Alpha blend layer 2 onto layer 1. \n"
+ " vec4 color2 = texture(uDistortionTexture2, vTexCoords2); \n"
+ " float alpha2 = color2.a; \n"
+ " color.rgb = (1.0 - alpha2) * color.rgb + alpha2 * color2.rgb; \n"
+ "#endif \n"
+ " fragColor = vec4(vVignette * color.rgb, color.a); \n"
+ "} \n";
+
+static const char* kVertexShaderSimpleVideoQuadString =
+ "uniform mat4 uProjectionMatrix;\n"
+ "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+ "uniform LateLatchData {\n"
+ " mat4 uEdsCorrection;\n"
+ "};\n"
+ "uniform mat4 uTexFromEyeMatrix;\n"
+ "uniform mat4 uEyeFromViewportMatrix;\n"
+ "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+ "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+ ") in vec2 aViewportCoords;\n"
+ "mediump out vec2 vTexCoords;\n"
+ "void main(void) {\n"
+ " mat4 m = uTexFromEyeMatrix * inverse(uEdsCorrection) * uEyeFromViewportMatrix;\n"
+ " mat3 uTexFromViewportMatrix = inverse(mat3(m[0].xyw, m[1].xyw, m[3].xyw)); \n"
+ " vec3 texCoords = uTexFromViewportMatrix * vec3(aViewportCoords, 1.0);\n"
+ " vTexCoords = texCoords.xy / texCoords.z;\n"
+ " gl_Position = uProjectionMatrix * vec4(aPosition, 0.0, 1.0);\n"
+ "}\n";
+
+static const char* kFragmentShaderSimpleVideoQuadString =
+ "#extension GL_OES_EGL_image_external_essl3 : enable\n"
+ " \n"
+ "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ " \n"
+ "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+ "uniform samplerExternalOES uDistortionTexture; \n"
+ "mediump in vec2 vTexCoords;\n"
+ "out vec4 fragColor;\n"
+ " \n"
+ "void main(void) { \n"
+ " if (clamp(vTexCoords, 0.0, 1.0) != vTexCoords) { \n"
+ " fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
+ " } else { \n"
+ " fragColor = texture(uDistortionTexture, vTexCoords); \n"
+ " } \n"
+ "} \n";
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// Note that converting from Clip Space ([-1,1]^3) to Viewport Space
+// for one eye ([0,1]x[0,1]) requires dividing by 2 in x and y.
+const mat4 DistortionRenderer::kViewportFromClipMatrix =
+ Eigen::Translation3f(vec3(0.5f, 0.5f, 0)) *
+ Eigen::DiagonalMatrix<float, 3>(vec3(0.5f, 0.5f, 1.0f));
+
+const mat4 DistortionRenderer::kClipFromViewportMatrix =
+ Eigen::DiagonalMatrix<float, 3>(vec3(2.0f, 2.0f, 1.0f)) *
+ Eigen::Translation3f(vec3(-0.5f, -0.5f, 0));
+
+void DistortionRenderer::EdsShader::load(const char* vertex,
+ const char* fragment, int num_layers,
+ bool use_alpha_vignette,
+ float rotation, bool flip_vertical,
+ bool blend_with_previous_layer) {
+ std::string vert_builder = "#version 310 es\n";
+ std::string frag_builder = "#version 310 es\n";
+ if (blend_with_previous_layer && kUseFramebufferReadback) {
+ frag_builder += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
+ }
+
+ if (num_layers == 2) {
+ vert_builder += "#define COMPOSITE_LAYER_2\n";
+ frag_builder += "#define COMPOSITE_LAYER_2\n";
+ } else {
+ CHECK_EQ(num_layers, 1);
+ }
+ if (blend_with_previous_layer) {
+ // Check for unsupported shader combinations:
+ CHECK_EQ(num_layers, 1);
+ CHECK_EQ(use_alpha_vignette, false);
+ if (kUseFramebufferReadback)
+ frag_builder += "#define BLEND_WITH_PREVIOUS_LAYER\n";
+ }
+ if (use_alpha_vignette) {
+ vert_builder += "#define ALPHA_VIGNETTE\n";
+ frag_builder += "#define ALPHA_VIGNETTE\n";
+ }
+
+ vert_builder += vertex;
+ frag_builder += fragment;
+ pgm.Link(vert_builder, frag_builder);
+ CHECK(pgm.IsUsable());
+
+ pgm.Use();
+
+ uProjectionMatrix =
+ glGetUniformLocation(pgm.GetProgram(), "uProjectionMatrix");
+ uTexFromEyeMatrix =
+ glGetUniformLocation(pgm.GetProgram(), "uTexFromEyeMatrix");
+ uEyeFromViewportMatrix =
+ glGetUniformLocation(pgm.GetProgram(), "uEyeFromViewportMatrix");
+ uTexXMinMax = glGetUniformLocation(pgm.GetProgram(), "uTexXMinMax");
+ CHECK_GL();
+
+ float vertical_multiply = flip_vertical ? -1.0 : 1.0;
+ mat4 projectionMatrix = OrthoMatrix(-0.5f, 0.5f, vertical_multiply * -0.5f,
+ vertical_multiply * 0.5f, -1.0f, 1.0f);
+
+ // Rotate the mesh into the screen's orientation.
+ // TODO(hendrikw): Once the display is finalized, and perhaps not portrait,
+ // look into removing this matrix altogether.
+ projectionMatrix =
+ projectionMatrix * Eigen::AngleAxisf(rotation, vec3::UnitZ());
+
+ CHECK(sizeof(mat4) == 4 * 4 * 4);
+ glUniformMatrix4fv(uProjectionMatrix, 1, false, projectionMatrix.data());
+}
+
+DistortionRenderer::DistortionRenderer(
+ const CompositeHmd& hmd, vec2i display_size, int distortion_mesh_resolution,
+ bool flip_texture_horizontally, bool flip_texture_vertically,
+ bool separated_eye_buffers, bool eds_enabled, bool late_latch_enabled)
+ : shader_type_(kChromaticAberrationCorrection),
+ eds_enabled_(eds_enabled),
+ chromatic_aberration_correction_enabled_(true),
+ use_alpha_vignette_(false),
+ distortion_mesh_resolution_(distortion_mesh_resolution),
+ last_distortion_texture_id_(0),
+ app_texture_target_(GL_TEXTURE_2D),
+ display_size_(display_size),
+ separated_eye_buffers_(separated_eye_buffers) {
+ ATRACE_NAME("DistortionRenderer::DistortionRenderer");
+
+ float device_rotation = 0.0;
+
+ if (eds_enabled_) {
+ // Late latch must be on if eds_enabled_ is true.
+ if (!late_latch_enabled) {
+ LOG(ERROR) << "Cannot enable EDS without late latch. "
+ << "Force enabling late latch.";
+ late_latch_enabled = true;
+ }
+ }
+
+ // TODO(hendrikw): Look into moving this logic into DisplayMetrics.
+ if (hmd.GetDisplayMetrics().IsPortrait()) {
+ device_rotation = -M_PI / 2.0f;
+ }
+
+ // Create shader programs.
+ shaders_[kNoChromaticAberrationCorrection].load(
+ kVertexShaderNoChromaticAberrationString,
+ kFragmentShaderNoChromaticAberrationString, 1, false, device_rotation,
+ flip_texture_horizontally, false);
+ shaders_[kNoChromaticAberrationCorrectionTwoLayers].load(
+ kVertexShaderNoChromaticAberrationString,
+ kFragmentShaderNoChromaticAberrationString, 2, false, device_rotation,
+ flip_texture_horizontally, false);
+ shaders_[kChromaticAberrationCorrection].load(
+ kVertexShaderChromaticAberrationString,
+ kFragmentShaderChromaticAberrationString, 1, false, device_rotation,
+ flip_texture_horizontally, false);
+ shaders_[kChromaticAberrationCorrectionTwoLayers].load(
+ kVertexShaderChromaticAberrationString,
+ kFragmentShaderChromaticAberrationString, 2, false, device_rotation,
+ flip_texture_horizontally, false);
+ shaders_[kChromaticAberrationCorrectionAlphaVignette].load(
+ kVertexShaderChromaticAberrationString,
+ kFragmentShaderChromaticAberrationString, 1, true, device_rotation,
+ flip_texture_horizontally, false);
+ shaders_[kChromaticAberrationCorrectionAlphaVignetteTwoLayers].load(
+ kVertexShaderChromaticAberrationString,
+ kFragmentShaderChromaticAberrationString, 2, true, device_rotation,
+ flip_texture_horizontally, false);
+ shaders_[kChromaticAberrationCorrectionWithBlend].load(
+ kVertexShaderChromaticAberrationString,
+ kFragmentShaderChromaticAberrationString, 1, false, device_rotation,
+ flip_texture_horizontally, true);
+ shaders_[kSimpleVideoQuad].load(
+ kVertexShaderSimpleVideoQuadString,
+ kFragmentShaderSimpleVideoQuadString, 1, false, device_rotation,
+ flip_texture_horizontally, true);
+ CHECK_GL();
+
+ mat4 tex_from_recommended_viewport_matrix[2][2][2];
+ for (int eye = 0; eye < 2; ++eye) {
+ // Near and far plane don't actually matter for the clip_from_eye_matrix
+ // below since it is only used (for EDS) to transform coordinates for
+ // which the Z has been dropped.
+ static const float kNear = 0.1f, kFar = 100.0f;
+ const FieldOfView& fov =
+ (eye == kLeftEye ? hmd.GetEyeFov(kLeftEye) : hmd.GetEyeFov(kRightEye));
+ mat4 c_clip_from_eye_matrix = fov.GetProjectionMatrix(kNear, kFar);
+ mat4 c_eye_from_clip_matrix = c_clip_from_eye_matrix.inverse();
+
+ // Compute tex_from_recommended_viewport_matrix.
+
+ // flip_texture_vertically defines the default flip behavior.
+ // do_flip[0] should be the default, while do_flip[1] should be the
+ // inverse of the default.
+ int do_flip[2] = {flip_texture_vertically ? 1 : 0,
+ flip_texture_vertically ? 0 : 1};
+ for (int flip = 0; flip < 2; ++flip) {
+ vec2 flip_scale(1.0f, do_flip[flip] ? -1.0f : 1.0f);
+ vec2 flip_offset(0.0f, do_flip[flip] ? 1.0f : 0.0f);
+
+ for (int separate_eye = 0; separate_eye < 2; ++separate_eye) {
+ vec2 viewport_corner_offset = (eye == kLeftEye || separate_eye)
+ ? vec2(0.0f, 0.0f)
+ : vec2(0.5f, 0.0f);
+ const vec2 txy = viewport_corner_offset + flip_offset;
+ const vec2 scalexy = vec2(separate_eye ? 1.0f : 0.5f, 1.0f);
+ tex_from_recommended_viewport_matrix[eye][flip][separate_eye] =
+ Eigen::Translation3f(vec3(txy.x(), txy.y(), 0.0f)) *
+ Eigen::DiagonalMatrix<float, 3>(vec3(flip_scale.x() * scalexy.x(),
+ flip_scale.y(), scalexy.y()));
+
+ tex_from_eye_matrix_[eye][flip][separate_eye] =
+ tex_from_recommended_viewport_matrix[eye][flip][separate_eye] *
+ kViewportFromClipMatrix * c_clip_from_eye_matrix;
+ }
+ }
+
+ eye_from_viewport_matrix_[eye] =
+ c_eye_from_clip_matrix * kClipFromViewportMatrix;
+ }
+
+ // Create UBO for setting the EDS matrix to identity when EDS is disabled.
+ glGenBuffers(2 * 2 * 2, &uTexFromRecommendedViewportMatrix[0][0][0]);
+ for (int eye = 0; eye < 2; ++eye) {
+ for (int flip = 0; flip < 2; ++flip) {
+ for (int separate_eye = 0; separate_eye < 2; ++separate_eye) {
+ glBindBuffer(
+ GL_UNIFORM_BUFFER,
+ uTexFromRecommendedViewportMatrix[eye][flip][separate_eye]);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(mat4), 0, GL_STATIC_DRAW);
+ CHECK_GL();
+ mat4* mat = static_cast<mat4*>(glMapBufferRange(
+ GL_UNIFORM_BUFFER, 0, sizeof(mat4), GL_MAP_WRITE_BIT));
+ CHECK_GL();
+ *mat = tex_from_recommended_viewport_matrix[eye][flip][separate_eye];
+ glUnmapBuffer(GL_UNIFORM_BUFFER);
+ }
+ }
+ }
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ // Create distortion meshes and associated GL resources.
+ glGenBuffers(2, mesh_vbo_);
+ glGenVertexArrays(2, mesh_vao_);
+ glGenBuffers(2, mesh_ibo_);
+ RecomputeDistortion(hmd);
+
+ SetDisplaySize(display_size);
+
+ if (hmd.GetDisplayMetrics().IsPortrait()) {
+ eye_viewport_origin_[0] =
+ vec2i(0, flip_texture_horizontally ? 0 : display_size_[1] / 2);
+ eye_viewport_origin_[1] =
+ vec2i(0, flip_texture_horizontally ? display_size_[1] / 2 : 0);
+ eye_viewport_size_ = vec2i(display_size_[0], display_size_[1] / 2);
+ } else {
+ eye_viewport_origin_[0] = vec2i(0, 0);
+ eye_viewport_origin_[1] = vec2i(display_size_[0] / 2, 0);
+ eye_viewport_size_ = vec2i(display_size_[0] / 2, display_size_[1]);
+ }
+
+ CHECK_GL();
+}
+
+DistortionRenderer::~DistortionRenderer() {
+ glDeleteBuffers(2 * 2 * 2, &uTexFromRecommendedViewportMatrix[0][0][0]);
+ glDeleteBuffers(2, mesh_vbo_);
+ glDeleteVertexArrays(2, mesh_vao_);
+ glDeleteBuffers(2, mesh_ibo_);
+}
+
+void DistortionRenderer::ApplyDistortionCorrectionToTexture(
+ EyeType eye, const GLuint* texture_ids, const bool* vertical_flip,
+ const bool* separate_eye, const int* late_latch_layer, int num_textures,
+ bool blend_with_previous_layer, bool do_gl_state_prep) {
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+
+ bool use_gl_blend = use_alpha_vignette_ ||
+ (blend_with_previous_layer && !kUseFramebufferReadback);
+ if (use_gl_blend) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ DrawEye(eye, texture_ids, vertical_flip, separate_eye, late_latch_layer,
+ num_textures, blend_with_previous_layer, do_gl_state_prep);
+ if (use_gl_blend) {
+ glDisable(GL_BLEND);
+ }
+ CHECK_GL();
+}
+
+void DistortionRenderer::DrawVideoQuad(EyeType eye, int layer_i,
+ GLuint texture_id,
+ const mat4& transform) {
+ shaders_[kSimpleVideoQuad].use();
+
+ shaders_[kSimpleVideoQuad].SetTexFromEyeTransform(
+ tex_from_eye_matrix_[eye][0][1]);
+ shaders_[kSimpleVideoQuad].SetEyeFromViewportTransform(
+ transform * kClipFromViewportMatrix);
+
+ if (eds_enabled_) {
+ // Bind late latch view-projection UBO that is produced by AddEdsLateLatch.
+ late_latch_[layer_i]->BindUniformBuffer(
+ POSE_BINDING, LateLatch::kViewMatrix, eye);
+ CHECK_GL();
+ } else {
+ // When EDS is disabled we just set the matrix here with no pose offset.
+ glBindBufferBase(GL_UNIFORM_BUFFER, POSE_BINDING + layer_i,
+ uTexFromRecommendedViewportMatrix[eye][0][1]);
+ CHECK_GL();
+ }
+
+ glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
+ CHECK_GL();
+
+ glDrawElements(GL_TRIANGLE_STRIP, mesh_node_[eye].indices.size(),
+ GL_UNSIGNED_SHORT, nullptr);
+
+ CHECK_GL();
+}
+
+void DistortionRenderer::DoLateLatch(uint32_t target_vsync_count,
+ const uint32_t* render_buffer_index,
+ const GLuint* render_pose_buffer_objects,
+ const bool* vertical_flip,
+ const bool* separate_eye,
+ int num_textures) {
+ if (eds_enabled_) {
+ LateLatchInput data;
+ memset(&data, 0, sizeof(data));
+ for (int ti = 0; ti < num_textures; ++ti) {
+ if (late_latch_[ti] == nullptr)
+ late_latch_[ti].reset(new LateLatch(false));
+
+ int flip_index = vertical_flip[ti] ? 1 : 0;
+ int separate_eye_i = separate_eye[ti] ? 1 : 0;
+ // Copy data into late latch input struct.
+ for (int eye = 0; eye < 2; ++eye) {
+ data.eds_mat1[eye] =
+ tex_from_eye_matrix_[eye][flip_index][separate_eye_i];
+ data.eds_mat2[eye] = eye_from_viewport_matrix_[eye];
+ }
+ data.pose_index = target_vsync_count & kPoseAsyncBufferIndexMask;
+ data.render_pose_index = render_buffer_index[ti];
+
+ late_latch_[ti]->AddEdsLateLatch(data, render_pose_buffer_objects[ti]);
+ }
+ }
+}
+
+void DistortionRenderer::PrepGlState(EyeType eye) {
+ glViewport(eye_viewport_origin_[eye].x(), eye_viewport_origin_[eye].y(),
+ eye_viewport_size_.x(), eye_viewport_size_.y());
+
+ glBindVertexArray(mesh_vao_[eye]);
+ CHECK_GL();
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_[eye]);
+ CHECK_GL();
+
+ if (!eds_enabled_) {
+ glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+ }
+}
+
+void DistortionRenderer::ResetGlState(int num_textures) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ if (eds_enabled_) {
+ for (int ti = 0; ti < num_textures; ++ti)
+ glBindBufferBase(GL_UNIFORM_BUFFER, POSE_BINDING + ti, 0);
+ } else {
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ }
+
+ CHECK_GL();
+
+ // Unbind all texture inputs.
+ for (int ti = 0; ti < num_textures; ++ti) {
+ glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING + ti);
+ glBindTexture(app_texture_target_, 0);
+ }
+ glActiveTexture(GL_TEXTURE0);
+}
+
+void DistortionRenderer::DrawEye(EyeType eye, const GLuint* texture_ids,
+ const bool* vertical_flip,
+ const bool* separate_eye,
+ const int* late_latch_layer, int num_textures,
+ bool blend_with_previous_layer,
+ bool do_gl_state_prep) {
+ if (do_gl_state_prep)
+ PrepGlState(eye);
+
+ if (num_textures > kMaxLayers) {
+ LOG(ERROR) << "Too many textures for DistortionRenderer";
+ num_textures = kMaxLayers;
+ }
+
+ CHECK(num_textures == 1 || num_textures == 2);
+
+ if (num_textures == 2) {
+ if (chromatic_aberration_correction_enabled_) {
+ if (use_alpha_vignette_) {
+ shader_type_ = kChromaticAberrationCorrectionAlphaVignetteTwoLayers;
+ } else {
+ shader_type_ = kChromaticAberrationCorrectionTwoLayers;
+ }
+ } else {
+ shader_type_ = kNoChromaticAberrationCorrectionTwoLayers;
+ }
+ } else {
+ if (chromatic_aberration_correction_enabled_) {
+ if (blend_with_previous_layer) {
+ shader_type_ = kChromaticAberrationCorrectionWithBlend;
+ } else if (use_alpha_vignette_) {
+ shader_type_ = kChromaticAberrationCorrectionAlphaVignette;
+ } else {
+ shader_type_ = kChromaticAberrationCorrection;
+ }
+ } else {
+ shader_type_ = kNoChromaticAberrationCorrection;
+ }
+ }
+ shaders_[shader_type_].use();
+
+ for (int ti = 0; ti < num_textures; ++ti) {
+ int flip_index = vertical_flip[ti] ? 1 : 0;
+ if (eds_enabled_) {
+ // Bind late latch view-projection UBO that is produced by
+ // AddEdsLateLatch.
+ late_latch_[late_latch_layer[ti]]->BindUniformBuffer(
+ POSE_BINDING + ti, LateLatch::kViewProjMatrix, eye);
+ CHECK_GL();
+ } else {
+ // When EDS is disabled we just set the matrix here with no pose offset.
+ // With app late-latching, we can't know the pose that the app used
+ // because it's in the app's framebuffer.
+ int separate_eye_i = separate_eye[ti] ? 1 : 0;
+ glBindBufferBase(
+ GL_UNIFORM_BUFFER, POSE_BINDING + ti,
+ uTexFromRecommendedViewportMatrix[eye][flip_index][separate_eye_i]);
+ CHECK_GL();
+ }
+
+ glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING + ti);
+ glBindTexture(app_texture_target_, texture_ids[ti]);
+ CHECK_GL();
+ }
+
+ // Prevents left eye data from bleeding into right eye and vice-versa.
+ vec2 layer_min_max[kMaxLayers];
+ for (int i = 0; i < kMaxLayers; ++i)
+ layer_min_max[i] = vec2(0.0f, 0.0f);
+ for (int ti = 0; ti < num_textures; ++ti) {
+ if (separate_eye[ti]) {
+ layer_min_max[ti] = vec2(0.0f, 1.0f); // Use the whole texture.
+ } else if (eye == kLeftEye) {
+ layer_min_max[ti] = vec2(0.0f, 0.499f);
+ } else {
+ layer_min_max[ti] = vec2(0.501f, 1.0f);
+ }
+ }
+ // The second layer stores its x min and max in the z,w slots of the vec4.
+ vec4 xTexMinMax(layer_min_max[0].x(), layer_min_max[0].y(),
+ layer_min_max[1].x(), layer_min_max[1].y());
+
+ glUniform4fv(shaders_[shader_type_].uTexXMinMax, 1, &xTexMinMax[0]);
+ CHECK_GL();
+
+ glDrawElements(GL_TRIANGLE_STRIP, mesh_node_[eye].indices.size(),
+ GL_UNSIGNED_SHORT, nullptr);
+ CHECK_GL();
+ if (do_gl_state_prep)
+ ResetGlState(num_textures);
+}
+
+void DistortionRenderer::SetDisplaySize(vec2i display_size) {
+ display_size_ = display_size;
+}
+
+void DistortionRenderer::SetEdsEnabled(bool enabled) { eds_enabled_ = enabled; }
+
+void DistortionRenderer::RecomputeDistortion(const CompositeHmd& hmd) {
+ using std::placeholders::_1;
+ using std::placeholders::_2;
+ using std::placeholders::_3;
+ using std::placeholders::_4;
+ DistortionFunction distortion_function =
+ std::bind(&CompositeHmd::ComputeDistortedVertex, &hmd, _1, _2, _3, _4);
+
+ for (int i = 0; i < 2; ++i) {
+ mesh_node_[i] =
+ BuildDistortionMesh(static_cast<EyeType>(i),
+ distortion_mesh_resolution_, distortion_function);
+
+ glBindVertexArray(mesh_vao_[i]);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_[i]);
+ glBufferData(GL_ARRAY_BUFFER,
+ sizeof(EdsVertex) * mesh_node_[i].vertices.size(),
+ &mesh_node_[i].vertices.front(), GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(POSITION_ATTR);
+ glEnableVertexAttribArray(VIEWPORT_COORD_R_ATTR);
+ glEnableVertexAttribArray(VIEWPORT_COORD_G_ATTR);
+ glEnableVertexAttribArray(VIEWPORT_COORD_B_ATTR);
+
+ glVertexAttribPointer(
+ POSITION_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+ reinterpret_cast<void*>(offsetof(EdsVertex, position)));
+
+ glVertexAttribPointer(
+ VIEWPORT_COORD_R_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+ reinterpret_cast<void*>(offsetof(EdsVertex, red_viewport_coords)));
+
+ glVertexAttribPointer(
+ VIEWPORT_COORD_G_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+ reinterpret_cast<void*>(offsetof(EdsVertex, green_viewport_coords)));
+
+ glVertexAttribPointer(
+ VIEWPORT_COORD_B_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+ reinterpret_cast<void*>(offsetof(EdsVertex, blue_viewport_coords)));
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_[i]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(uint16_t) * mesh_node_[i].indices.size(),
+ &mesh_node_[i].indices.front(), GL_STATIC_DRAW);
+ CHECK_GL();
+ }
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glBindVertexArray(0);
+}
+
+bool DistortionRenderer::GetLastEdsPose(LateLatchOutput* out_data, int layer_id) const {
+ if (layer_id >= kMaxLayers) {
+ LOG(ERROR) << "Accessing invalid layer " << layer_id << std::endl;
+ return false;
+ }
+
+ if (late_latch_[layer_id] != nullptr) {
+ late_latch_[layer_id]->CaptureOutputData(out_data);
+ return true;
+ } else {
+ LOG(ERROR) << "Late latch shader not enabled." << std::endl;
+ return false;
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/eds.cpp b/libs/vr/libeds/eds.cpp
new file mode 100644
index 0000000..8af5b27
--- /dev/null
+++ b/libs/vr/libeds/eds.cpp
@@ -0,0 +1,35 @@
+#include <dvr/eds.h>
+
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/types.h>
+
+// TODO(jbates) delete this file and eds.h
+
+extern "C" int dvrEdsInit(bool with_late_latch) { return 0; }
+
+extern "C" void dvrEdsDeinit() {}
+
+extern "C" int dvrEdsCapturePoseAsync(int eye, uint32_t target_vsync_count,
+ const float* projection_matrix,
+ const float* eye_from_head_matrix,
+ const float* pose_offset_matrix) {
+ return 0;
+}
+
+extern "C" int dvrEdsBindPose(int eye, uint32_t ubo_binding, intptr_t offset,
+ ssize_t size) {
+ return 0;
+}
+
+extern "C" int dvrEdsBlitPose(int eye, int viewport_width,
+ int viewport_height) {
+ return 0;
+}
+
+extern "C" int dvrEdsBlitPoseFromCpu(int eye, int viewport_width,
+ int viewport_height,
+ const float* pose_quaternion,
+ const float* pose_position) {
+ return 0;
+}
diff --git a/libs/vr/libeds/eds_mesh.cpp b/libs/vr/libeds/eds_mesh.cpp
new file mode 100644
index 0000000..2c7dc2f
--- /dev/null
+++ b/libs/vr/libeds/eds_mesh.cpp
@@ -0,0 +1,136 @@
+#include "include/private/dvr/eds_mesh.h"
+
+#include <math.h>
+
+#include <base/logging.h>
+#include <private/dvr/types.h>
+
+namespace {
+
+using android::dvr::EdsVertex;
+using android::dvr::EyeType;
+using android::dvr::DistortionFunction;
+using android::dvr::vec2;
+
+// Computes the vertices for a distortion mesh with resolution |resolution| and
+// distortion provided by |hmd| and stores them in |vertices|.
+static void ComputeDistortionMeshVertices(
+ EdsVertex* vertices, int resolution,
+ const DistortionFunction& distortion_function, EyeType eye) {
+ for (int row = 0; row < resolution; row++) {
+ for (int col = 0; col < resolution; col++) {
+ const float x_norm =
+ static_cast<float>(col) / (static_cast<float>(resolution - 1U));
+ const float y_norm =
+ static_cast<float>(row) / (static_cast<float>(resolution - 1U));
+
+ const vec2 xy_norm(x_norm, y_norm);
+ const size_t index = col * resolution + row;
+
+ // Evaluate distortion function to get the new coordinates for each color
+ // channel. The distortion function returns the new coordinates relative
+ // to a full viewport with 0 <= x <= 1 for each eye.
+ vec2 coords[3];
+ distortion_function(eye, xy_norm, &vertices[index].position, coords);
+
+ // Store distortion mapping in texture coordinates.
+ vertices[index].red_viewport_coords = coords[0];
+ vertices[index].green_viewport_coords = coords[1];
+ vertices[index].blue_viewport_coords = coords[2];
+ }
+ }
+}
+
+// Computes the triangle strip indices for a distortion mesh with resolution
+// |resolution| and stores them in |indices|.
+static void ComputeDistortionMeshIndices(uint16_t* indices, int resolution) {
+ // The following strip method has been used in the Cardboard SDK
+ // (java/com/google/vrtoolkit/cardboard/DistortionRenderer.java) and has
+ // originally been described at:
+ //
+ // http://dan.lecocq.us/wordpress/2009/12/25/triangle-strip-for-grids-a-construction/
+ //
+ // For a grid with 4 rows and 4 columns of vertices, the strip would
+ // look like:
+ // ↻
+ // 0 - 4 - 8 - 12
+ // ↓ ↗ ↓ ↗ ↓ ↗ ↓
+ // 1 - 5 - 9 - 13
+ // ↓ ↖ ↓ ↖ ↓ ↖ ↓
+ // 2 - 6 - 10 - 14
+ // ↓ ↗ ↓ ↗ ↓ ↗ ↓
+ // 3 - 7 - 11 - 15
+ // ↺
+ //
+ // Note the little circular arrows next to 7 and 8 that indicate
+ // repeating that vertex once so as to produce degenerate triangles.
+ //
+ // To facilitate scanline racing, the vertex order is left to right.
+
+ int16_t index_offset = 0;
+ int16_t vertex_offset = 0;
+ for (int row = 0; row < resolution - 1; ++row) {
+ if (row > 0) {
+ indices[index_offset] = indices[index_offset - 1];
+ ++index_offset;
+ }
+ for (int col = 0; col < resolution; ++col) {
+ if (col > 0) {
+ if (row % 2 == 0) {
+ // Move right on even rows.
+ ++vertex_offset;
+ } else {
+ --vertex_offset;
+ }
+ }
+ // A cast to uint16_t is safe here as |vertex_offset| will not drop below
+ // zero in this loop. As col is initially equal to zero |vertex_offset| is
+ // always incremented before being decremented, is initialized to zero and
+ // is only incremented outside of the loop.
+ indices[index_offset++] = static_cast<uint16_t>(vertex_offset);
+ indices[index_offset++] = static_cast<uint16_t>(
+ vertex_offset + static_cast<int16_t>(resolution));
+ }
+ vertex_offset =
+ static_cast<int16_t>(static_cast<int>(resolution) + vertex_offset);
+ }
+}
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// Builds a distortion mesh of resolution |resolution| using the distortion
+// provided by |hmd| for |eye|.
+EdsMesh BuildDistortionMesh(EyeType eye, int resolution,
+ const DistortionFunction& distortion_function) {
+ CHECK_GT(resolution, 2);
+
+ // Number of indices produced by the strip method
+ // (see comment in ComputeDistortionMeshIndices):
+ //
+ // 1 vertex per triangle
+ // 2 triangles per quad, (rows - 1) * (cols - 1) quads
+ // 2 vertices at the start of each row for the first triangle
+ // 1 extra vertex per row (except first and last) for a
+ // degenerate triangle
+ //
+ const uint16_t index_count =
+ static_cast<uint16_t>(resolution * (2 * resolution - 1U) - 2U);
+ const uint16_t vertex_count = static_cast<uint16_t>(resolution * resolution);
+
+ EdsMesh mesh;
+ mesh.vertices.resize(vertex_count);
+ mesh.indices.resize(index_count);
+
+ // Populate vertex and index buffer.
+ ComputeDistortionMeshVertices(&mesh.vertices[0], resolution,
+ distortion_function, eye);
+ ComputeDistortionMeshIndices(&mesh.indices[0], resolution);
+
+ return mesh;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/include/CPPLINT.cfg b/libs/vr/libeds/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libeds/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libeds/include/dvr/eds.h b/libs/vr/libeds/include/dvr/eds.h
new file mode 100644
index 0000000..37b1297
--- /dev/null
+++ b/libs/vr/libeds/include/dvr/eds.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_DVR_EDS_H_
+#define ANDROID_DVR_EDS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// This struct aligns with GLSL uniform blocks with std140 layout.
+// std140 allows padding between certain types, so padding must be explicitly
+// added as struct members.
+struct __attribute__((__packed__)) DvrLateLatchData {
+ // Column-major order.
+ float view_proj_matrix[16];
+ // Column-major order.
+ float view_matrix[16];
+ float pose_quaternion[4];
+ float pose_position[4];
+};
+
+//
+// These APIs are not thread safe and must be called on a single thread with an
+// actively bound GL context corresponding to a display surface.
+//
+
+// Prepares EDS and Late Latching system. Idempotent if called more than once.
+// The target GL context must be created and bound.
+//
+// If |with_late_latch| is true, a thread will be created that asynchronously
+// updates the pose in memory.
+//
+// The following GL states are modified as follows:
+// glBindBuffer(GL_ARRAY_BUFFER, 0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+//
+// Returns 0 on success, negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsInit(bool with_late_latch);
+
+// Stops and destroys the EDS Late Latching system.
+void dvrEdsDeinit();
+
+// Submits GL draw command that will capture the latest head pose into a uniform
+// buffer object. This should be called twice per frame, before the app begins
+// drawing for each eye.
+// For each eye, a later call to dvrEdsBlitPose will write this pose into
+// the application framebuffer corner so that the EDS service knows what pose
+// the frame was rendered with.
+//
+// |eye| is 0 for left eye and 1 for right eye.
+//
+// The following GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, id);
+// glDisable(GL_RASTERIZER_DISCARD);
+//
+// Returns 0 on success, negative error code on failure:
+// EPERM - dvrEdsInit(true) was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsCapturePoseAsync(int eye, uint32_t target_vsync_count,
+ const float* projection_matrix,
+ const float* eye_from_head_matrix,
+ const float* pose_offset_matrix);
+
+// Binds the late-latch output data as a GL_UNIFORM_BUFFER so that your vertex
+// shaders can use the latest head pose. For example, to bind just the
+// view_matrix from the output:
+//
+// dvrEdsBindPose(eye, BINDING,
+// offsetof(DvrLateLatchData, view_matrix),
+// sizeof(DvrLateLatchData::view_matrix));
+//
+// Or more commonly, bind the view projection matrix:
+//
+// dvrEdsBindPose(eye, BINDING,
+// offsetof(DvrLateLatchData, view_proj_matrix),
+// sizeof(DvrLateLatchData::view_proj_matrix));
+//
+// BINDING in the above examples is the binding location of the uniform
+// interface block in the GLSL shader.
+//
+// Shader example (3 would be the |ubo_binding| passed to this function):
+// layout(binding = 3, std140) uniform LateLatchData {
+// mat4 uViewProjection;
+// };
+//
+// |eye| is 0 for left eye and 1 for right eye.
+//
+// The following GL states are modified as follows:
+// glBindBuffer(GL_UNIFORM_BUFFER, ...);
+// glBindBufferRange(GL_UNIFORM_BUFFER, ...);
+//
+// To clear the binding, call glBindBuffer(GL_UNIFORM_BUFFER, 0);
+//
+// Returns 0 on success, negative error code on failure:
+// EPERM - dvrEdsInit(true) was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBindPose(int eye, uint32_t ubo_binding, intptr_t offset,
+ ssize_t size);
+
+// DEPRECATED
+//
+// Blits the pose captured previously into the currently bound framebuffer.
+// The current framebuffer is assumed to be the default framebuffer 0, the
+// surface that will be sent to the display and have EDS and lens warp applied
+// to it.
+//
+// |eye| is 0 for left eye and 1 for right eye.
+// |viewport_width| is the width of the viewport for this eye, which is
+// usually half the width of the framebuffer.
+// |viewport_height| is the height of the viewport for this eye, which is
+// usually the height of the framebuffer.
+//
+// The following GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferRange(GL_UNIFORM_BUFFER, 23, ...);
+//
+// Returns 0 on success, negative error code on failure:
+// EPERM - dvrEdsInit was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBlitPose(int eye, int viewport_width, int viewport_height);
+
+// DEPRECATED
+//
+// Same as dvrEdsBlitPose except that the pose is provided as an
+// parameter instead of getting it from dvrEdsBindPose. This is for
+// applications that want EDS but do not want late-latching.
+//
+// |pose_quaternion| should point to 4 floats that represent a quaternion.
+// |pose_position| should point to 3 floats that represent x,y,z position.
+//
+// GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferBase(GL_UNIFORM_BUFFER, 23, ...);
+//
+// Returns 0 on success, negative error code on failure:
+// EPERM - dvrEdsInit was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBlitPoseFromCpu(int eye, int viewport_width, int viewport_height,
+ const float* pose_quaternion,
+ const float* pose_position);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_EDS_H_
diff --git a/libs/vr/libeds/include/private/dvr/color_channel_distortion.h b/libs/vr/libeds/include/private/dvr/color_channel_distortion.h
new file mode 100644
index 0000000..4e612cd
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/color_channel_distortion.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
+#define ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// ColorChannelDistortion encapsulates the way one color channel (wavelength)
+// is distorted optically when an image is viewed through a lens.
+class ColorChannelDistortion {
+ public:
+ virtual ~ColorChannelDistortion() {}
+
+ // Given a 2d point p, returns the corresponding distorted point.
+ // The units of both the input and output points are tan-angle units,
+ // which can be computed as the distance on the screen divided by
+ // distance from the virtual eye to the screen. For both the input
+ // and output points, the intersection of the optical axis of the lens
+ // with the screen defines the origin, the x axis points right, and
+ // the y axis points up.
+ virtual vec2 Distort(vec2 p) const = 0;
+
+ virtual vec2 DistortInverse(vec2 p) const = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/composite_hmd.h b/libs/vr/libeds/include/private/dvr/composite_hmd.h
new file mode 100644
index 0000000..70727e0
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/composite_hmd.h
@@ -0,0 +1,89 @@
+#ifndef ANDROID_DVR_COMPOSITE_HMD_H_
+#define ANDROID_DVR_COMPOSITE_HMD_H_
+
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// An intermediate structure composed of a head mount (described by
+// HeadMountMetrics) and a display (described by DisplayMetrics).
+class CompositeHmd {
+ public:
+ // Constructs a new CompositeHmd given a HeadMountMetrics and a
+ // DisplayMetrics.
+ CompositeHmd(const HeadMountMetrics& head_mount_metrics,
+ const DisplayMetrics& display_metrics);
+
+ CompositeHmd(CompositeHmd&& composite_hmd) = delete;
+ CompositeHmd(const CompositeHmd& composite_hmd) = delete;
+ CompositeHmd& operator=(CompositeHmd&& composite_hmd) = delete;
+ CompositeHmd& operator=(const CompositeHmd& composite_hmd) = delete;
+
+ // Headset metadata.
+ float GetTargetFrameDuration() const;
+ void ComputeDistortedVertex(EyeType eye, vec2 uv_in, vec2* vertex_out,
+ vec2* uv_out) const;
+
+ // Eye-unspecific view accessors.
+ vec2i GetRecommendedRenderTargetSize() const;
+ Range2i GetDisplayRange() const;
+
+ // Eye-specific view accessors.
+ mat4 GetEyeFromHeadMatrix(EyeType eye) const;
+ FieldOfView GetEyeFov(EyeType eye) const;
+ Range2i GetEyeViewportBounds(EyeType eye) const;
+
+ // Set HeadMountMetrics and recompute everything that depends on
+ // HeadMountMetrics.
+ void SetHeadMountMetrics(const HeadMountMetrics& head_mount_metrics);
+
+ // Returns a reference to the |head_mount_metrics_| member.
+ const HeadMountMetrics& GetHeadMountMetrics() const;
+
+ // Set DisplayMetrics and recompute everything that depends on DisplayMetrics.
+ void SetDisplayMetrics(const DisplayMetrics& display_metrics);
+
+ // Returns a reference to the current display metrics.
+ const DisplayMetrics& GetDisplayMetrics() const;
+
+ // Compute the distorted point for a single channel.
+ vec2 ComputeDistortedPoint(EyeType eye, vec2 position,
+ RgbColorChannel channel) const;
+
+ // Compute the inverse distorted point for a single channel.
+ vec2 ComputeInverseDistortedPoint(EyeType eye, vec2 position,
+ RgbColorChannel channel) const;
+
+ private:
+ FieldOfView eye_fov_[2];
+ Range2i eye_viewport_range_[2];
+ mat4 eye_from_head_matrix_[2];
+ Range2i display_range_;
+ vec2i recommended_render_target_size_;
+
+ // Per-eye scale and translation to convert from normalized Screen Space
+ // ([0:1]x[0:1]) to tan-angle space.
+ mat3 eye_tan_angle_from_norm_screen_matrix_[2];
+ mat3 eye_tan_angle_from_norm_screen_inv_matrix_[2];
+
+ // Per-eye scale and translation to convert from tan-angle space to normalized
+ // Texture Space ([0:1]x[0:1]).
+ mat3 eye_norm_texture_from_tan_angle_matrix_[2];
+ mat3 eye_norm_texture_from_tan_angle_inv_matrix_[2];
+
+ HeadMountMetrics head_mount_metrics_;
+ DisplayMetrics display_metrics_;
+
+ // Called by SetHeadMountMetrics/SetDisplayMetrics after metrics get changed.
+ // This function will update head_mount_metrics_/display_metrics_ based on the
+ // metrics supplied in the above two methods.
+ void MetricsChanged();
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_COMPOSITE_HMD_H_
diff --git a/libs/vr/libeds/include/private/dvr/cpu_thread_pose_updater.h b/libs/vr/libeds/include/private/dvr/cpu_thread_pose_updater.h
new file mode 100644
index 0000000..6a2c8a6
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/cpu_thread_pose_updater.h
@@ -0,0 +1,48 @@
+#ifndef ANDROID_DVR_CPU_THREAD_POSE_UPDATER_H_
+#define ANDROID_DVR_CPU_THREAD_POSE_UPDATER_H_
+
+#include <atomic>
+#include <thread>
+
+#include <private/dvr/lucid_pose_tracker.h>
+#include <private/dvr/raw_pose.h>
+
+namespace android {
+namespace dvr {
+
+// Temporary version of pose updater that uses a CPU thread to update
+// the pose buffer. Note that this thread starts and runs indefinitely
+class CpuThreadPoseUpdater {
+ public:
+ CpuThreadPoseUpdater();
+ ~CpuThreadPoseUpdater();
+
+ // Start the thread to update the given buffer with the given number of
+ // microseconds between updates.
+ void Start(volatile RawPosePair* mapped_pose_buffer, int period_us);
+
+ void StopAndJoin();
+
+ private:
+ void UpdateThread();
+
+ volatile RawPosePair* mapped_pose_buffer_;
+
+ // Pose update thread.
+ std::thread update_thread_;
+
+ volatile bool stop_request_;
+
+ // Update period in microseconds.
+ int update_period_us_;
+
+ // Current pose count, used to avoid writing to the same buffer that is being
+ // read by the GPU.
+ uint32_t count_;
+ LucidPoseTracker pose_tracker_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_CPU_THREAD_POSE_UPDATER_H_
diff --git a/libs/vr/libeds/include/private/dvr/display_metrics.h b/libs/vr/libeds/include/private/dvr/display_metrics.h
new file mode 100644
index 0000000..87d9d04
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/display_metrics.h
@@ -0,0 +1,79 @@
+#ifndef ANDROID_DVR_DISPLAY_METRICS_H_
+#define ANDROID_DVR_DISPLAY_METRICS_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+enum class DisplayOrientation { kPortrait, kLandscape };
+
+// DisplayMetrics encapsulates metrics describing a display to be used
+// with a head mount to create a head mounted display.
+class DisplayMetrics {
+ public:
+ DisplayMetrics();
+ // Constructs a DisplayMetrics given a display size in pixels,
+ // meters per pixel, border size in meters, and frame duration in
+ // seconds.
+ //
+ // size_pixels The size of the display in pixels.
+ // meters_per_pixel The meters per pixel in each dimension.
+ // border_size_meters The size of the border around the display
+ // in meters. When the device sits on a surface in the proper
+ // orientation this is the distance from the surface to the edge
+ // of the display.
+ // frame_duration_seconds The duration in seconds of each frame
+ // (i.e., 1 / framerate).
+ DisplayMetrics(vec2i size_pixels, vec2 meters_per_pixel,
+ float border_size_meters, float frame_duration_seconds,
+ DisplayOrientation orientation);
+
+ // Gets the size of the display in physical pixels (not logical pixels).
+ vec2i GetSizePixels() const { return size_pixels_; }
+
+ DisplayOrientation GetOrientation() const { return orientation_; }
+ bool IsPortrait() const {
+ return orientation_ == DisplayOrientation::kPortrait;
+ }
+
+ // Gets the size of the display in meters.
+ vec2 GetSizeMeters() const {
+ return vec2(static_cast<float>(size_pixels_[0]),
+ static_cast<float>(size_pixels_[1]))
+ .array() *
+ meters_per_pixel_.array();
+ }
+
+ // Gets the meters per pixel.
+ vec2 GetMetersPerPixel() const { return meters_per_pixel_; }
+
+ // Gets the size of the border around the display.
+ // For a phone in landscape position this would be the distance from
+ // the bottom the edge of the phone to the bottom of the screen.
+ float GetBorderSizeMeters() const { return border_size_meters_; }
+
+ // Gets the frame duration in seconds for the display.
+ float GetFrameDurationSeconds() const { return frame_duration_seconds_; }
+
+ // Toggles the orientation and swaps all of the settings such that the
+ // display is being held in the other orientation.
+ void ToggleOrientation();
+
+ // Override the meters per pixel.
+ void SetMetersPerPixel(const vec2& meters_per_pixel) {
+ meters_per_pixel_ = meters_per_pixel;
+ }
+
+ private:
+ vec2i size_pixels_;
+ vec2 meters_per_pixel_;
+ float border_size_meters_;
+ float frame_duration_seconds_;
+ DisplayOrientation orientation_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/distortion_renderer.h b/libs/vr/libeds/include/private/dvr/distortion_renderer.h
new file mode 100644
index 0000000..e1c8114
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/distortion_renderer.h
@@ -0,0 +1,233 @@
+#ifndef ANDROID_DVR_DISTORTION_RENDERER_H_
+#define ANDROID_DVR_DISTORTION_RENDERER_H_
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <array>
+#include <functional>
+
+#include <private/dvr/eds_mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/lucid_pose_tracker.h>
+#include <private/dvr/render_texture_params.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class CompositeHmd;
+
+// Encapsulates the rendering operations to correct for the HMD's lens
+// distortion.
+class DistortionRenderer {
+ public:
+ static constexpr int kMaxLayers = 2;
+ static constexpr int kMaxLatchedLayers = 4;
+
+ static const mat4 kViewportFromClipMatrix;
+ static const mat4 kClipFromViewportMatrix;
+
+ // Creates a distortion renderer for distortion function.
+ //
+ // distortion_function the black-box distortion function to apply.
+ // display_size the resolution of the output of the distortion renderer.
+ // distortion_mesh_resolution the amount of subdivision in the
+ // distortion mesh.
+ DistortionRenderer(const CompositeHmd& hmd, vec2i display_size,
+ int distortion_mesh_resolution,
+ bool flip_texture_horizontally,
+ bool flip_texture_vertically, bool separated_eye_buffers,
+ bool eds_enabled, bool late_latch_enabled);
+ ~DistortionRenderer();
+
+ // Returns the distortion factor array for the distortion function that was
+ // passed in at creation time. The distortion factor array contains the
+ // magnification factor induced by the distortion mesh at every vertex. There
+ // is one entry per vertex, and entries are ordered in row-major major. The
+ // array contains the magnification for both eyes averaged.
+ const std::vector<float>& GetDistortionFactorArray();
+
+ // |render_pose_buffer_object| is the per-texture pose array buffer object.
+ // |render_buffer_index| is the per-texture index into the pose array buffer
+ // object. This selects which pose was rendered into the
+ // corresponding texture.
+ void DoLateLatch(uint32_t target_vsync_count,
+ const uint32_t* render_buffer_index,
+ const GLuint* render_pose_buffer_objects,
+ const bool* vertical_flip, const bool* separate_eye,
+ int num_textures);
+
+ // Convenience method that does no flipping.
+ void DoLateLatch(uint32_t target_vsync_count,
+ const uint32_t* render_buffer_index,
+ const GLuint* render_pose_buffer_objects, int num_textures) {
+ bool flip[kMaxLayers] = {false};
+ bool separate[kMaxLayers] = {separated_eye_buffers_};
+ DoLateLatch(target_vsync_count, render_buffer_index,
+ render_pose_buffer_objects, flip, separate, num_textures);
+ }
+
+ void PrepGlState(EyeType eye);
+ void ResetGlState(int num_textures);
+
+ // Applies distortion correction to the given textures by rendering into the
+ // current output target.
+ //
+ // eye Which eye is being corrected.
+ // texture_ids The OpenGL texture IDs of the texture layers.
+ // texture_sizes Dimensions of the corresponding textures.
+ // vertical_flip Whether to flip each input texture vertically.
+ // separate_eye Whether the correspending texture is a separate texture for
+ // left and right eyes. If false, it is a shared texture with
+ // the left view on the left half and right on the right half.
+ // late_latch_layer Which late latch layer index to use for each texture.
+ // Typically this is just {0, 1} unless blend_with_previous_layer is used.
+ // num_textures Number of textures in texture_ids and texture_sizes.
+ // blend_with_previous_layer If enabled, blend this single layer with the
+ // existing framebuffer contents.
+ void ApplyDistortionCorrectionToTexture(
+ EyeType eye, const GLuint* texture_ids, const bool* vertical_flip,
+ const bool* separate_eye, const int* late_latch_layer, int num_textures,
+ bool blend_with_previous_layer, bool do_gl_state_prep);
+
+ // Convenience method that does no flipping.
+ void ApplyDistortionCorrectionToTexture(EyeType eye,
+ const GLuint* texture_ids,
+ int num_textures) {
+ bool flip[kMaxLayers] = {false};
+ bool separate[kMaxLayers] = {separated_eye_buffers_,
+ separated_eye_buffers_};
+ int latch_layer[kMaxLayers] = {0, 1};
+ ApplyDistortionCorrectionToTexture(eye, texture_ids, flip, separate,
+ latch_layer, num_textures, false, true);
+ }
+
+ // Draw a video quad based on the given video texture by rendering into the
+ // current output target.
+ //
+ // eye Which eye is being corrected.
+ // layer_id Which compositor layer the video mesh should be drawn into.
+ // texture_ids The OpenGL texture IDs of the texture layers.
+ // transform The transformation matrix that transforms the video mesh to its
+ // desired eye space position for the target eye.
+ void DrawVideoQuad(EyeType eye, int layer_id, GLuint texture_id,
+ const mat4& transform);
+
+ // Modifies the size of the output display. This is the number of physical
+ // pixels per dimension covered by the display on the output device. Calling
+ // this method is cheap; it only updates the state table of the two
+ // eye-specific mesh nodes.
+ void SetDisplaySize(vec2i size);
+
+ void SetEdsEnabled(bool enabled);
+ void SetChromaticAberrationCorrectionEnabled(bool enabled) {
+ chromatic_aberration_correction_enabled_ = enabled;
+ }
+ void SetUseAlphaVignette(bool enabled) { use_alpha_vignette_ = enabled; }
+
+ bool GetLastEdsPose(LateLatchOutput* out_data, int layer_id = 0) const;
+
+ private:
+ enum ShaderProgramType {
+ kNoChromaticAberrationCorrection,
+ kNoChromaticAberrationCorrectionTwoLayers,
+ kChromaticAberrationCorrection,
+ kChromaticAberrationCorrectionTwoLayers,
+ kChromaticAberrationCorrectionAlphaVignette,
+ kChromaticAberrationCorrectionAlphaVignetteTwoLayers,
+ kChromaticAberrationCorrectionWithBlend,
+ kSimpleVideoQuad,
+ kNumShaderPrograms,
+ };
+
+ struct EdsShader {
+ EdsShader() {}
+ ~EdsShader() {
+ }
+
+ void load(const char* vertex, const char* fragment, int num_layers,
+ bool use_alpha_vignette, float rotation, bool flip_vertical,
+ bool blend_with_previous_layer);
+ void use() { pgm.Use(); }
+
+ // Update uTexFromEyeMatrix and uEyeFromViewportMatrix by the distortion
+ // renderer with the transform matrix.
+ void SetTexFromEyeTransform(const mat4& transform) {
+ glUniformMatrix4fv(uTexFromEyeMatrix, 1, false, transform.data());
+ }
+
+ void SetEyeFromViewportTransform(const mat4& transform) {
+ glUniformMatrix4fv(uEyeFromViewportMatrix, 1, false, transform.data());
+ }
+
+ ShaderProgram pgm;
+
+ // Texture variables, named to match shader strings for convenience.
+ GLint uProjectionMatrix;
+ GLint uTexFromEyeMatrix;
+ GLint uEyeFromViewportMatrix;
+ GLint uTexXMinMax;
+ };
+
+ void DrawEye(EyeType eye, const GLuint* texture_ids,
+ const bool* vertical_flip, const bool* separate_eye,
+ const int* late_latch_layer, int num_textures,
+ bool blend_with_previous_layer, bool do_gl_state_prep);
+
+ // This function is called when there is an update on Hmd and distortion mesh
+ // vertices and factor array will be updated.
+ void RecomputeDistortion(const CompositeHmd& hmd);
+
+ // Per-eye, per flip, per separate eye mode buffers for setting EDS matrix
+ // when EDS is disabled.
+ GLuint uTexFromRecommendedViewportMatrix[2][2][2];
+
+ // Distortion mesh for the each eye.
+ EdsMesh mesh_node_[2];
+ // VBO (vertex buffer object) for distortion mesh vertices.
+ GLuint mesh_vbo_[2];
+ // VAO (vertex array object) for distortion mesh vertex array data.
+ GLuint mesh_vao_[2];
+ // IBO (index buffer object) for distortion mesh indices.
+ GLuint mesh_ibo_[2];
+
+ EdsShader shaders_[kNumShaderPrograms];
+
+ // Enum to indicate which shader program is being used.
+ ShaderProgramType shader_type_;
+
+ bool eds_enabled_;
+ bool chromatic_aberration_correction_enabled_;
+ bool use_alpha_vignette_;
+
+ // This keeps track of what distortion mesh resolution we are using currently.
+ // When there is an update on Hmd, the distortion mesh vertices/factor array
+ // will be re-computed with the old resolution that is stored here.
+ int distortion_mesh_resolution_;
+
+ // The OpenGL ID of the last texture passed to
+ // ApplyDistortionCorrectionToTexture().
+ GLuint last_distortion_texture_id_;
+
+ // GL texture 2D target for application texture.
+ GLint app_texture_target_;
+
+ // Precomputed matrices for EDS and viewport transforms.
+ mat4 tex_from_eye_matrix_[2][2][2];
+ mat4 eye_from_viewport_matrix_[2];
+
+ // Eye viewport locations.
+ vec2i eye_viewport_origin_[2];
+ vec2i eye_viewport_size_;
+
+ vec2i display_size_;
+
+ std::unique_ptr<LateLatch> late_latch_[kMaxLatchedLayers];
+ bool separated_eye_buffers_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISTORTION_RENDERER_H_
diff --git a/libs/vr/libeds/include/private/dvr/eds_mesh.h b/libs/vr/libeds/include/private/dvr/eds_mesh.h
new file mode 100644
index 0000000..d2c901e
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/eds_mesh.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_DVR_EDS_MESH_H_
+#define ANDROID_DVR_EDS_MESH_H_
+
+#include <stdint.h>
+#include <functional>
+#include <vector>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+struct EdsVertex {
+ vec2 position;
+ vec2 red_viewport_coords;
+ vec2 green_viewport_coords;
+ vec2 blue_viewport_coords;
+};
+
+struct EdsMesh {
+ std::vector<EdsVertex> vertices;
+ std::vector<uint16_t> indices;
+};
+
+// Distortion function takes in a point in the range [0..1, 0..1] and returns
+// the vertex position and the three distorted points for separate R, G and B
+// channels.
+typedef std::function<void(EyeType, vec2, vec2*, vec2*)> DistortionFunction;
+
+// Builds a distortion mesh of resolution |resolution| using
+// the distortion provided by |hmd| for |eye|.
+EdsMesh BuildDistortionMesh(EyeType eye, int resolution,
+ const DistortionFunction& distortion_function);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_EDS_MESH_H_
diff --git a/libs/vr/libeds/include/private/dvr/head_mount_metrics.h b/libs/vr/libeds/include/private/dvr/head_mount_metrics.h
new file mode 100644
index 0000000..f3e63a6
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/head_mount_metrics.h
@@ -0,0 +1,134 @@
+#ifndef ANDROID_DVR_HEAD_MOUNT_METRICS_H_
+#define ANDROID_DVR_HEAD_MOUNT_METRICS_H_
+
+#include <array>
+
+#include <private/dvr/color_channel_distortion.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// HeadMountMetrics encapsulates metrics describing a head mount to be used
+// with a display to create a head mounted display.
+class HeadMountMetrics {
+ public:
+ // The vertical point of the HMD where the lens distance is measured from.
+ enum VerticalAlignment { kBottom = 0, kCenter = 1, kTop = 2 };
+
+ enum EyeOrientation {
+ kCCW0Degrees = 0,
+ kCCW90Degrees = 1,
+ kCCW180Degrees = 2,
+ kCCW270Degrees = 3,
+ kCCW0DegreesMirrored = 4,
+ kCCW90DegreesMirrored = 5,
+ kCCW180DegreesMirrored = 6,
+ kCCW270DegreesMirrored = 7,
+
+ // Rotations that consist of an odd number of 90 degree rotations will swap
+ // the height and width of any bounding boxes/viewports. This bit informs
+ // any viewport manipulating code to perform the appropriate transformation.
+ kRightAngleBit = 0x01,
+ // Viewports are represented as four floating point values (four half
+ // angles). Rotating this structure can be done through a shift operation.
+ // This mask extracts the rotation portion of the orientation.
+ kRotationMask = 0x03,
+ // This mask specifies whether the output is mirrored.
+ kMirroredBit = 0x04
+ };
+
+ HeadMountMetrics(
+ float inter_lens_distance, float tray_to_lens_distance,
+ float virtual_eye_to_screen_distance,
+ VerticalAlignment vertical_alignment, const FieldOfView& left_eye_max_fov,
+ const FieldOfView& right_eye_max_fov,
+ const std::shared_ptr<ColorChannelDistortion>& red_distortion,
+ const std::shared_ptr<ColorChannelDistortion>& green_distortion,
+ const std::shared_ptr<ColorChannelDistortion>& blue_distortion,
+ EyeOrientation left_eye_orientation, EyeOrientation right_eye_orientation,
+ float screen_center_to_lens_distance)
+ : inter_lens_distance_(inter_lens_distance),
+ tray_to_lens_distance_(tray_to_lens_distance),
+ virtual_eye_to_screen_distance_(virtual_eye_to_screen_distance),
+ screen_center_to_lens_distance_(screen_center_to_lens_distance),
+ vertical_alignment_(vertical_alignment),
+ eye_max_fov_({{left_eye_max_fov, right_eye_max_fov}}),
+ color_channel_distortion_(
+ {{red_distortion, green_distortion, blue_distortion}}),
+ supports_chromatic_aberration_correction_(true),
+ eye_orientation_({{left_eye_orientation, right_eye_orientation}}) {
+ // If we're missing the green or blur distortions, assume that we don't
+ // correct for chromatic aberration.
+ if (!green_distortion || !blue_distortion) {
+ color_channel_distortion_[1] = red_distortion;
+ color_channel_distortion_[2] = red_distortion;
+ supports_chromatic_aberration_correction_ = false;
+ }
+ }
+
+ // Returns the distance in meters between the optical centers of the two
+ // lenses.
+ float GetInterLensDistance() const { return inter_lens_distance_; }
+
+ // Returns the distance in meters from the "tray" upon which the display
+ // rests to the optical center of a lens.
+ float GetTrayToLensDistance() const { return tray_to_lens_distance_; }
+
+ // Returns the distance in meters from the virtual eye to the screen.
+ // See http://go/vr-distortion-correction for an explanation of what
+ // this distance is.
+ float GetVirtualEyeToScreenDistance() const {
+ return virtual_eye_to_screen_distance_;
+ }
+
+ // Returns the horizontal distance from the center of the screen to the center
+ // of the lens, in meters.
+ float GetScreenCenterToLensDistance() const {
+ return screen_center_to_lens_distance_;
+ }
+
+ // Returns the vertical alignment of the HMD. The tray-to-lens distance
+ // is relative to this position. Exception: if the alignment is kCenter,
+ // then the offset has no meaning.
+ VerticalAlignment GetVerticalAlignment() const { return vertical_alignment_; }
+
+ // Returns the given eye's maximum field of view visible through the lens.
+ // The actual rendered field of view will be limited by this and also by
+ // the size of the screen.
+ const FieldOfView& GetEyeMaxFov(EyeType eye) const {
+ return eye_max_fov_[eye];
+ }
+
+ // Returns the ColorChannelDistortion object representing the distortion
+ // caused by the lenses for the given color channel.
+ const ColorChannelDistortion& GetColorChannelDistortion(
+ RgbColorChannel channel) const {
+ return *color_channel_distortion_[channel];
+ }
+
+ bool supports_chromatic_aberration_correction() const {
+ return supports_chromatic_aberration_correction_;
+ }
+
+ EyeOrientation GetEyeOrientation(EyeType eye) const {
+ return eye_orientation_[eye];
+ }
+
+ private:
+ float inter_lens_distance_;
+ float tray_to_lens_distance_;
+ float virtual_eye_to_screen_distance_;
+ float screen_center_to_lens_distance_;
+ VerticalAlignment vertical_alignment_;
+ std::array<FieldOfView, 2> eye_max_fov_;
+ std::array<std::shared_ptr<ColorChannelDistortion>, 3>
+ color_channel_distortion_;
+ bool supports_chromatic_aberration_correction_;
+ std::array<EyeOrientation, 2> eye_orientation_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_HEAD_MOUNT_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/identity_distortion.h b/libs/vr/libeds/include/private/dvr/identity_distortion.h
new file mode 100644
index 0000000..b9c5cf6
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/identity_distortion.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_DVR_IDENTITY_DISTORTION_H_
+#define ANDROID_DVR_IDENTITY_DISTORTION_H_
+
+#include <private/dvr/color_channel_distortion.h>
+
+namespace android {
+namespace dvr {
+
+// Provides an identity distortion operation if running the device without any
+// lenses.
+class IdentityDistortion : public ColorChannelDistortion {
+ public:
+ IdentityDistortion() {}
+
+ vec2 Distort(vec2 p) const override { return p; }
+
+ vec2 DistortInverse(vec2 p) const override { return p; }
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_IDENTITY_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/lookup_radial_distortion.h b/libs/vr/libeds/include/private/dvr/lookup_radial_distortion.h
new file mode 100644
index 0000000..56fc5db
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/lookup_radial_distortion.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_LOOKUP_RADIAL_DISTORTION_H_
+#define ANDROID_DVR_LOOKUP_RADIAL_DISTORTION_H_
+
+#include <vector>
+
+#include <private/dvr/color_channel_distortion.h>
+
+namespace android {
+namespace dvr {
+
+// LookupRadialDistortion implements a radial distortion based using using a
+// vector of tan(angle) -> multipliers. This can use measured data directly.
+class LookupRadialDistortion : public ColorChannelDistortion {
+ public:
+ // lookup.x = tan(angle), lookup.y = distance from center multiplier.
+ explicit LookupRadialDistortion(const vec2* lookup, size_t count);
+
+ vec2 Distort(vec2 p) const override;
+ vec2 DistortInverse(vec2 p) const override;
+
+ private:
+ float DistortionFactor(float r) const;
+ float DistortRadius(float r) const;
+
+ std::vector<vec2> lookup_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LOOKUP_RADIAL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/lucid_metrics.h b/libs/vr/libeds/include/private/dvr/lucid_metrics.h
new file mode 100644
index 0000000..0e4ada4
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/lucid_metrics.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_DVR_LUCID_METRICS_H_
+#define ANDROID_DVR_LUCID_METRICS_H_
+
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+HeadMountMetrics CreateHeadMountMetrics();
+HeadMountMetrics CreateHeadMountMetrics(const FieldOfView& l_fov,
+ const FieldOfView& r_fov);
+HeadMountMetrics CreateUndistortedHeadMountMetrics();
+HeadMountMetrics CreateUndistortedHeadMountMetrics(const FieldOfView& l_fov,
+ const FieldOfView& r_fov);
+DisplayMetrics CreateDisplayMetrics(vec2i screen_size);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LUCID_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/lucid_pose_tracker.h b/libs/vr/libeds/include/private/dvr/lucid_pose_tracker.h
new file mode 100644
index 0000000..4ceda5a
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/lucid_pose_tracker.h
@@ -0,0 +1,49 @@
+#ifndef ANDROID_DVR_LUCID_POSE_TRACKER_H_
+#define ANDROID_DVR_LUCID_POSE_TRACKER_H_
+
+#include <memory>
+
+#include <dvr/pose_client.h>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// Provides pose tracking via the system pose service.
+class LucidPoseTracker {
+ public:
+ // When set, the pose service is ignored and the given pose is always returned
+ // by GetPose. As long as this is called before any LucidPoseTracker is
+ // used, the pose service will not be created.
+ // Threading: this is not thread safe.
+ static void SetPoseOverride(const Posef& pose);
+
+ // Reset prior override pose.
+ static void ClearPoseOverride();
+
+ LucidPoseTracker();
+ ~LucidPoseTracker();
+
+ // Currently GetPose() will ignore timestamp_ns and always return the most
+ // recent orientation.
+ // TODO(stefanus): support prediction.
+ Posef GetPose(uint64_t timestamp_ns);
+
+ private:
+ static bool is_override_pose_;
+ static Posef override_pose_;
+
+ DvrPose* pose_client_;
+
+ // The most recent pose.
+ Posef latest_pose_;
+
+ // The time stamp corresponding to when latest_pose_ was last updated.
+ uint64_t latest_timestamp_ns_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LUCID_POSE_TRACKER_H_
diff --git a/libs/vr/libeds/include/private/dvr/polynomial_radial_distortion.h b/libs/vr/libeds/include/private/dvr/polynomial_radial_distortion.h
new file mode 100644
index 0000000..8f080aa
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/polynomial_radial_distortion.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_DVR_POLYNOMIAL_RADIAL_DISTORTION_H_
+#define ANDROID_DVR_POLYNOMIAL_RADIAL_DISTORTION_H_
+
+#include <vector>
+
+#include <private/dvr/color_channel_distortion.h>
+
+namespace android {
+namespace dvr {
+
+// PolynomialRadialDistortion implements a radial distortion based using
+// a set of coefficients describing a polynomial function.
+// See http://en.wikipedia.org/wiki/Distortion_(optics).
+//
+// Unless otherwise stated, the units used in this class are tan-angle units
+// which can be computed as distance on the screen divided by distance from the
+// virtual eye to the screen.
+class PolynomialRadialDistortion : public ColorChannelDistortion {
+ public:
+ // Construct a PolynomialRadialDistortion with coefficients for
+ // the radial distortion equation:
+ //
+ // p' = p (1 + K1 r^2 + K2 r^4 + ... + Kn r^(2n))
+ //
+ // where r is the distance in tan-angle units from the optical center,
+ // p the input point and p' the output point.
+ // The provided vector contains the coefficients for the even monomials
+ // in the distortion equation: coefficients[0] is K1, coefficients[1] is K2,
+ // etc. Thus the polynomial used for distortion has degree
+ // (2 * coefficients.size()).
+ explicit PolynomialRadialDistortion(const std::vector<float>& coefficients);
+
+ // Given a radius (measuring distance from the optical axis of the lens),
+ // returns the distortion factor for that radius.
+ float DistortionFactor(float r_squared) const;
+
+ // Given a radius (measuring distance from the optical axis of the lens),
+ // returns the corresponding distorted radius.
+ float DistortRadius(float r) const;
+
+ // Given a 2d point p, returns the corresponding distorted point.
+ // distance from the virtual eye to the screen. The optical axis
+ // of the lens defines the origin for both input and output points.
+ vec2 Distort(vec2 p) const override;
+
+ // Given a 2d point p, returns the point that would need to be passed to
+ // Distort to get point p (approximately).
+ vec2 DistortInverse(vec2 p) const override;
+
+ // Returns the distortion coefficients.
+ const std::vector<float>& GetCoefficients() const;
+
+ private:
+ std::vector<float> coefficients_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_POLYNOMIAL_RADIAL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/raw_pose.h b/libs/vr/libeds/include/private/dvr/raw_pose.h
new file mode 100644
index 0000000..7058f1a
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/raw_pose.h
@@ -0,0 +1,54 @@
+#ifndef ANDROID_DVR_RAW_POSE_H_
+#define ANDROID_DVR_RAW_POSE_H_
+
+#include <atomic>
+
+namespace android {
+namespace dvr {
+
+// POD raw data of a head pose with a count field for read consistency checking.
+// Warning: The layout of this struct and RawPosePair are specific to match the
+// corresponding buffer type in the shader in late_latch.cpp.
+struct RawPose {
+ void Reset(uint32_t new_count) volatile {
+ qx = qy = qz = 0.0f;
+ qw = 1.0f;
+ px = py = pz = 0.0f;
+ count = new_count;
+ }
+
+ float qx, qy, qz, qw;
+ float px, py, pz;
+ std::atomic<uint32_t> count;
+};
+
+// RawPosePair is used for lock-free writing at about 1khz by the CPU/DSP
+// and reading by the GPU. At creation time, pose1 is given count = 1 and
+// pose2 is given count = 2.
+//
+// The lock-free write pattern is:
+// - write to pose with least count.
+// - memory write barrier.
+// - write count = count + 2.
+//
+// For reads, there is an important assumption about the GPU: it generally
+// processes things contiguously, without arbitrary preemptions that save and
+// restore full cache states. In other words, if the GPU is preempted and then
+// later resumed, any data that was read from memory before the preemption will
+// be re-read from memory after resume. This allows the following read trick to
+// work:
+// - read the full RawPosePair into a shader.
+// - select the pose with the newest count.
+//
+// The older pose may be partially written by the async stores from CPU/DSP, but
+// because of the memory barrier and GPU characteristics, the highest count pose
+// should always be a fully consistent RawPose.
+struct RawPosePair {
+ RawPose pose1;
+ RawPose pose2;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_RAW_POSE_H_
diff --git a/libs/vr/libeds/include/private/dvr/render_texture_params.h b/libs/vr/libeds/include/private/dvr/render_texture_params.h
new file mode 100644
index 0000000..71aebef
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/render_texture_params.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_DVR_RENDER_TEXTURE_PARAMS_H_
+#define ANDROID_DVR_RENDER_TEXTURE_PARAMS_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates information about the render texture, includes the size
+// of the render texture, and the left/right viewport which define the
+// portion each eye is rendering onto. This struct will be passed to
+// PresentFrame every frame before the client actually drawing the scene.
+struct RenderTextureParams {
+ RenderTextureParams() {}
+
+ RenderTextureParams(vec2i target_texture_size,
+ const Range2i& eye_viewport_bounds_left,
+ const Range2i& eye_viewport_bounds_right,
+ const FieldOfView& eye_fov_left,
+ const FieldOfView& eye_fov_right)
+ : texture_size(target_texture_size) {
+ eye_viewport_bounds[kLeftEye] = eye_viewport_bounds_left;
+ eye_viewport_bounds[kRightEye] = eye_viewport_bounds_right;
+ eye_fov[kLeftEye] = eye_fov_left;
+ eye_fov[kRightEye] = eye_fov_right;
+ }
+
+ explicit RenderTextureParams(vec2i target_texture_size,
+ const FieldOfView& eye_fov_left,
+ const FieldOfView& eye_fov_right) {
+ texture_size = target_texture_size;
+ eye_viewport_bounds[0] = Range2i::FromSize(
+ vec2i(0, 0), vec2i(texture_size[0] / 2, texture_size[1]));
+ eye_viewport_bounds[1] =
+ Range2i::FromSize(vec2i(texture_size[0] / 2, 0),
+ vec2i(texture_size[0] / 2, texture_size[1]));
+
+ eye_fov[kLeftEye] = eye_fov_left;
+ eye_fov[kRightEye] = eye_fov_right;
+ }
+
+ // The render texture size.
+ vec2i texture_size;
+
+ // The viewport bounds on the render texture for each eye.
+ Range2i eye_viewport_bounds[2];
+
+ // The field of view for each eye in degrees.
+ FieldOfView eye_fov[2];
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_RENDER_TEXTURE_PARAMS_H_
diff --git a/libs/vr/libeds/lookup_radial_distortion.cpp b/libs/vr/libeds/lookup_radial_distortion.cpp
new file mode 100644
index 0000000..2cee863
--- /dev/null
+++ b/libs/vr/libeds/lookup_radial_distortion.cpp
@@ -0,0 +1,47 @@
+#include "include/private/dvr/lookup_radial_distortion.h"
+
+namespace android {
+namespace dvr {
+
+LookupRadialDistortion::LookupRadialDistortion(const vec2* lookup, size_t count)
+ : lookup_(lookup, lookup + count) {}
+
+float LookupRadialDistortion::DistortionFactor(float r) const {
+ for (size_t i = 1; i < lookup_.size(); ++i) {
+ if (lookup_[i].x() > r) {
+ float t =
+ (r - lookup_[i - 1].x()) / (lookup_[i].x() - lookup_[i - 1].x());
+ return lookup_[i - 1].y() + t * (lookup_[i].y() - lookup_[i - 1].y());
+ }
+ }
+ return lookup_.back().y();
+}
+
+float LookupRadialDistortion::DistortRadius(float r) const {
+ return r * DistortionFactor(r);
+}
+
+vec2 LookupRadialDistortion::Distort(vec2 p) const {
+ return p * DistortionFactor(p.norm());
+}
+
+vec2 LookupRadialDistortion::DistortInverse(vec2 p) const {
+ // Secant method.
+ const float radius = p.norm();
+ float r0 = radius / 0.9f;
+ float r1 = radius * 0.9f;
+ float r2;
+ float dr0 = radius - DistortRadius(r0);
+ float dr1;
+ while (fabsf(r1 - r0) > 0.0001f /** 0.1mm */) {
+ dr1 = radius - DistortRadius(r1);
+ r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
+ r0 = r1;
+ r1 = r2;
+ dr0 = dr1;
+ }
+ return (r1 / radius) * p;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/lucid_metrics.cpp b/libs/vr/libeds/lucid_metrics.cpp
new file mode 100644
index 0000000..690c326
--- /dev/null
+++ b/libs/vr/libeds/lucid_metrics.cpp
@@ -0,0 +1,327 @@
+#include "include/private/dvr/display_metrics.h"
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/identity_distortion.h>
+#include <private/dvr/lookup_radial_distortion.h>
+#include <private/dvr/lucid_metrics.h>
+#include <private/dvr/types.h>
+
+namespace {
+
+// These numbers are specific to the OnePlus One and therefore
+// temporary until we advance to the next Lucid development platform.
+
+// Head mount metrics for Lucid A00
+static const float kDefaultInterLensDistance = 0.064f; // 64mm
+static const float kDefaultTrayToLensDistance = 0.035f;
+static const float kDefaultVirtualEyeToScreenDistance = 0.042f;
+static const android::dvr::HeadMountMetrics::VerticalAlignment
+ kDefaultVerticalAlignment = android::dvr::HeadMountMetrics::kCenter;
+static const float kDefaultFovHalfAngleInsideH = 43.7f * M_PI / 180.0f;
+static const float kDefaultFovHalfAngleOutsideH = 47.8f * M_PI / 180.0f;
+static const float kDefaultFovHalfAngleV = 54.2f * M_PI / 180.0f;
+
+// Screen size in meters for Lucid (Nexus 6 display in portrait mode).
+static const android::dvr::vec2 kScreenSizeInMeters(0.0742177f, 0.131943f);
+
+// Border size in meters for the OnePlus One.
+static const float kScreenBorderSize = 0.004f;
+
+// Refresh rate.
+static const float kScreenRefreshRate = 60.0f;
+
+// Lucid display orientation is portrait.
+static const android::dvr::DisplayOrientation kDisplayOrientation =
+ android::dvr::DisplayOrientation::kPortrait;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// The distortion lookup tables were generated via a raytraced lens simulation.
+// Please see for full calculations:
+// https://docs.google.com/a/google.com/spreadsheets/d/
+// 15cfHmCw5mHVOQ1rAJxMhta4q0e8zzcUDka1nRkfl7pY/edit?usp=sharing
+LookupRadialDistortion* GetBlueDistortionLookup() {
+ // clang-format off
+ vec2 kBlueDistortionLookup[] = {
+ {0.00000000000f, 1.00000000000f},
+ {0.01888626190f, 1.00096958278f},
+ {0.03777223810f, 1.00133301793f},
+ {0.05665761905f, 1.00193985168f},
+ {0.07554214286f, 1.00279048731f},
+ {0.09442542857f, 1.00388751781f},
+ {0.11330704762f, 1.00523363045f},
+ {0.13218657143f, 1.00683149424f},
+ {0.15106340476f, 1.00868516849f},
+ {0.16993695238f, 1.01079861126f},
+ {0.18880640476f, 1.01317712726f},
+ {0.20767092857f, 1.01582607321f},
+ {0.22652945238f, 1.01875203063f},
+ {0.24538078571f, 1.02196207850f},
+ {0.26422352381f, 1.02546421601f},
+ {0.28305602381f, 1.02926737969f},
+ {0.30187640476f, 1.03338139216f},
+ {0.32068252381f, 1.03781702504f},
+ {0.33947190476f, 1.04258620905f},
+ {0.35824171429f, 1.04770206653f},
+ {0.37698869048f, 1.05317909331f},
+ {0.39570916667f, 1.05903306635f},
+ {0.41439900000f, 1.06528124790f},
+ {0.43305350000f, 1.07194257391f},
+ {0.45166738095f, 1.07903777957f},
+ {0.47023471429f, 1.08658953759f},
+ {0.48874897619f, 1.09462239798f},
+ {0.50720285714f, 1.10316330018f},
+ {0.52558835714f, 1.11224144183f},
+ {0.54389669048f, 1.12188861421f},
+ {0.56211826190f, 1.13213939967f},
+ {0.58024261905f, 1.14303145047f},
+ {0.59825847619f, 1.15460566091f},
+ {0.61615335714f, 1.16690711338f},
+ {0.63391345238f, 1.17998560444f},
+ {0.65152300000f, 1.19389708987f},
+ {0.66896328571f, 1.20870580446f},
+ {0.68621100000f, 1.22448751087f},
+ {0.70323578571f, 1.24133415620f},
+ {0.71999716667f, 1.25935962776f},
+ {0.73643969048f, 1.27870875648f},
+ {0.75250778571f, 1.29953256670f},
+ {0.76817614286f, 1.32193822000f},
+ {0.78342009524f, 1.34604270338f},
+ {0.79828314286f, 1.37185833833f},
+ {0.81267376190f, 1.39964322604f},
+ {0.82656559524f, 1.42955958262f},
+ {0.83983054762f, 1.46196539657f},
+ {0.85234333333f, 1.49724142650f},
+ {0.86394971429f, 1.53585530271f},
+ {0.87422461905f, 1.57881139444f},
+ {0.88382583095f, 1.62091537826f},
+ {0.89571361286f, 1.67610209261f},
+ {0.90490389167f, 1.72118819668f},
+ {0.91526452143f, 1.77496904481f},
+ {0.92651365452f, 1.83722833673f},
+ {0.93437489976f, 1.88337590145f},
+ {0.94654105500f, 1.95937892848f},
+ {0.95476685095f, 2.01469745492f},
+ {0.96720383310f, 2.10451495481f},
+ {0.97546726405f, 2.16904926656f},
+ {0.98774046786f, 2.27302748020f},
+ {0.99579206762f, 2.34720582421f},
+ {1.00763328857f, 2.46603526105f},
+ {1.01533118405f, 2.55049232288f},
+ {1.02287120929f, 2.63936582235f}
+ };
+ // clang-format on
+ return new LookupRadialDistortion(
+ kBlueDistortionLookup, sizeof(kBlueDistortionLookup) / sizeof(vec2));
+}
+
+LookupRadialDistortion* GetGreenDistortionLookup() {
+ // clang-format off
+ vec2 kGreenDistortionLookup[] = {
+ {0.00000000000f, 1.00000000000f},
+ {0.01898883333f, 1.00000000000f},
+ {0.03797750000f, 1.00000000000f},
+ {0.05696585714f, 1.00000000000f},
+ {0.07595369048f, 1.00000000000f},
+ {0.09494078571f, 1.00000000000f},
+ {0.11392685714f, 1.00000000000f},
+ {0.13291157143f, 1.00000000000f},
+ {0.15189450000f, 1.00176560670f},
+ {0.17087511905f, 1.00384553961f},
+ {0.18985280952f, 1.00618614484f},
+ {0.20882680952f, 1.00879302066f},
+ {0.22779623810f, 1.01167234096f},
+ {0.24675997619f, 1.01483135203f},
+ {0.26571680952f, 1.01827767641f},
+ {0.28466519048f, 1.02202026825f},
+ {0.30360342857f, 1.02606859705f},
+ {0.32252950000f, 1.03043334057f},
+ {0.34144104762f, 1.03512630376f},
+ {0.36033538095f, 1.04016038545f},
+ {0.37920942857f, 1.04554970984f},
+ {0.39805966667f, 1.05130981266f},
+ {0.41688209524f, 1.05745768999f},
+ {0.43567214286f, 1.06401204155f},
+ {0.45442473810f, 1.07099310305f},
+ {0.47313411905f, 1.07842314596f},
+ {0.49179388095f, 1.08632639514f},
+ {0.51039692857f, 1.09472920992f},
+ {0.52893538095f, 1.10366038032f},
+ {0.54740061905f, 1.11315113705f},
+ {0.56578326190f, 1.12323535769f},
+ {0.58407300000f, 1.13395008040f},
+ {0.60225871429f, 1.14533547370f},
+ {0.62032809524f, 1.15743581542f},
+ {0.63826750000f, 1.17030000749f},
+ {0.65606135714f, 1.18398295206f},
+ {0.67369107143f, 1.19854780583f},
+ {0.69113350000f, 1.21406895255f},
+ {0.70835842857f, 1.23063670464f},
+ {0.72532545238f, 1.24836302903f},
+ {0.74197478571f, 1.26739777609f},
+ {0.75822164286f, 1.28793886907f},
+ {0.77407361905f, 1.31003521318f},
+ {0.78948523810f, 1.33383710115f},
+ {0.80448471429f, 1.35938255065f},
+ {0.81901733333f, 1.38686361242f},
+ {0.83305214286f, 1.41644808409f},
+ {0.84646438095f, 1.44848277406f},
+ {0.85912733333f, 1.48334485259f},
+ {0.87088369048f, 1.52149970074f},
+ {0.88131250000f, 1.56392750036f},
+ {0.89105132929f, 1.60552684742f},
+ {0.90312479476f, 1.66002695068f},
+ {0.91244067452f, 1.70458805205f},
+ {0.92297971714f, 1.75767475825f},
+ {0.93440940905f, 1.81916050294f},
+ {0.94237194976f, 1.86478635937f},
+ {0.95471202405f, 1.93989738862f},
+ {0.96305355738f, 1.99457325750f},
+ {0.97567372071f, 2.08333293385f},
+ {0.98407229071f, 2.14708073108f},
+ {0.99653762071f, 2.24981649552f},
+ {1.00471276167f, 2.32311751786f},
+ {1.01672394000f, 2.44057411530f},
+ {1.02452363381f, 2.52407947994f},
+ {1.03216732667f, 2.61194301580f}
+ };
+ // clang-format on
+ return new LookupRadialDistortion(
+ kGreenDistortionLookup, sizeof(kGreenDistortionLookup) / sizeof(vec2));
+}
+
+LookupRadialDistortion* GetRedDistortionLookup() {
+ // clang-format off
+ vec2 kRedDistortionLookup[] = {
+ {0.00000000000f, 1.00000000000f},
+ {0.01906776190f, 1.00000000000f},
+ {0.03813547619f, 1.00000000000f},
+ {0.05720304762f, 1.00000000000f},
+ {0.07627040476f, 1.00000000000f},
+ {0.09533740476f, 1.00000000000f},
+ {0.11440385714f, 1.00000000000f},
+ {0.13346952381f, 1.00000000000f},
+ {0.15253409524f, 1.00000000000f},
+ {0.17159714286f, 1.00000000000f},
+ {0.19065814286f, 1.00053530030f},
+ {0.20971645238f, 1.00310924426f},
+ {0.22877123810f, 1.00595236192f},
+ {0.24782154762f, 1.00907150786f},
+ {0.26686623810f, 1.01247435420f},
+ {0.28590388095f, 1.01616968529f},
+ {0.30493288095f, 1.02016688932f},
+ {0.32395133333f, 1.02447646681f},
+ {0.34295697619f, 1.02911011406f},
+ {0.36194726190f, 1.03408046560f},
+ {0.38091921429f, 1.03940151599f},
+ {0.39986942857f, 1.04508858434f},
+ {0.41879402381f, 1.05115843585f},
+ {0.43768857143f, 1.05762946333f},
+ {0.45654809524f, 1.06452169646f},
+ {0.47536695238f, 1.07185711363f},
+ {0.49413888095f, 1.07965956927f},
+ {0.51285690476f, 1.08795508025f},
+ {0.53151326190f, 1.09677206014f},
+ {0.55009952381f, 1.10614118417f},
+ {0.56860633333f, 1.11609607621f},
+ {0.58702361905f, 1.12667304464f},
+ {0.60534028571f, 1.13791190276f},
+ {0.62354421429f, 1.14985618930f},
+ {0.64162188095f, 1.16255413653f},
+ {0.65955780952f, 1.17605992962f},
+ {0.67733352381f, 1.19043584317f},
+ {0.69492602381f, 1.20575517508f},
+ {0.71230514286f, 1.22210708787f},
+ {0.72943057143f, 1.23960199799f},
+ {0.74623921429f, 1.25839340501f},
+ {0.76262400000f, 1.27871385661f},
+ {0.77861754762f, 1.30056919119f},
+ {0.79415866667f, 1.32413401001f},
+ {0.80926385714f, 1.34946540639f},
+ {0.82390640476f, 1.37670655635f},
+ {0.83805190476f, 1.40602920817f},
+ {0.85157807143f, 1.43777181543f},
+ {0.86435700000f, 1.47230885729f},
+ {0.87622914286f, 1.51010361811f},
+ {0.88677650000f, 1.55211817236f},
+ {0.89663317738f, 1.59330127207f},
+ {0.90883197952f, 1.64729627820f},
+ {0.91827594357f, 1.69138814689f},
+ {0.92892199405f, 1.74398939784f},
+ {0.94047261548f, 1.80490554711f},
+ {0.94852659262f, 1.85009630648f},
+ {0.96099790167f, 1.92451421938f},
+ {0.96945317500f, 1.97863645920f},
+ {0.98221554286f, 2.06656418112f},
+ {0.99069599476f, 2.12974390154f},
+ {1.00331392976f, 2.23149730290f},
+ {1.01157138762f, 2.30414058939f},
+ {1.02372409452f, 2.42049694265f},
+ {1.03162992905f, 2.50318810924f},
+ {1.03934762000f, 2.59027212626f}
+ };
+ // clang-format on
+ return new LookupRadialDistortion(
+ kRedDistortionLookup, sizeof(kRedDistortionLookup) / sizeof(vec2));
+}
+
+HeadMountMetrics CreateHeadMountMetrics(const FieldOfView& l_fov,
+ const FieldOfView& r_fov) {
+ std::shared_ptr<ColorChannelDistortion> default_distortion_r(
+ GetRedDistortionLookup());
+ std::shared_ptr<ColorChannelDistortion> default_distortion_g(
+ GetGreenDistortionLookup());
+ std::shared_ptr<ColorChannelDistortion> default_distortion_b(
+ GetBlueDistortionLookup());
+
+ return HeadMountMetrics(
+ kDefaultInterLensDistance, kDefaultTrayToLensDistance,
+ kDefaultVirtualEyeToScreenDistance, kDefaultVerticalAlignment, l_fov,
+ r_fov, default_distortion_r, default_distortion_g, default_distortion_b,
+ HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+ HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+ kDefaultInterLensDistance / 2.0f);
+}
+
+HeadMountMetrics CreateHeadMountMetrics() {
+ FieldOfView l_fov(kDefaultFovHalfAngleOutsideH, kDefaultFovHalfAngleInsideH,
+ kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+ FieldOfView r_fov(kDefaultFovHalfAngleInsideH, kDefaultFovHalfAngleOutsideH,
+ kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+
+ return CreateHeadMountMetrics(l_fov, r_fov);
+}
+
+DisplayMetrics CreateDisplayMetrics(vec2i screen_size) {
+ vec2 meters_per_pixel(
+ kScreenSizeInMeters[0] / static_cast<float>(screen_size[0]),
+ kScreenSizeInMeters[1] / static_cast<float>(screen_size[1]));
+ return DisplayMetrics(screen_size, meters_per_pixel, kScreenBorderSize,
+ 1000.0f / kScreenRefreshRate, kDisplayOrientation);
+}
+
+HeadMountMetrics CreateUndistortedHeadMountMetrics() {
+ FieldOfView l_fov(kDefaultFovHalfAngleOutsideH, kDefaultFovHalfAngleInsideH,
+ kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+ FieldOfView r_fov(kDefaultFovHalfAngleInsideH, kDefaultFovHalfAngleOutsideH,
+ kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+ return CreateUndistortedHeadMountMetrics(l_fov, r_fov);
+}
+
+HeadMountMetrics CreateUndistortedHeadMountMetrics(const FieldOfView& l_fov,
+ const FieldOfView& r_fov) {
+ auto distortion_all = std::make_shared<IdentityDistortion>();
+
+ return HeadMountMetrics(kDefaultInterLensDistance, kDefaultTrayToLensDistance,
+ kDefaultVirtualEyeToScreenDistance,
+ kDefaultVerticalAlignment, l_fov, r_fov,
+ distortion_all, distortion_all, distortion_all,
+ HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+ HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+ kDefaultInterLensDistance / 2.0f);
+}
+
+} // namespace dvr
+} // namespace dvr
diff --git a/libs/vr/libeds/lucid_pose_tracker.cpp b/libs/vr/libeds/lucid_pose_tracker.cpp
new file mode 100644
index 0000000..c321bb0
--- /dev/null
+++ b/libs/vr/libeds/lucid_pose_tracker.cpp
@@ -0,0 +1,90 @@
+#include "include/private/dvr/lucid_pose_tracker.h"
+
+#define LOG_TAG "LucidPoseTracker"
+#include <cutils/log.h>
+
+#include <private/dvr/clock_ns.h>
+
+namespace android {
+namespace dvr {
+
+bool LucidPoseTracker::is_override_pose_ = false;
+Posef LucidPoseTracker::override_pose_ = Posef();
+
+void LucidPoseTracker::SetPoseOverride(const Posef& pose) {
+ is_override_pose_ = true;
+ override_pose_ = pose;
+}
+
+void LucidPoseTracker::ClearPoseOverride() {
+ is_override_pose_ = false;
+ override_pose_ = Posef();
+}
+
+LucidPoseTracker::LucidPoseTracker() : pose_client_(NULL) {}
+
+LucidPoseTracker::~LucidPoseTracker() {
+ if (pose_client_) {
+ dvrPoseDestroy(pose_client_);
+ }
+}
+
+Posef LucidPoseTracker::GetPose(uint64_t timestamp_ns) {
+ if (is_override_pose_) {
+ return override_pose_;
+ }
+
+ if (!pose_client_) {
+ pose_client_ = dvrPoseCreate();
+
+ if (!pose_client_) {
+ ALOGE("No pose service, returning identity pose");
+ return Posef();
+ }
+ }
+
+ DvrPoseState state;
+ dvrPosePoll(pose_client_, &state);
+
+ const vec4 head_rotation_in_start_quat(
+ state.head_from_start_rotation.x, state.head_from_start_rotation.y,
+ state.head_from_start_rotation.z, state.head_from_start_rotation.w);
+
+ // When the pose service hasn't computed a pose yet, it returns a zero
+ // quaternion; just use the identity rotation in that case.
+ // TODO(stefanus): Find a better way to signal and check this.
+ if (head_rotation_in_start_quat.squaredNorm() < 0.5f) {
+ latest_pose_.SetRotation(quat::Identity());
+ } else {
+ latest_pose_.SetRotation(
+ quat(head_rotation_in_start_quat.w(), head_rotation_in_start_quat.x(),
+ head_rotation_in_start_quat.y(), head_rotation_in_start_quat.z())
+ .normalized());
+ }
+
+ const vec3 head_position_in_start(state.head_from_start_translation.x,
+ state.head_from_start_translation.y,
+ state.head_from_start_translation.z);
+ latest_pose_.SetPosition(head_position_in_start);
+
+ latest_timestamp_ns_ = GetSystemClockNs();
+
+ // PoseState pose_state;
+ // pose_state.timestamp_ns = latest_timestamp_ns_;
+ // pose_state.sensor_from_start_rotation =
+ // ion::math::Rotationd::FromQuaternion(ion::math::Vector4d(
+ // state.head_from_start_rotation.x, state.head_from_start_rotation.y,
+ // state.head_from_start_rotation.z,
+ // state.head_from_start_rotation.w));
+ //// TODO(stefanus): Determine the first derivative of the rotation and set it
+ //// here.
+ // pose_state.sensor_from_start_rotation_velocity =
+ // ion::math::Vector3d::Zero();
+
+ // TODO(stefanus): perform prediction.
+
+ return latest_pose_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/polynomial_radial_distortion.cpp b/libs/vr/libeds/polynomial_radial_distortion.cpp
new file mode 100644
index 0000000..fa01bb4
--- /dev/null
+++ b/libs/vr/libeds/polynomial_radial_distortion.cpp
@@ -0,0 +1,53 @@
+#include "include/private/dvr/polynomial_radial_distortion.h"
+
+namespace android {
+namespace dvr {
+
+PolynomialRadialDistortion::PolynomialRadialDistortion(
+ const std::vector<float>& coefficients)
+ : coefficients_(coefficients) {}
+
+float PolynomialRadialDistortion::DistortionFactor(float r_squared) const {
+ float r_factor = 1.0f;
+ float distortion_factor = 1.0f;
+
+ for (float ki : coefficients_) {
+ r_factor *= r_squared;
+ distortion_factor += ki * r_factor;
+ }
+
+ return distortion_factor;
+}
+
+float PolynomialRadialDistortion::DistortRadius(float r) const {
+ return r * DistortionFactor(r * r);
+}
+
+vec2 PolynomialRadialDistortion::Distort(vec2 p) const {
+ return p * DistortionFactor(p.squaredNorm());
+}
+
+vec2 PolynomialRadialDistortion::DistortInverse(vec2 p) const {
+ // Secant method.
+ const float radius = p.norm();
+ float r0 = radius / 0.9f;
+ float r1 = radius * 0.9f;
+ float r2;
+ float dr0 = radius - DistortRadius(r0);
+ float dr1;
+ while (fabsf(r1 - r0) > 0.0001f /** 0.1mm */) {
+ dr1 = radius - DistortRadius(r1);
+ r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
+ r0 = r1;
+ r1 = r2;
+ dr0 = dr1;
+ }
+ return (r1 / radius) * p;
+}
+
+const std::vector<float>& PolynomialRadialDistortion::GetCoefficients() const {
+ return coefficients_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libeds/tests/eds_app_tests.cpp b/libs/vr/libeds/tests/eds_app_tests.cpp
new file mode 100644
index 0000000..1742736
--- /dev/null
+++ b/libs/vr/libeds/tests/eds_app_tests.cpp
@@ -0,0 +1,141 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <base/logging.h>
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace {
+
+#define POSE_BINDING 0
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+static const char g_vert_shader[] =
+ "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+ "uniform LateLatchData {\n"
+ " mat4 uViewProjection;\n"
+ "};\n"
+ "void main() {\n"
+ " vec2 verts[4];\n"
+ " verts[0] = vec2(-1, -1);\n"
+ " verts[1] = vec2(-1, 1);\n"
+ " verts[2] = vec2(1, -1);\n"
+ " verts[3] = vec2(1, 1);\n"
+ " gl_Position = uViewProjection * vec4(verts[gl_VertexID], 0.0, 1.0);\n"
+ "}\n";
+
+static const char g_frag_shader[] =
+ "precision mediump float;\n"
+ "out vec4 outColor;\n"
+ "void main() {\n"
+ " outColor = vec4(1.0);\n"
+ "}\n";
+
+DvrGraphicsContext* CreateContext(int* surface_width, int* surface_height) {
+ DvrGraphicsContext* context = nullptr;
+ int display_width = 0, display_height = 0;
+ float inter_lens_meters = 0.0f;
+ float left_fov[4] = {0.0f};
+ float right_fov[4] = {0.0f};
+ int disable_warp = 0;
+ int enable_late_latch = 1;
+ DvrSurfaceParameter surface_params[] = {
+ DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+ DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, enable_late_latch),
+ 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;
+}
+
+} // namespace
+
+TEST(SensorAppTests, EdsWithLateLatch) {
+ int surface_width = 0, surface_height = 0;
+ DvrGraphicsContext* context = CreateContext(&surface_width, &surface_height);
+ ASSERT_NE(nullptr, context);
+
+ android::dvr::ShaderProgram shader(g_vert_shader, g_frag_shader);
+
+ for (int i = 0; i < 5; ++i) {
+ DvrFrameSchedule schedule;
+ dvrGraphicsWaitNextFrame(context, 0, &schedule);
+
+ const auto ident_mat = android::dvr::mat4::Identity();
+ const float* ident_mats[] = { ident_mat.data(), ident_mat.data() };
+ GLuint late_latch_buffer_id = 0;
+ int ret = dvrBeginRenderFrameLateLatch(context, 0, schedule.vsync_count, 2,
+ ident_mats, ident_mats, ident_mats,
+ &late_latch_buffer_id);
+ EXPECT_EQ(0, ret);
+ for (int eye = 0; eye < 2; ++eye) {
+ if (eye == 0)
+ glViewport(0, 0, surface_width / 2, surface_height);
+ else
+ glViewport(surface_width / 2, 0, surface_width / 2, surface_height);
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ shader.Use();
+
+ // Bind late latch pose matrix buffer.
+ glBindBufferRange(
+ GL_UNIFORM_BUFFER, POSE_BINDING, late_latch_buffer_id,
+ offsetof(DvrGraphicsLateLatchData, view_proj_matrix[eye]),
+ 16 * sizeof(float));
+
+ // TODO(jbates): use transform feedback here to grab the vertex output
+ // and verify that it received late-latch pose data. Combine this with
+ // mocked pose data to verify that late-latching is working.
+ glDrawArrays(GL_POINTS, 0, 4);
+ }
+ dvrPresent(context);
+ }
+
+ glFinish();
+ dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, EdsWithoutLateLatch) {
+ int surface_width = 0, surface_height = 0;
+ DvrGraphicsContext* context = CreateContext(&surface_width, &surface_height);
+ ASSERT_NE(nullptr, context);
+ DvrPose* client = dvrPoseCreate();
+ ASSERT_NE(nullptr, client);
+
+ for (int i = 0; i < 5; ++i) {
+ DvrFrameSchedule schedule;
+ dvrGraphicsWaitNextFrame(context, 0, &schedule);
+ DvrPoseAsync pose;
+ int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+ ASSERT_EQ(0, ret);
+
+ dvrBeginRenderFrameEds(context, pose.orientation, pose.translation);
+ for (int eye = 0; eye < 2; ++eye) {
+ if (eye == 0)
+ glViewport(0, 0, surface_width / 2, surface_height);
+ else
+ glViewport(surface_width / 2, 0, surface_width / 2, surface_height);
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ EXPECT_EQ(0, ret);
+ }
+ dvrPresent(context);
+ }
+
+ dvrPoseDestroy(client);
+ dvrGraphicsContextDestroy(context);
+}
diff --git a/libs/vr/libgvr/.clang-format b/libs/vr/libgvr/.clang-format
new file mode 100644
index 0000000..3287160
--- /dev/null
+++ b/libs/vr/libgvr/.clang-format
@@ -0,0 +1,2 @@
+Language: Cpp
+DisableFormat: true
diff --git a/libs/vr/libgvr/Android.mk b/libs/vr/libgvr/Android.mk
new file mode 100644
index 0000000..bcb3961
--- /dev/null
+++ b/libs/vr/libgvr/Android.mk
@@ -0,0 +1,126 @@
+# 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)
+
+include_dirs := \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/prebuilt/include
+
+# Java platform library for the system implementation of the GVR API.
+include $(CLEAR_VARS)
+LOCAL_MODULE := gvr_platform
+LOCAL_MODULE_STEM := com.google.vr.gvr.platform
+LOCAL_REQUIRED_MODULES := libgvr_system_loader libgvr_system
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+include $(BUILD_JAVA_LIBRARY)
+
+# Library to perform dlopen on the actual platform library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_system_loader
+LOCAL_SRC_FILES := library_loader.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library implementing the GVR API.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_system
+
+LOCAL_SRC_FILES := \
+ shim_gvr.cpp \
+ shim_gvr_controller.cpp \
+ shim_gvr_private.cpp \
+ deviceparams/CardboardDevice.nolite.proto
+
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+LOCAL_C_INCLUDES := $(include_dirs)
+LOCAL_C_INCLUDES += $(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_dirs)
+
+gvr_api_linker_script := $(LOCAL_PATH)/exported_apis.lds
+LOCAL_ADDITIONAL_DEPENDENCIES := $(gvr_api_linker_script)
+
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+LOCAL_LDFLAGS += -Wl,-version-script,$(gvr_api_linker_script)
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libbase \
+ libbinder \
+ libcutils \
+ libutils \
+ libgui \
+ libui \
+ libEGL \
+ libGLESv2 \
+ libvulkan \
+ libhardware \
+ liblog \
+ libsync \
+ libevent \
+ libprotobuf-cpp-full
+
+LOCAL_STATIC_LIBRARIES := \
+ libdisplay \
+ libbufferhub \
+ libbufferhubqueue \
+ libchrome \
+ libdvrcommon \
+ libeds \
+ libdvrgraphics \
+ libsensor \
+ libperformance \
+ libpdx_default_transport \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Prebuilt shared library for libgvr_audio.so
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_audio
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_SUFFIX := .so
+LOCAL_MULTILIB := both
+LOCAL_SRC_FILES_arm := prebuilt/lib/android_arm/libgvr_audio.so
+LOCAL_SRC_FILES_arm64 := prebuilt/lib/android_arm64/libgvr_audio.so
+include $(BUILD_PREBUILT)
+
+# Prebuilt shared library for libgvr.so
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/prebuilt/include
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_SUFFIX := .so
+LOCAL_MULTILIB := both
+LOCAL_SRC_FILES_arm := prebuilt/lib/android_arm/libgvr.so
+LOCAL_SRC_FILES_arm64 := prebuilt/lib/android_arm64/libgvr.so
+include $(BUILD_PREBUILT)
+
+# Prebuilt Java static library for common_library.aar
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
+ gvr_common_library_aar:prebuilt/lib/common_library.aar
+include $(BUILD_MULTI_PREBUILT)
+
+# Dummy libgvr_ext to be used along side libgvr.so prebuilt.
+# This shall be replaced with Google3 prebuilts in future.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_ext
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := dummy_gvr_ext.cpp
+LOCAL_STATIC_LIBRARIES := libchrome
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES += libgvr
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/vr/libgvr/CPPLINT.cfg b/libs/vr/libgvr/CPPLINT.cfg
new file mode 100644
index 0000000..2da1c50
--- /dev/null
+++ b/libs/vr/libgvr/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard,-build/include_alpha
diff --git a/libs/vr/libgvr/README.dreamos b/libs/vr/libgvr/README.dreamos
new file mode 100644
index 0000000..d847210
--- /dev/null
+++ b/libs/vr/libgvr/README.dreamos
@@ -0,0 +1 @@
+Files under public/vr/gvr were imported from the public Google VR SDK.
diff --git a/libs/vr/libgvr/com.google.vr.gvr.platform.xml b/libs/vr/libgvr/com.google.vr.gvr.platform.xml
new file mode 100644
index 0000000..9297c7f
--- /dev/null
+++ b/libs/vr/libgvr/com.google.vr.gvr.platform.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <library name="com.google.vr.gvr.platform"
+ file="/system/framework/com.google.vr.gvr.platform.jar" />
+</permissions>
diff --git a/libs/vr/libgvr/deviceparams/CardboardDevice.nolite.proto b/libs/vr/libgvr/deviceparams/CardboardDevice.nolite.proto
new file mode 100644
index 0000000..77b5d72
--- /dev/null
+++ b/libs/vr/libgvr/deviceparams/CardboardDevice.nolite.proto
@@ -0,0 +1,299 @@
+
+syntax = "proto2";
+
+option java_package = "com.google.vrtoolkit.cardboard.proto";
+option java_outer_classname = "CardboardDevice";
+option optimize_for = SPEED;
+
+package proto;
+
+
+/**
+ * Message describing properties of a VR head mount device (HMD) which uses an
+ * interchangeable smartphone as a display (e.g. Google Cardboard).
+ *
+ * While some properties are certain (e.g. inter_lens_distance), others
+ * represent nominal values which may be refined depending on context (e.g.
+ * viewport_angles).
+ *
+ * Lengths are in meters unless noted otherwise. Fields are _required_
+ * unless noted otherwise.
+ *
+ * Some context on why this set of parameters are deemed necessary and
+ * sufficient:
+ * * FOV scale can be reasonably approximated from lens-to-screen distance
+ * and display size (i.e. knowing lens focal length isn't crucial).
+ * * Lenses are assumed to be horizontally centered with respect to
+ * display.
+ * * The display is not necessarily vertically centered. For interchangeable
+ * phones where the device rests against a tray, we can derive
+ * the vertical offset from tray-to-lens height along with phone-specific
+ * bezel and screen sizes (supplied separately).
+ */
+message DeviceParams {
+ // String identifying the device's vendor (e.g. "Google, Inc.").
+ // A device's [vendor, model] pair is expected to be globally unique.
+ optional string vendor = 1;
+
+ // String identifying the device's model, including revision info if
+ // needed (e.g. "Cardboard v1"). A device's [vendor, model] pair is
+ // expected to be globally unique.
+ optional string model = 2;
+
+ // Distance from the display screen to the optical center of lenses.
+ // This is a required field for distortion rendering, and must be positive.
+ optional float screen_to_lens_distance = 3;
+
+ // Horizontal distance between optical center of the lenses.
+ // This is a required field for distortion rendering, and must be positive.
+ optional float inter_lens_distance = 4;
+
+ // Four-element tuple (left, right, bottom, top) of left eye's view extent
+ // angles relative to center, assuming the following:
+ // * eye is aligned with optical center of lens
+ // * display screen is equal or larger than extents viewable through lens
+ // * nominal eye-to-lens distance
+ // * mirrored field of view will be applied to the right eye
+ // These values are essentially used as an optimization to avoid rendering
+ // pixels which can't be seen.
+ // This is a required field for distortion rendering, and angles must be
+ // positive.
+ repeated float left_eye_field_of_view_angles = 5 [packed = true];
+
+ enum VerticalAlignmentType {
+ BOTTOM = 0; // phone rests against a fixed bottom tray
+ CENTER = 1; // phone screen assumed to be centered w.r.t. lenses
+ TOP = 2; // phone rests against a fixed top tray
+ }
+
+ // Set according to vertical alignment strategy-- see enum comments above.
+ // NOTE: If you set this to CENTER, see special instructions for the
+ // tray_to_lens_distance field below.
+ optional VerticalAlignmentType vertical_alignment = 11 [default = BOTTOM];
+
+ // If the phone is aligned vertically within the device by resting against
+ // a fixed top or bottom tray, this is the distance from the tray to
+ // optical center of the lenses.
+ // This is a required field for distortion rendering, and must be positive.
+ // NOTE: Due to a bug in initial versions of the SDK's, this field
+ // must be set explicitly to .035 when vertical_alignment = CENTER.
+ optional float tray_to_lens_distance = 6;
+
+ // Coefficients Ki for pincushion distortion function which maps
+ // from position on real screen to virtual screen (i.e. texture) relative
+ // to optical center:
+ //
+ // p' = p (1 + K1 r^2 + K2 r^4 + ... + Kn r^(2n))
+ //
+ // where r is the distance in tan-angle units from the optical center,
+ // p the input point, and p' the output point. Tan-angle units can be
+ // computed as distance on the screen divided by distance from the
+ // virtual eye to the screen.
+ repeated float distortion_coefficients = 7 [packed = true];
+ // Slots 8, 9 reserved for per-color channel distortion.
+
+ // Optionally, whether the head mount uses a magnet in any part of its
+ // design. Intended as hint as to whether phone's magnetometer is
+ // available for tasks such as orientation tracking.
+ optional bool has_magnet = 10;
+
+ enum ButtonType {
+ // No physical button, and touch screen is not easily accessible.
+ NONE = 0;
+ // HMD has integrated magnet switch similar to original Cardboard.
+ MAGNET = 1;
+ // At least a portion of touch screen is easily accessible to user for taps.
+ TOUCH = 2;
+ // Touch screen is triggered indirectly via integrated button on the HMD.
+ INDIRECT_TOUCH = 3;
+ }
+
+ // Specify primary input mechanism of the HMD. Intended for advisory
+ // purposes only, to address simple questions such as "can HMD
+ // be used with apps requiring a physical button event?" or "what icon
+ // should be used to represent button action to the user?".
+ optional ButtonType primary_button = 12 [default = MAGNET];
+
+ // Some internal data for Cardboard. This data is not intended to be
+ // set or used by developers, and any data in this proto is not guaranteed
+ // to be supported with new SDK updates.
+ optional CardboardInternalParams internal = 1729;
+
+ // Optionally, specifies the additional parameters that are necessary for
+ // a Daydream-ready headset. This field is non-null if the headset is
+ // Daydream-ready.
+ // TODO(b/30112366) The inclusion of this message inside a DeviceParams is a
+ // somewhat ugly result of some historical choices in the SDK. We should
+ // consider refactoring our code to allow us to remove this, and the
+ // CardboardInternalParams messages from this proto.
+ optional DaydreamInternalParams daydream_internal = 196883;
+}
+
+// TODO(b/27108179): CardboardInternalParams should be migrated into its own
+// file, and not live in this file.
+
+/**
+ * Message describing parameters that are used internally by Cardboard
+ * and VRToolkit. These parameters don't necessarily fit into the DeviceParams
+ * notion of a VR viewer combined with user's phone (e.g. case of viewer with
+ * dedicated display, etc.) and are not intended to be used by developers
+ * and may or may not be supported or changed without notice on new releases
+ * of the Cardboard SDK or VR Toolkit.
+ */
+message CardboardInternalParams {
+ // Used to specify a per-eye post-process transformation -- an optional
+ // rotation and x-axis reflection -- to be applied after distortion
+ // correction.
+ enum OrientationType {
+ CCW_0_DEGREES = 0;
+ CCW_90_DEGREES = 1;
+ CCW_180_DEGREES = 2;
+ CCW_270_DEGREES = 3;
+ CCW_0_DEGREES_MIRRORED = 4;
+ CCW_90_DEGREES_MIRRORED = 5;
+ CCW_180_DEGREES_MIRRORED = 6;
+ CCW_270_DEGREES_MIRRORED = 7;
+ }
+
+ // Specify a post-process transformation that is applied after the distortion
+ // function. This field is optional, if not specified, CCW_0_DEGREES is
+ // assumed. If repeated, the first orientation is for the left eye, the second
+ // is for the right eye.
+ //
+ // For example, if [CCW_90_DEGREES, CCW_270_DEGREES_MIRRORED] is specified,
+ //
+ // this input:
+ //
+ // ***************** *****************
+ // *1 2* *1 2*
+ // * * * * *** *
+ // * * * * * * *
+ // * **** * * * * *
+ // *4 3* *4 3*
+ // ***************** *****************
+ //
+ // is rendered on the screen like this:
+ //
+ // ***************** *****************
+ // *2 * 3* *3 * 2*
+ // * * * * ** *
+ // * * * * * *
+ // * *** * * *** *
+ // *1 4* *4 1*
+ // ***************** *****************
+ repeated OrientationType eye_orientations = 1 [packed = true];
+
+ // Specify a horizontal offset from the middle of the screen to the center of
+ // the lens, in meters. If one is not provided, half of the inter lens
+ // distance is used.
+ //
+ // This is only necessary if the HMD has some sort of periscope effect, where
+ // the position of the lenses, relative to the screen, is different than
+ // their position relative to the user.
+ //
+ // For example, in the HMD below, two mirrors reflect the image from the
+ // screen to the user, creating a larger inter lens distance than the screen
+ // can support.
+ //
+ // [In the diagram below, S = screen, L = lens]
+ //
+ // screen_center_to_lens_distance
+ // |--|
+ //
+ // -------------------------
+ // | SSSSSSSSSSSS |
+ // | | | | |
+ // | /----/ | \----\ |
+ // | | | | |
+ // | LLL LLL |
+ //
+ // |---------------|
+ // inter_lens_distance
+ //
+ optional float screen_center_to_lens_distance = 2;
+
+ // Optional x-dimension physical pixels per inch of the external display,
+ // assuming landscape orientation. If set, this will override OS-reported
+ // values.
+ optional float x_ppi_override = 3;
+
+ // Optional y-dimension physical pixels per inch of the external display,
+ // assuming landscape orientation. If set, this will override OS-reported
+ // values.
+ optional float y_ppi_override = 4;
+
+ // Optional string identifying the device's accelerometer and gyroscope.
+ // If either field is filled out, the corresponding sensor (gyroscope or
+ // accelerometer) will be used for head tracking.
+ //
+ // Valid strings are usually found in:
+ // vendor/<vendorname>/<devicename>/xxx/sensors.cpp
+ //
+ // For dynamic sensors, this string will be provided in a separate way.
+ //
+ // NB: Vendors and manufacturers should make the name of the sensor as
+ // specific as possible, since if multiple sensors with the same name are
+ // connected, the first will be used.
+ optional string accelerometer = 5;
+ optional string gyroscope = 6;
+}
+
+/**
+ * Message describing the additional properties of a Daydream-ready headset that
+ * are not used for a normal cardboard viewer. These parameters are not intended
+ * to be used, or consumed, by developers and may or may not be supported or
+ * changed without notice on new releases of the Cardboard SDK or VR Toolkit.
+ */
+message DaydreamInternalParams {
+ // The version of the Daydream-ready specification to which this device
+ // conforms.
+ optional int32 version = 1;
+
+ // Optionally, specifies the collection of screen alignment markers in the
+ // headset.
+ repeated ScreenAlignmentMarker alignment_markers = 2;
+}
+
+/**
+ * Message describing a single screen alignment marker.
+ *
+ * A screen alignment marker is a capacitive touch point affixed to the headset
+ * which is capable of making contact with the screen. The location of the touch
+ * point is given in meters, measured along a horizontal axis which passes
+ * through the center of both lenses, and a vertical axis which is equidistant
+ * from the centers of the lenses. A positive vertical value indicates that the
+ * point lies above the horizontal axis, and a positive horizontal value
+ * indicates that the point lies to the right, as seen by a user of the headset,
+ * of the vertical axis. For example, if the following is a representation of a
+ * headset, viewed from the point of view of a user, with three points marked by
+ * the numbers 1, 2, and 3.
+ *
+ * *****************************************************************************
+ * * ^ *
+ * * _____ | _____ *
+ * * / \ 1 / \ *
+ * * / \ | / \ *
+ * * / \ | / \ *
+ * * / \ | / \ *
+ * * / \ | / \ *
+ * *---------|-------*-------|----------+------2---|-------*-------|---------->*
+ * * \ / | \ / *
+ * * \ / | \ / *
+ * * \ / 3 | \ / *
+ * * \ / | \ / *
+ * * \_____/ | \_____/ *
+ * * | *
+ * * | *
+ * *****************************************************************************
+ *
+ * Then the coordinates of point 1 could be horizontal = 0.0, vertical = 0.035;
+ * point 2 could be horizontal = 0.02; and point 3 could be horizontal = -0.01
+ * vertical = -0.012
+ */
+message ScreenAlignmentMarker {
+ // The horizontal coordinate of the touch point.
+ optional float horizontal = 1;
+
+ // The vertical coordinate of the touch point.
+ optional float vertical = 2;
+}
diff --git a/libs/vr/libgvr/dummy_gvr_ext.cpp b/libs/vr/libgvr/dummy_gvr_ext.cpp
new file mode 100644
index 0000000..c507038
--- /dev/null
+++ b/libs/vr/libgvr/dummy_gvr_ext.cpp
@@ -0,0 +1,29 @@
+#include <base/logging.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_ext.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+
+gvr_frame_schedule* gvr_frame_schedule_create() { return NULL; }
+
+void gvr_frame_schedule_destroy(gvr_frame_schedule** /* schedule */) {}
+
+uint32_t gvr_frame_schedule_get_vsync_count(
+ gvr_frame_schedule* /* schedule */) {
+ return 0;
+}
+
+gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
+ uint32_t /* vsync_count */) {
+ LOG(FATAL) << "gvr_get_6dof_head_pose_in_start_space is not implemented. "
+ << "Use gvr_get_head_space_from_start_space_pose instead.";
+ return gvr_mat4f({{{1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 1.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}}});
+}
+
+void gvr_wait_next_frame(gvr_swap_chain* /* swap_chain */,
+ int64_t /* sched_offset_nanos */,
+ gvr_frame_schedule* /* out_next_frame_schedule */) {
+ LOG(FATAL) << "gvr_wait_next_frame is not implemented.";
+}
diff --git a/libs/vr/libgvr/exported_apis.lds b/libs/vr/libgvr/exported_apis.lds
new file mode 100644
index 0000000..2d19303
--- /dev/null
+++ b/libs/vr/libgvr/exported_apis.lds
@@ -0,0 +1,16 @@
+{
+ global:
+ # Export GVR APIs, both public and private.
+ gvr_*;
+
+ # Whitelist of DVR APIs required by VrCore for Ambrosia controller support.
+ dvrPoseCreate;
+ dvrPoseDestroy;
+ dvrPoseGet;
+ dvrPoseGetController;
+ dvrPoseGetVsyncCount;
+
+ local:
+ # Hide everything else.
+ *;
+};
diff --git a/libs/vr/libgvr/include/private/dvr/internal_types.h b/libs/vr/libgvr/include/private/dvr/internal_types.h
new file mode 100644
index 0000000..dafdb73
--- /dev/null
+++ b/libs/vr/libgvr/include/private/dvr/internal_types.h
@@ -0,0 +1,170 @@
+#ifndef ANDROID_DVR_INTERNAL_TYPES_H_
+#define ANDROID_DVR_INTERNAL_TYPES_H_
+
+#include <GLES2/gl2.h>
+#include <atomic>
+#include <memory>
+#include <vector>
+#include <unordered_map>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_core.h>
+#include <private/dvr/display_client.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+#include <vr/gvr/capi/src/gvr_types_experimental.h>
+
+typedef struct gvr_user_prefs_ {
+} gvr_user_prefs;
+
+typedef struct gvr_context_ {
+ int32_t last_error_;
+ JNIEnv* jni_env_;
+ DvrPose* pose_client_;
+ std::unique_ptr<android::dvr::DisplayClient> display_client_;
+ android::dvr::SystemDisplayMetrics display_metrics_;
+ gvr_mat4f left_eye_viewport_transform_;
+ gvr_mat4f right_eye_viewport_transform_;
+ gvr_mat4f next_frame_6dof_pose_;
+ gvr_mat4f next_frame_controller_pose_[2];
+ gvr_user_prefs user_prefs_;
+ bool force_6dof_;
+ std::vector<gvr_swap_chain*> swap_chains_;
+
+ gvr_context_() :
+ last_error_(GVR_ERROR_NONE),
+ jni_env_(nullptr),
+ pose_client_(nullptr),
+ force_6dof_(false) {}
+
+ ~gvr_context_();
+} gvr_context;
+
+typedef struct gvr_buffer_spec_ {
+ gvr_sizei size;
+ int32_t msaa_samples;
+ int32_t color_format;
+ int32_t depth_stencil_format;
+ bool blur_behind;
+ bool initially_visible;
+ int z_order;
+
+ // The default values are configured to match SVR defaults
+ gvr_buffer_spec_()
+ : size{0, 0},
+ msaa_samples(0),
+ color_format(GVR_COLOR_FORMAT_RGBA_8888),
+ depth_stencil_format(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16),
+ blur_behind(true),
+ initially_visible(true),
+ z_order(0) {}
+} gvr_buffer_spec;
+
+// This isn't a public gvr type
+struct gvr_buffer {
+ gvr_buffer_spec spec;
+ GLuint frame_buffer;
+ GLuint color_render_buffer;
+ GLuint depth_stencil_render_buffer;
+
+ // requested_size is used for resizing. It will be {-1, -1} when no resize is
+ // requested. Any other value indicates the app changed the size.
+ gvr_sizei requested_size;
+
+ gvr_buffer();
+ // If creation fails frame_buffer will be 0
+ gvr_buffer(gvr_context* gvr, const gvr_buffer_spec& spec,
+ GLuint texture_id, GLenum texture_target);
+ ~gvr_buffer();
+
+ gvr_buffer(gvr_buffer&& other);
+ gvr_buffer& operator=(gvr_buffer&& other);
+ gvr_buffer(const gvr_buffer& other) = delete;
+ gvr_buffer& operator=(const gvr_buffer& other) = delete;
+
+ // Set default values. Doesn't free GL resources first.
+ void SetDefaults();
+
+ // Frees all GL resources associated with the buffer
+ void FreeGl();
+};
+
+typedef struct gvr_swap_chain_ {
+ gvr_context* context;
+ DvrGraphicsContext* graphics_context_;
+ std::vector<gvr_buffer> buffers_;
+ bool frame_acquired_;
+ bool wait_next_frame_called_by_app_;
+ std::atomic<int32_t> next_external_surface_id_;
+ std::unordered_map<int32_t, gvr_external_surface*> external_surfaces_;
+
+ explicit gvr_swap_chain_(gvr_context* context)
+ : context(context),
+ graphics_context_(nullptr),
+ frame_acquired_(false),
+ wait_next_frame_called_by_app_(false),
+ next_external_surface_id_(0) {}
+ ~gvr_swap_chain_();
+} gvr_swap_chain;
+
+typedef struct gvr_buffer_viewport_ {
+ int32_t buffer_index;
+ gvr_rectf uv;
+ gvr_mat4f transform;
+ int32_t eye;
+ int32_t external_surface_id;
+ gvr_reprojection reprojection;
+
+ gvr_buffer_viewport_()
+ : buffer_index(0),
+ uv{0, 0, 0, 0},
+ transform{{{1.f, 0.f, 0.f, 0.f},
+ {0.f, 1.f, 0.f, 0.f},
+ {0.f, 0.f, 1.f, 0.f},
+ {0.f, 0.f, 0.f, 1.f}}},
+ eye(0),
+ external_surface_id(-1),
+ reprojection(GVR_REPROJECTION_FULL) {}
+
+ gvr_buffer_viewport_(int32_t /* buffer_index */, gvr_rectf uv,
+ const gvr_mat4f& transform, int32_t eye,
+ int32_t external_surface_id,
+ gvr_reprojection reprojection)
+ : buffer_index(0),
+ uv(uv),
+ transform(transform),
+ eye(eye),
+ external_surface_id(external_surface_id),
+ reprojection(reprojection) {}
+
+ bool operator==(const gvr_buffer_viewport_& other) const;
+
+ bool operator!=(const gvr_buffer_viewport_& other) const {
+ return !operator==(other);
+ }
+} gvr_buffer_viewport;
+
+typedef struct gvr_buffer_viewport_list_ {
+ std::vector<gvr_buffer_viewport> viewports;
+} gvr_buffer_viewport_list;
+
+typedef struct gvr_frame_schedule_ {
+ uint32_t vsync_count;
+ gvr_clock_time_point scheduled_finish;
+
+ gvr_frame_schedule_() : vsync_count(0) {
+ scheduled_finish.monotonic_system_time_nanos = 0;
+ }
+} gvr_frame_schedule;
+
+typedef struct gvr_display_synchronizer_ {} gvr_display_synchronizer;
+
+typedef struct gvr_external_surface_ {
+ int32_t id;
+ gvr_swap_chain* swap_chain;
+ DvrVideoMeshSurface* video_surface;
+} gvr_external_surface;
+
+#endif // ANDROID_DVR_INTERNAL_TYPES_H_
diff --git a/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_ext.h b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_ext.h
new file mode 100644
index 0000000..8af434f
--- /dev/null
+++ b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_ext.h
@@ -0,0 +1,119 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_EXT_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_EXT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Constants that represent GVR error codes.
+typedef enum {
+ // TODO(steventhomas): All errors should be switched to something more
+ // meaningful and this should eventually go away.
+ GVR_ERROR_INTERNAL = 9000,
+} gvr_error_ext;
+
+typedef struct gvr_frame_schedule_ gvr_frame_schedule;
+
+gvr_frame_schedule* gvr_frame_schedule_create();
+
+void gvr_frame_schedule_destroy(gvr_frame_schedule** schedule);
+
+uint32_t gvr_frame_schedule_get_vsync_count(gvr_frame_schedule* schedule);
+
+gvr_clock_time_point gvr_frame_schedule_get_scheduled_finish(
+ gvr_frame_schedule* schedule);
+
+/// Sleep until it's time to render the next frame.
+// |start_delay_ns| adjusts how long this function blocks the app from starting
+// its next frame. If |start_delay_ns| is 0, the function waits until the
+// scheduled frame finish time for the current frame, which gives the app one
+// full vsync period to render the next frame. If the app needs less than a full
+// vysnc period to render the frame, pass in a non-zero |start_delay_ns| to
+// delay the start of frame rendering further. For example, if the vsync period
+// is 11.1ms and the app takes 6ms to render a frame, consider setting this to
+// 5ms (note that the value is in nanoseconds, so 5,000,000ns) so that the app
+// finishes the frame closer to the scheduled frame finish time. Delaying the
+// start of rendering allows the app to use a more up-to-date pose for
+// rendering.
+// |start_delay_ns| must be a positive value or 0. If you're unsure what to set
+// for |start_delay_ns|, use 0.
+/// |out_next_frame_schedule| is an output parameter that will contain the
+/// schedule for the next frame. It can be null.
+void gvr_wait_next_frame(gvr_swap_chain* swap_chain, int64_t start_delay_nanos,
+ gvr_frame_schedule* out_next_frame_schedule);
+
+gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
+ uint32_t vsync_count);
+
+gvr_mat4f gvr_get_head_space_from_start_space_pose(
+ gvr_context* gvr, const gvr_clock_time_point time);
+
+gvr_mat4f gvr_get_start_space_from_controller_space_pose(
+ gvr_context* gvr, int controller_id, const gvr_clock_time_point time);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+#include <utility>
+
+namespace gvr {
+
+/// Convenience C++ wrapper for gvr_frame_schedule. Frees the underlying
+/// gvr_frame_schedule on destruction.
+class FrameSchedule {
+ public:
+ FrameSchedule() { schedule_ = gvr_frame_schedule_create(); }
+
+ ~FrameSchedule() {
+ if (schedule_)
+ gvr_frame_schedule_destroy(&schedule_);
+ }
+
+ FrameSchedule(FrameSchedule&& other) {
+ std::swap(schedule_, other.schedule_);
+ }
+
+ FrameSchedule& operator=(FrameSchedule&& other) {
+ std::swap(schedule_, other.schedule_);
+ return *this;
+ }
+
+ gvr_frame_schedule* cobj() { return schedule_; }
+ const gvr_frame_schedule* cobj() const { return schedule_; }
+
+ uint32_t GetVsyncCount() const {
+ return gvr_frame_schedule_get_vsync_count(schedule_);
+ }
+
+ gvr_clock_time_point GetScheduledFinish() const {
+ return gvr_frame_schedule_get_scheduled_finish(schedule_);
+ }
+
+ private:
+ gvr_frame_schedule* schedule_;
+};
+
+} // namespace gvr
+#endif // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif // VR_GVR_CAPI_INCLUDE_GVR_EXT_H_
diff --git a/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_util.h b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_util.h
new file mode 100644
index 0000000..1fae611
--- /dev/null
+++ b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_util.h
@@ -0,0 +1,123 @@
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_UTIL_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_UTIL_H_
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/types.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+
+namespace android {
+namespace dvr {
+
+inline gvr_rectf FovRadiansToDegrees(const gvr_rectf& fov) {
+ return gvr_rectf{ToDeg(fov.left), ToDeg(fov.right), ToDeg(fov.bottom),
+ ToDeg(fov.top)};
+}
+
+inline gvr_rectf FovDegreesToRadians(const gvr_rectf& fov) {
+ return gvr_rectf{ToRad(fov.left), ToRad(fov.right), ToRad(fov.bottom),
+ ToRad(fov.top)};
+}
+
+inline FieldOfView GvrToDvrFov(const gvr_rectf& fov) {
+ gvr_rectf fov_rad = FovDegreesToRadians(fov);
+ return FieldOfView(fov_rad.left, fov_rad.right, fov_rad.bottom, fov_rad.top);
+}
+
+inline gvr_rectf DvrToGvrFov(const FieldOfView& fov) {
+ return FovRadiansToDegrees(
+ gvr_rectf{fov.GetLeft(), fov.GetRight(), fov.GetBottom(), fov.GetTop()});
+}
+
+inline gvr_mat4f GvrIdentityMatrix() {
+ gvr_mat4f identity;
+ memset(&identity.m, 0, sizeof(identity.m));
+ for (int i = 0; i < 4; i++)
+ identity.m[i][i] = 1;
+ return identity;
+}
+
+inline gvr_mat4f GvrTranslationMatrix(float x, float y, float z) {
+ gvr_mat4f trans = GvrIdentityMatrix();
+ trans.m[0][3] = x;
+ trans.m[1][3] = y;
+ trans.m[2][3] = z;
+ return trans;
+}
+
+inline gvr_mat4f EigenToGvrMatrix(const mat4& m) {
+ gvr_mat4f ret;
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ ret.m[i][j] = m(i, j);
+ return ret;
+}
+
+inline mat4 GvrToEigenMatrix(const gvr::Mat4f& m) {
+ mat4 ret;
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ ret(i, j) = m.m[i][j];
+ return ret;
+}
+
+inline quat GvrToEigenRotation(const gvr_mat4f& m) {
+ mat3 ret;
+ for (int r = 0; r < 3; ++r)
+ for (int c = 0; c < 3; ++c)
+ ret(r, c) = m.m[r][c];
+ return quat(ret.matrix());
+}
+
+inline vec3 GvrToEigenTranslation(const gvr_mat4f& m) {
+ return vec3(m.m[0][3], m.m[1][3], m.m[2][3]);
+}
+
+// Converts a DVR pose to 6DOF head transform as a GVR matrix.
+inline gvr_mat4f PosefToGvrMatrix(const Posef& pose) {
+ return EigenToGvrMatrix(pose.GetObjectFromReferenceMatrix());
+}
+
+// Converts a DVR pose to 3DOF head transform as a GVR matrix by stripping out
+// position translation.
+inline gvr_mat4f PosefTo3DofGvrMatrix(const Posef& pose) {
+ gvr_mat4f ret = PosefToGvrMatrix(pose);
+ ret.m[0][3] = 0;
+ ret.m[1][3] = 0;
+ ret.m[2][3] = 0;
+ return ret;
+}
+
+// Converts a GVR matrix to a DVR pose.
+inline Posef GvrMatrixToPosef(const gvr_mat4f& m) {
+ return Posef(GvrToEigenRotation(m), GvrToEigenTranslation(m)).Inverse();
+}
+
+// Calculates an transform with only the yaw and position components of |pose|.
+// The inverse of this matrix cancels yaw and position without affecting roll or
+// pitch.
+inline mat4 CalculateRecenterTransform(const mat4& pose) {
+ const vec4 z_axis = pose * vec4::UnitZ();
+ const float yaw = std::atan2(z_axis[0], z_axis[2]);
+ const vec3 position = pose.translation();
+ return mat4(Eigen::AngleAxis<float>(yaw, vec3::UnitY())).translate(position);
+}
+
+// Calculates a transform that negates the position component of |pose| and
+// offsets the pose by |position|. The inverse of this matrix cancels the
+// position component of pose and translates by |position| without affecting
+// orientation.
+inline mat4 CalculateOffsetTransform(const mat4& pose, const vec3& position) {
+ // Transform the origin by the pose matrix to produce the offset that cancels
+ // only the position of the pose.
+ // -1 T
+ // [ R | t ] [ 0 ] = -R * t
+ // [ 0 1 ] [ 1 ]
+ const vec3 position_offset = (pose.inverse() * vec4(0, 0, 0, 1)).head<3>();
+ return mat4(mat4::Identity()).translate(position - position_offset);
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_GVR_CAPI_INCLUDE_GVR_UTIL_H_
diff --git a/libs/vr/libgvr/java/com/google/vr/gvr/platform/Loader.java b/libs/vr/libgvr/java/com/google/vr/gvr/platform/Loader.java
new file mode 100644
index 0000000..5c5cc62
--- /dev/null
+++ b/libs/vr/libgvr/java/com/google/vr/gvr/platform/Loader.java
@@ -0,0 +1,32 @@
+package com.google.vr.gvr.platform;
+
+/**
+ * Auxiliary class to load the system implementation of the GVR API.
+ */
+public class Loader {
+
+ /**
+ * Opens a shared library containing the system implementation for the GVR
+ * API and returns the handle to it.
+ *
+ * @return A Long object describing the handle returned by dlopen.
+ */
+ public static Long loadLibrary() {
+ // Note: we cannot safely do caller verifications here, so instead we do
+ // them in the service side. This means that private API symbols will be
+ // visible to any app adding the appropriate <uses-library> in their
+ // manifest, but any requests to such APIs will fail if not done from a
+ // trusted package like VrCore.
+ //
+ // Trusted packages are defined by (package name, signature) pairs in within
+ // a system service, and both must match.
+
+ // Load a thin JNI library that runs dlopen on request.
+ System.loadLibrary("gvr_system_loader");
+
+ // Performs dlopen on the library and returns the handle.
+ return nativeLoadLibrary("libgvr_system.so");
+ }
+
+ private static native long nativeLoadLibrary(String library);
+}
diff --git a/libs/vr/libgvr/library_loader.cpp b/libs/vr/libgvr/library_loader.cpp
new file mode 100644
index 0000000..3cdc7d6
--- /dev/null
+++ b/libs/vr/libgvr/library_loader.cpp
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <jni.h>
+
+#include <string>
+
+extern "C" {
+
+JNIEXPORT jlong JNICALL
+Java_com_google_vr_gvr_platform_Loader_nativeLoadLibrary(
+ JNIEnv* env, jclass, jstring java_library) {
+ if (!java_library)
+ return 0;
+
+ // Convert the Java String object to a C++ null-terminated string.
+ const char* data = env->GetStringUTFChars(java_library, NULL);
+ size_t size = env->GetStringUTFLength(java_library);
+ std::string library(data, size);
+ env->ReleaseStringUTFChars(java_library, data);
+
+ // Return the handle to the requested library.
+ return reinterpret_cast<jlong>(
+ dlopen(library.c_str(), RTLD_NOW | RTLD_LOCAL));
+}
+
+} // extern "C"
diff --git a/libs/vr/libgvr/prebuilt/build_gvr_prebuilts.sh b/libs/vr/libgvr/prebuilt/build_gvr_prebuilts.sh
new file mode 100644
index 0000000..5d5076b
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/build_gvr_prebuilts.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+# Build and copy libgvr from Google3. Make sure that your local Google3 client
+# is up-to-date by running `p4 sync` before executing this script.
+#
+# Usage:
+# build_gvr_prebuilts.sh --google3_dir=<path_google3_client_root>
+
+source gbash.sh || exit
+
+DEFINE_string --required "google3_dir" "" \
+ "Path to the root directory of Google3 client"
+
+BLAZE_COMMON_OPTS=(
+ --compilation_mode=opt
+ --copt=-fdata-sections
+ --copt=-ffunction-sections
+ --define='prod=1'
+ --define='enable_experimental_sdk=1'
+ --linkopt=-Wl,--gc-sections
+)
+
+function copy_file() {
+ cp -v "${1}" ${CURRENT_DIR}/"${2}"
+}
+
+function copy_gvr_headers() {
+ echo "Copy GVR headers ..."
+
+ GVR_HEADER_DIR="include/vr/gvr/capi/include"
+ GVR_SOURCE_DIR="include/vr/gvr/capi/src"
+
+ # GVR public headers
+ copy_file "vr/gvr/capi/include/gvr.h" ${GVR_HEADER_DIR}
+ copy_file "vr/gvr/capi/include/gvr_audio.h" ${GVR_HEADER_DIR}
+ copy_file "vr/gvr/capi/include/gvr_controller.h" ${GVR_HEADER_DIR}
+ copy_file "vr/gvr/capi/include/gvr_types.h" ${GVR_HEADER_DIR}
+
+ # GVR private and experimental headers
+ copy_file "vr/gvr/capi/src/gvr_experimental.h" ${GVR_SOURCE_DIR}
+ copy_file "vr/gvr/capi/src/gvr_private.h" ${GVR_SOURCE_DIR}
+ copy_file "vr/gvr/capi/src/gvr_types_experimental.h" ${GVR_SOURCE_DIR}
+}
+
+function build_gvr_libs() {
+ echo "Build GVR libraries ..."
+
+ blaze build \
+ //java/com/google/vr/sdk/release:common_library.aar \
+ //vr/gvr/platform:libgvr.so \
+ //vr/gvr/platform:libgvr_audio.so \
+ ${BLAZE_COMMON_OPTS[@]} --config=android_arm --symlink_prefix blaze-arm-
+
+ blaze build \
+ //vr/gvr/platform:libgvr.so \
+ //vr/gvr/platform:libgvr_audio.so \
+ ${BLAZE_COMMON_OPTS[@]} --config=android_arm64 --symlink_prefix blaze-arm64-
+
+ copy_file "blaze-arm-genfiles/java/com/google/vr/sdk/release/common_library.aar" \
+ "lib/common_library.aar"
+ copy_file "blaze-arm-genfiles/vr/gvr/platform/libgvr.so" "lib/android_arm"
+ copy_file "blaze-arm-genfiles/vr/gvr/platform/libgvr_audio.so" "lib/android_arm"
+ copy_file "blaze-arm64-genfiles/vr/gvr/platform/libgvr.so" "lib/android_arm64"
+ copy_file "blaze-arm64-genfiles/vr/gvr/platform/libgvr_audio.so" "lib/android_arm64"
+}
+
+function main() {
+ set -ex
+
+ CURRENT_DIR=$(pwd)
+ GOOGLE3_DIR=${FLAGS_google3_dir}
+
+ cd ${GOOGLE3_DIR}
+ copy_gvr_headers
+ build_gvr_libs
+}
+
+gbash::init_google "$@"
+main "$@"
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
new file mode 100644
index 0000000..5054ebd
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
@@ -0,0 +1,1737 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_H_
+
+#ifdef __ANDROID__
+#include <jni.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+#include <array>
+#include <memory>
+#include <vector>
+#endif
+
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @defgroup base Google VR Base C API
+/// @brief This is the Google VR C API. It supports clients writing VR
+/// experiences for head mounted displays that consist of a mobile phone and a
+/// VR viewer.
+///
+/// Example API usage:
+///
+/// #ifdef __ANDROID__
+/// // On Android, the gvr_context should almost always be obtained from
+/// // the Java GvrLayout object via
+/// // GvrLayout.getGvrApi().getNativeGvrContext().
+/// gvr_context* gvr = ...;
+/// #else
+/// gvr_context* gvr = gvr_create();
+/// #endif
+///
+/// gvr_initialize_gl(gvr);
+///
+/// gvr_buffer_viewport_list* viewport_list =
+/// gvr_buffer_viewport_list_create(gvr);
+/// gvr_get_recommended_buffer_viewports(gvr, viewport_list);
+/// gvr_buffer_viewport* left_eye_vp = gvr_buffer_viewport_create(gvr);
+/// gvr_buffer_viewport* right_eye_vp = gvr_buffer_viewport_create(gvr);
+/// gvr_buffer_viewport_list_get_item(viewport_list, 0, left_eye_vp);
+/// gvr_buffer_viewport_list_get_item(viewport_list, 1, right_eye_vp);
+///
+/// while (client_app_should_render) {
+/// // A client app should be ready for the render target size to change
+/// // whenever a new QR code is scanned, or a new viewer is paired.
+/// gvr_sizei render_target_size =
+/// gvr_get_maximum_effective_render_target_size(gvr);
+/// // The maximum effective render target size can be very large, most
+/// // applications need to scale down to compensate.
+/// render_target_size.width /= 2;
+/// render_target_size.height /= 2;
+/// gvr_swap_chain_resize_buffer(swap_chain, 0, render_target_size);
+///
+/// // This function will depend on your render loop's implementation.
+/// gvr_clock_time_point next_vsync = AppGetNextVsyncTime();
+///
+/// const gvr_mat4f head_view =
+/// gvr_get_head_space_from_start_space_rotation(gvr, next_vsync);
+/// const gvr_mat4f left_eye_view = MatrixMultiply(
+/// gvr_get_eye_from_head_matrix(gvr, GVR_LEFT_EYE), head_view);
+/// const gvr::Mat4f right_eye_view = MatrixMultiply(
+/// gvr_get_eye_from_head_matrix(gvr, GVR_RIGHT_EYE), head_view);
+///
+/// // Insert client rendering code here.
+///
+/// AppSetRenderTarget(offscreen_texture_id);
+///
+/// AppDoSomeRenderingForEye(
+/// gvr_buffer_viewport_get_source_uv(left_eye_view),
+/// left_eye_matrix);
+/// AppDoSomeRenderingForEye(
+/// gvr_buffer_viewport_get_source_uv(right_eye_view),
+/// right_eye_matrix);
+/// AppSetRenderTarget(primary_display);
+///
+/// gvr_frame_submit(&frame, viewport_list, head_matrix);
+/// }
+///
+/// // Cleanup memory.
+/// gvr_buffer_viewport_list_destroy(&viewport_list);
+/// gvr_buffer_viewport_destroy(&left_eye_vp);
+/// gvr_buffer_viewport_destroy(&right_eye_vp);
+///
+/// #ifdef __ANDROID__
+/// // On Android, The Java GvrLayout owns the gvr_context.
+/// #else
+/// gvr_destroy(gvr);
+/// #endif
+///
+/// Head tracking is enabled by default, and will begin as soon as the
+/// gvr_context is created. The client should call gvr_pause_tracking() and
+/// gvr_resume_tracking() when the app is paused and resumed, respectively.
+///
+/// Note: Unless otherwise noted, the functions in this API may not be
+/// thread-safe with respect to the gvr_context, and it is up the caller to use
+/// the API in a thread-safe manner.
+///
+/// @{
+
+/// Creates a new gvr instance.
+///
+/// The instance must remain valid as long as any GVR object is in use. When
+/// the application no longer needs to use the GVR SDK, call gvr_destroy().
+///
+///
+/// On Android, the gvr_context should *almost always* be obtained from the Java
+/// GvrLayout object, rather than explicitly created here. The GvrLayout should
+/// live in the app's View hierarchy, and its use is required to ensure
+/// consistent behavior across all varieties of GVR-compatible viewers. See
+/// the Java GvrLayout and GvrApi documentation for more details.
+///
+#ifdef __ANDROID__
+/// @param env The JNIEnv associated with the current thread.
+/// @param app_context The Android application context. This must be the
+/// application context, NOT an Activity context (Note: from any Android
+/// Activity in your app, you can call getApplicationContext() to
+/// retrieve the application context).
+/// @param class_loader The class loader to use when loading Java classes.
+/// This must be your app's main class loader (usually accessible through
+/// activity.getClassLoader() on any of your Activities).
+///
+/// @return Pointer to the created gvr instance, NULL on failure.
+gvr_context* gvr_create(JNIEnv* env, jobject app_context, jobject class_loader);
+#else
+/// @return Pointer to the created gvr instance, NULL on failure.
+gvr_context* gvr_create();
+#endif // #ifdef __ANDROID__
+
+/// Gets the current GVR runtime version.
+///
+/// Note: This runtime version may differ from the version against which the
+/// client app is compiled, as defined by the semantic version components in
+/// gvr_version.h.
+///
+/// @return The version as a gvr_version.
+gvr_version gvr_get_version();
+
+/// Gets a string representation of the current GVR runtime version. This is of
+/// the form "MAJOR.MINOR.PATCH".
+///
+/// Note: This runtime version may differ from the version against which the
+/// client app is compiled, as defined in gvr_version.h by
+/// GVR_SDK_VERSION_STRING.
+///
+/// @return The version as a static char pointer.
+const char* gvr_get_version_string();
+
+/// Gets the current GVR error code, or GVR_ERROR_NONE if there is no error.
+/// This function doesn't clear the error code; see gvr_clear_error().
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return The current gvr_error code, or GVR_ERROR_NONE if no error has
+/// occurred.
+int32_t gvr_get_error(gvr_context* gvr);
+
+/// Clears the current GVR error code, and returns the error code that was
+/// cleared.
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return The gvr_error code that was cleared by this function, or
+/// GVR_ERROR_NONE if no error has occurred.
+int32_t gvr_clear_error(gvr_context* gvr);
+
+/// Gets a human-readable string representing the given error code.
+///
+/// @param error_code The gvr_error code.
+/// @return A human-readable string representing the error code.
+const char* gvr_get_error_string(int32_t error_code);
+
+/// Returns an opaque struct containing information about user preferences.
+///
+/// The returned struct will remain valid as long as the context is valid.
+/// The returned struct may be updated when the user changes their preferences,
+/// so this function only needs to be called once, and calling it multiple
+/// times will return the same object each time.
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return An opaque struct containing information about user preferences.
+const gvr_user_prefs* gvr_get_user_prefs(gvr_context* gvr);
+
+/// Returns the controller handedness of the given gvr_user_prefs struct.
+///
+/// @param user_prefs Pointer to the gvr_user_prefs object returned by
+/// gvr_get_user_prefs.
+/// @return Either GVR_CONTROLLER_RIGHT_HANDED or GVR_CONTROLLER_LEFT_HANDED
+/// depending on which hand the user holds the controller in.
+int32_t gvr_user_prefs_get_controller_handedness(
+ const gvr_user_prefs* user_prefs);
+
+/// Destroys a gvr_context instance. The parameter will be nulled by this
+/// operation. Once this function is called, the behavior of any subsequent
+/// call to a GVR SDK function that references objects created from this
+/// context is undefined.
+///
+/// @param gvr Pointer to a pointer to the gvr instance to be destroyed and
+/// nulled.
+void gvr_destroy(gvr_context** gvr);
+
+/// Initializes necessary GL-related objects and uses the current thread and
+/// GL context for rendering. Please make sure that a valid GL context is
+/// available when this function is called. This should never be called more
+/// than once on the same GL context (doing so would cause resource leaks).
+///
+/// @param gvr Pointer to the gvr instance to be initialized.
+void gvr_initialize_gl(gvr_context* gvr);
+
+/// Gets whether asynchronous reprojection is currently enabled.
+///
+/// If enabled, frames will be collected by the rendering system and
+/// asynchronously re-projected in sync with the scanout of the display. This
+/// feature may not be available on every platform, and requires a
+/// high-priority render thread with special extensions to function properly.
+///
+/// Note: On Android, this feature can be enabled solely via the GvrLayout Java
+/// instance which (indirectly) owns this gvr_context. The corresponding
+/// method call is GvrLayout.setAsyncReprojectionEnabled().
+///
+/// Note: Because of the above requirements, asynchronous reprojection is only
+/// currently available on Daydream-ready Android devices. This function will
+/// always return false on other devices.
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return Whether async reprojection is enabled. Defaults to false.
+bool gvr_get_async_reprojection_enabled(const gvr_context* gvr);
+
+/// Gets the recommended buffer viewport configuration, populating a previously
+/// allocated gvr_buffer_viewport_list object. The updated values include the
+/// per-eye recommended viewport and field of view for the target.
+///
+/// When the recommended viewports are used for distortion rendering, this
+/// method should always be called after calling refresh_viewer_profile(). That
+/// will ensure that the populated viewports reflect the currently paired
+/// viewer.
+///
+/// @param gvr Pointer to the gvr instance from which to get the viewports.
+/// @param viewport_list Pointer to a previously allocated viewport list. This
+/// will be populated with the recommended buffer viewports and resized if
+/// necessary.
+void gvr_get_recommended_buffer_viewports(
+ const gvr_context* gvr, gvr_buffer_viewport_list* viewport_list);
+
+/// Gets the screen (non-distorted) buffer viewport configuration, populating a
+/// previously allocated gvr_buffer_viewport_list object. The updated values
+/// include the per-eye recommended viewport and field of view for the target.
+///
+/// @param gvr Pointer to the gvr instance from which to get the viewports.
+/// @param viewport_list Pointer to a previously allocated viewport list. This
+/// will be populated with the screen buffer viewports and resized if
+/// necessary.
+void gvr_get_screen_buffer_viewports(const gvr_context* gvr,
+ gvr_buffer_viewport_list* viewport_list);
+
+/// Returns the maximum effective size for the client's render target, given the
+/// parameters of the head mounted device selected. At this resolution, we have
+/// a 1:1 ratio between source pixels and screen pixels in the most magnified
+/// region of the screen. Applications should rarely, if ever, need to render
+/// to a larger target, as it will simply result in sampling artifacts.
+///
+/// Note that this is probably too large for most applications to use as a
+/// render target size. Applications should scale this value to be appropriate
+/// to their graphical load.
+///
+/// @param gvr Pointer to the gvr instance from which to get the size.
+///
+/// @return Maximum effective size for the target render target.
+gvr_sizei gvr_get_maximum_effective_render_target_size(const gvr_context* gvr);
+
+/// Returns a non-distorted size for the screen, given the parameters
+/// of the phone and/or the head mounted device selected.
+///
+/// @param gvr Pointer to the gvr instance from which to get the size.
+///
+/// @return Screen (non-distorted) size for the render target.
+gvr_sizei gvr_get_screen_target_size(const gvr_context* gvr);
+
+// Sets the size of the underlying render surface.
+//
+// By default, it is assumed that the display size matches the surface
+// size. If that is the case for the client app, this method need never be
+// called. However, in certain cases (e.g., hardware scaling), this will not
+// always hold, in which case the distortion pass must be informed of the
+// custom surface size.
+//
+// Note that the caller is responsible for resizing any BufferSpec objects
+// created before this function is called. Otherwise there will be rendering
+// artifacts, such as edges appearing pixelated. This function will change the
+// result of get_maximum_effective_render_target_size(), so that function can be
+// used to compute the appropriate size for buffers.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param surface_size_pixels The size in pixels of the display surface. If
+// non-empty, this will be used in conjunction with the current display to
+// perform properly scaled distortion. If empty, it is assumed that the
+// rendering surface dimensions match that of the active display.
+void gvr_set_surface_size(gvr_context* gvr, gvr_sizei surface_size_pixels);
+
+/// Performs postprocessing, including lens distortion, on the contents of the
+/// passed texture and shows the result on the screen. Lens distortion is
+/// determined by the parameters of the viewer encoded in its QR code. The
+/// passed texture is not modified.
+///
+/// If the application does not call gvr_initialize_gl() before calling this
+/// function, the results are undefined.
+///
+/// @deprecated This function exists only to support legacy rendering pathways
+/// for Cardboard devices. It is incompatible with the low-latency
+/// experiences supported by async reprojection. Use the swap chain API
+/// instead.
+///
+/// @param gvr Pointer to the gvr instance which will do the distortion.
+/// @param texture_id The OpenGL ID of the texture that contains the next frame
+/// to be displayed.
+/// @param viewport_list Rendering parameters.
+/// @param head_space_from_start_space This parameter is ignored.
+/// @param target_presentation_time This parameter is ignored.
+void gvr_distort_to_screen(gvr_context* gvr, int32_t texture_id,
+ const gvr_buffer_viewport_list* viewport_list,
+ gvr_mat4f head_space_from_start_space,
+ gvr_clock_time_point target_presentation_time);
+/// @}
+
+/////////////////////////////////////////////////////////////////////////////
+// Viewports and viewport lists
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup viewport Viewports and viewport lists
+/// @brief Objects to define the mapping between the application's rendering
+/// output and the user's field of view.
+/// @{
+
+/// Creates a gvr_buffer_viewport instance.
+gvr_buffer_viewport* gvr_buffer_viewport_create(gvr_context* gvr);
+
+/// Frees a gvr_buffer_viewport instance and clears the pointer.
+void gvr_buffer_viewport_destroy(gvr_buffer_viewport** viewport);
+
+/// Gets the UV coordinates specifying where the output buffer is sampled.
+///
+/// @param viewport The buffer viewport.
+/// @return UV coordinates as a rectangle.
+gvr_rectf gvr_buffer_viewport_get_source_uv(
+ const gvr_buffer_viewport* viewport);
+
+/// Sets the UV coordinates specifying where the output buffer should be
+/// sampled when compositing the final distorted image.
+///
+/// @param viewport The buffer viewport.
+/// @param uv The new UV coordinates for sampling. The coordinates must be
+/// valid, that is, left <= right and bottom <= top. Otherwise an empty
+/// source region is set, which will result in no output for this viewport.
+void gvr_buffer_viewport_set_source_uv(gvr_buffer_viewport* viewport,
+ gvr_rectf uv);
+
+/// Retrieves the field of view for the referenced buffer region.
+///
+/// This is a helper that converts the stored projection matrix to a field of
+/// view. Note that if the previously set projection matrix cannot be expressed
+/// as a view frustum aligned with the eye's optical axis, the result will be
+/// incorrect.
+///
+/// @param viewport The buffer viewport.
+/// @return The field of view of the rendered image, in degrees.
+gvr_rectf gvr_buffer_viewport_get_source_fov(
+ const gvr_buffer_viewport* viewport);
+
+/// Sets the field of view for the viewport contents.
+///
+/// This is a helper that sets the projection matrix in such a way that the
+/// viewport's contents fill the specified FOV around the eye's optical axis.
+///
+/// @param viewport The buffer viewport.
+/// @param fov The field of view to use when compositing the rendered image,
+/// in degrees.
+void gvr_buffer_viewport_set_source_fov(gvr_buffer_viewport* viewport,
+ gvr_rectf fov);
+
+/// Gets the matrix that positions the viewport in eye space.
+///
+/// @param viewport The buffer viewport.
+/// @return Matrix that transforms a quad with vertices (-1, -1, 0), (1, -1, 0),
+/// (-1, 1, 0), (1, 1, 0) representing the viewport contents to its desired
+/// eye space position for the target eye.
+gvr_mat4f gvr_buffer_viewport_get_transform(
+ const gvr_buffer_viewport* viewport);
+
+/// Sets the matrix that positions the viewport in eye space.
+///
+/// @param viewport The buffer viewport.
+/// @param transform Matrix that transforms a quad with vertices (-1, -1, 0),
+/// (1, -1, 0), (-1, 1, 0), (1, 1, 0) representing the viewport contents to
+/// its desired eye space position for the target eye.
+void gvr_buffer_viewport_set_transform(gvr_buffer_viewport* viewport,
+ gvr_mat4f transform);
+
+/// Gets the target logical eye for the specified viewport.
+///
+/// @param viewport The buffer viewport.
+/// @return Index of the target logical eye for this viewport.
+int32_t gvr_buffer_viewport_get_target_eye(const gvr_buffer_viewport* viewport);
+
+/// Sets the target logical eye for the specified viewport.
+///
+/// @param viewport The buffer viewport.
+/// @param index Index of the target logical eye.
+void gvr_buffer_viewport_set_target_eye(gvr_buffer_viewport* viewport,
+ int32_t index);
+
+/// Gets the index of the source buffer from which the viewport reads its
+/// undistorted pixels.
+///
+/// @param viewport The buffer viewport.
+/// @return Index of the source buffer. This corresponds to the index in the
+/// list of buffer specs that was passed to gvr_swap_chain_create().
+int32_t gvr_buffer_viewport_get_source_buffer_index(
+ const gvr_buffer_viewport* viewport);
+
+/// Sets the buffer from which the viewport reads its undistorted pixels.
+///
+/// To use the contents of the external surface as buffer contents, associate an
+/// external surface with the viewport by calling
+/// gvr_buffer_viewport_set_external_surface_id(), then call this function and
+/// pass GVR_BUFFER_INDEX_EXTERNAL_SURFACE.
+///
+/// @param viewport The buffer viewport.
+/// @param buffer_index The index of the source buffer. This is either an index
+/// in the list of buffer specs that was passed to
+/// gvr_swap_chain_create(), or GVR_BUFFER_INDEX_EXTERNAL_SURFACE.
+void gvr_buffer_viewport_set_source_buffer_index(
+ gvr_buffer_viewport* viewport, int32_t buffer_index);
+
+/// Gets the ID of the externally-managed Surface texture from which this
+/// viewport reads undistored pixels.
+///
+/// @param viewport The buffer viewport.
+/// @return ID of the externally-managed Surface of undistorted pixels.
+int32_t gvr_buffer_viewport_get_external_surface_id(
+ const gvr_buffer_viewport* viewport);
+
+/// Sets the ID of the externally-managed Surface texture from which this
+/// viewport reads. The ID is issued by GvrLayout.
+///
+/// @param viewport The buffer viewport.
+/// @param external_surface_id The ID of the surface to read from.
+void gvr_buffer_viewport_set_external_surface_id(
+ gvr_buffer_viewport* viewport, int32_t external_surface_id);
+
+/// Gets the type of reprojection to perform on the specified viewport.
+///
+/// @param viewport The buffer viewport.
+/// @return Type of reprojection that is applied to the viewport.
+int32_t gvr_buffer_viewport_get_reprojection(
+ const gvr_buffer_viewport* viewport);
+
+/// Sets the type of reprojection to perform on the specified viewport.
+/// Viewports that display world content should use full reprojection.
+/// Viewports that display head-locked UI should disable reprojection to avoid
+/// excessive judder. The default is to perform full reprojection.
+///
+/// @param viewport The buffer viewport.
+/// @param reprojection Type of reprojection that will be applied to the passed
+/// viewport.
+void gvr_buffer_viewport_set_reprojection(gvr_buffer_viewport* viewport,
+ int32_t reprojection);
+
+/// Compares two gvr_buffer_viewport instances and returns true if they specify
+/// the same view mapping.
+///
+/// @param a Instance of a buffer viewport.
+/// @param b Another instance of a buffer viewport.
+/// @return True if the passed viewports are the same.
+bool gvr_buffer_viewport_equal(const gvr_buffer_viewport* a,
+ const gvr_buffer_viewport* b);
+
+/// Creates a new, empty list of viewports. The viewport list defines how the
+/// application's rendering output should be transformed into the stabilized,
+/// lens-distorted image that is sent to the screen.
+///
+/// The caller should populate the returned viewport using one of:
+/// - gvr_get_recommended_buffer_viewports()
+/// - gvr_get_screen_buffer_viewports()
+/// - gvr_buffer_viewport_list_set_item()
+///
+/// @param gvr Pointer the gvr instance from which to allocate the viewport
+/// list.
+/// @return Pointer to an allocated gvr_buffer_viewport_list object. The caller
+// is responsible for calling gvr_buffer_viewport_list_destroy() on the
+/// returned object when it is no longer needed.
+gvr_buffer_viewport_list* gvr_buffer_viewport_list_create(
+ const gvr_context* gvr);
+
+/// Destroys a gvr_buffer_viewport_list instance. The parameter will be nulled
+/// by this operation.
+///
+/// @param viewport_list Pointer to a pointer to the viewport list instance to
+/// be destroyed and nulled.
+void gvr_buffer_viewport_list_destroy(gvr_buffer_viewport_list** viewport_list);
+
+/// Returns the size of the given viewport list.
+///
+/// @param viewport_list Pointer to a viewport list.
+/// @return The number of entries in the viewport list.
+size_t gvr_buffer_viewport_list_get_size(
+ const gvr_buffer_viewport_list* viewport_list);
+
+/// Retrieve a buffer viewport entry from a list.
+///
+/// @param viewport_list Pointer to the previously allocated viewport list.
+/// @param index Zero-based index of the viewport entry to query. Must be
+/// smaller than the list size.
+/// @param viewport The buffer viewport structure that will be populated with
+/// retrieved data.
+void gvr_buffer_viewport_list_get_item(
+ const gvr_buffer_viewport_list* viewport_list, size_t index,
+ gvr_buffer_viewport* viewport);
+
+/// Update an element of the viewport list or append a new one at the end.
+///
+/// @param viewport_list Pointer to a previously allocated viewport list.
+/// @param index Index of the buffer viewport entry to update. If the
+/// `viewport_list` size is equal to the index, a new viewport entry will be
+/// added. The `viewport_list` size must *not* be less than the index value.
+/// @param viewport A pointer to the buffer viewport object.
+void gvr_buffer_viewport_list_set_item(gvr_buffer_viewport_list* viewport_list,
+ size_t index,
+ const gvr_buffer_viewport* viewport);
+
+/// @}
+
+/////////////////////////////////////////////////////////////////////////////
+// Swapchains and frames
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup swap_chain Swap chains and frames
+/// @brief Functions to create a swap chain, manipulate it and submit frames
+/// for lens distortion and presentation on the screen.
+/// @{
+
+/// Creates a default buffer specification.
+gvr_buffer_spec* gvr_buffer_spec_create(gvr_context* gvr);
+
+/// Destroy the buffer specification and null the pointer.
+void gvr_buffer_spec_destroy(gvr_buffer_spec** spec);
+
+/// Gets the size of the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @return Size of the pixel buffer. The default is equal to the recommended
+/// render target size at the time when the specification was created.
+gvr_sizei gvr_buffer_spec_get_size(const gvr_buffer_spec* spec);
+
+/// Sets the size of the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @param size The size. Width and height must both be greater than zero.
+/// Otherwise, the application is aborted.
+void gvr_buffer_spec_set_size(gvr_buffer_spec* spec, gvr_sizei size);
+
+/// Gets the number of samples per pixel in the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @return Value >= 1 giving the number of samples. 1 means multisampling is
+/// disabled. Negative values and 0 are never returned.
+int32_t gvr_buffer_spec_get_samples(const gvr_buffer_spec* spec);
+
+/// Sets the number of samples per pixel in the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @param num_samples The number of samples. Negative values are an error.
+/// The values 0 and 1 are treated identically and indicate that
+// multisampling should be disabled.
+void gvr_buffer_spec_set_samples(gvr_buffer_spec* spec, int32_t num_samples);
+
+/// Sets the color format for the buffer to be created. Default format is
+/// GVR_COLOR_FORMAT_RGBA_8888.
+///
+/// @param spec Buffer specification.
+/// @param color_format The color format for the buffer. Valid formats are in
+/// the gvr_color_format_type enum.
+void gvr_buffer_spec_set_color_format(gvr_buffer_spec* spec,
+ int32_t color_format);
+
+/// Sets the depth and stencil format for the buffer to be created. Currently,
+/// only packed stencil formats are supported. Default format is
+/// GVR_DEPTH_STENCIL_FORMAT_DEPTH_16.
+///
+/// @param spec Buffer specification.
+/// @param depth_stencil_format The depth and stencil format for the buffer.
+/// Valid formats are in the gvr_depth_stencil_format_type enum.
+void gvr_buffer_spec_set_depth_stencil_format(gvr_buffer_spec* spec,
+ int32_t depth_stencil_format);
+
+/// Creates a swap chain from the given buffer specifications.
+/// This is a potentially time-consuming operation. All frames within the
+/// swapchain will be allocated. Once rendering is stopped, call
+/// gvr_swap_chain_destroy() to free GPU resources. The passed gvr_context must
+/// not be destroyed until then.
+///
+/// Swap chains can have no buffers. This is useful when only displaying
+/// external surfaces. When `count` is zero, `buffers` must be null.
+///
+/// @param gvr GVR instance for which a swap chain will be created.
+/// @param buffers Array of pixel buffer specifications. Each frame in the
+/// swap chain will be composed of these buffers.
+/// @param count Number of buffer specifications in the array.
+/// @return Opaque handle to the newly created swap chain.
+gvr_swap_chain* gvr_swap_chain_create(gvr_context* gvr,
+ const gvr_buffer_spec** buffers,
+ int32_t count);
+
+/// Destroys the swap chain and nulls the pointer.
+void gvr_swap_chain_destroy(gvr_swap_chain** swap_chain);
+
+/// Gets the number of buffers in each frame of the swap chain.
+int32_t gvr_swap_chain_get_buffer_count(const gvr_swap_chain* swap_chain);
+
+/// Retrieves the size of the specified pixel buffer. Note that if the buffer
+/// was resized while the current frame was acquired, the return value will be
+/// different than the value obtained from the equivalent function for the
+/// current frame.
+///
+/// @param swap_chain The swap chain.
+/// @param index Index of the pixel buffer.
+/// @return Size of the specified pixel buffer in frames that will be returned
+/// from gvr_swap_chain_acquire_frame().
+gvr_sizei gvr_swap_chain_get_buffer_size(gvr_swap_chain* swap_chain,
+ int32_t index);
+
+/// Resizes the specified pixel buffer to the given size. The frames are resized
+/// when they are unused, so the currently acquired frame will not be resized
+/// immediately.
+///
+/// @param swap_chain The swap chain.
+/// @param index Index of the pixel buffer to resize.
+/// @param size New size for the specified pixel buffer.
+void gvr_swap_chain_resize_buffer(gvr_swap_chain* swap_chain, int32_t index,
+ gvr_sizei size);
+
+/// Acquires a frame from the swap chain for rendering. Buffers that are part of
+/// the frame can then be bound with gvr_frame_bind_buffer(). Once the frame
+/// is finished and all its constituent buffers are ready, call
+/// gvr_frame_submit() to display it while applying lens distortion.
+///
+/// @param swap_chain The swap chain.
+/// @return Handle to the acquired frame. NULL if the swap chain is invalid,
+/// or if acquire has already been called on this swap chain.
+gvr_frame* gvr_swap_chain_acquire_frame(gvr_swap_chain* swap_chain);
+
+/// Binds a pixel buffer that is part of the frame to the OpenGL framebuffer.
+///
+/// @param frame Frame handle acquired from the swap chain.
+/// @param index Index of the pixel buffer to bind. This corresponds to the
+/// index in the buffer spec list that was passed to
+/// gvr_swap_chain_create().
+void gvr_frame_bind_buffer(gvr_frame* frame, int32_t index);
+
+/// Unbinds any buffers bound from this frame and binds the default OpenGL
+/// framebuffer.
+void gvr_frame_unbind(gvr_frame* frame);
+
+/// Returns the dimensions of the pixel buffer with the specified index. Note
+/// that a frame that was acquired before resizing a swap chain buffer will not
+/// be resized until it is submitted to the swap chain.
+///
+/// @param frame Frame handle.
+/// @param index Index of the pixel buffer to inspect.
+/// @return Dimensions of the specified pixel buffer.
+gvr_sizei gvr_frame_get_buffer_size(const gvr_frame* frame, int32_t index);
+
+/// Gets the name (ID) of the framebuffer object associated with the specified
+/// buffer. The OpenGL state is not modified.
+///
+/// @param frame Frame handle.
+/// @param index Index of a pixel buffer.
+/// @return OpenGL object name (ID) of a framebuffer object which can be used
+/// to render into the buffer. The ID is valid only until the frame is
+/// submitted.
+int32_t gvr_frame_get_framebuffer_object(const gvr_frame* frame, int32_t index);
+
+/// Submits the frame for distortion and display on the screen. The passed
+/// pointer is nulled to prevent reuse.
+///
+/// @param frame The frame to submit.
+/// @param list Buffer view configuration to be used for this frame.
+/// @param head_space_from_start_space Transform from start space (space with
+/// head at the origin at last tracking reset) to head space (space with
+/// head at the origin and axes aligned to the view vector).
+void gvr_frame_submit(gvr_frame** frame, const gvr_buffer_viewport_list* list,
+ gvr_mat4f head_space_from_start_space);
+
+/// Resets the OpenGL framebuffer binding to what it was at the time the
+/// passed gvr_context was created.
+void gvr_bind_default_framebuffer(gvr_context* gvr);
+
+/// @}
+
+/////////////////////////////////////////////////////////////////////////////
+// Head tracking
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup Headtracking Head tracking
+/// @brief Functions for managing head tracking.
+/// @{
+
+/// Gets the current monotonic system time.
+///
+/// @return The current monotonic system time.
+gvr_clock_time_point gvr_get_time_point_now();
+
+/// Gets the rotation from start space to head space. The head space is a
+/// space where the head is at the origin and faces the -Z direction.
+///
+/// @param gvr Pointer to the gvr instance from which to get the pose.
+/// @param time The time at which to get the head pose. The time should be in
+/// the future. If the time is not in the future, it will be clamped to now.
+/// @return A matrix representation of the rotation from start space (the space
+/// where the head was last reset) to head space (the space with the head
+/// at the origin, and the axes aligned to the view vector).
+gvr_mat4f gvr_get_head_space_from_start_space_rotation(
+ const gvr_context* gvr, const gvr_clock_time_point time);
+
+/// Applies a simple neck model translation based on the rotation of the
+/// provided head pose.
+///
+/// Note: Neck model application may not be appropriate for all tracking
+/// scenarios, e.g., when tracking is non-biological.
+///
+/// @param gvr Pointer to the context instance from which the pose was obtained.
+/// @param head_rotation_in_start_space The head rotation as returned by
+/// gvr_get_head_space_from_start_space_rotation().
+/// @param factor A scaling factor for the neck model offset, clamped from 0 to
+/// 1. This should be 1 for most scenarios, while 0 will effectively disable
+/// neck model application. This value can be animated to smoothly
+/// interpolate between alternative (client-defined) neck models.
+/// @return The new head pose with the neck model applied.
+gvr_mat4f gvr_apply_neck_model(const gvr_context* gvr,
+ gvr_mat4f head_space_from_start_space_rotation,
+ float factor);
+
+/// Pauses head tracking, disables all sensors (to save power).
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be paused and
+/// sensors disabled.
+void gvr_pause_tracking(gvr_context* gvr);
+
+/// Resumes head tracking, re-enables all sensors.
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be resumed.
+void gvr_resume_tracking(gvr_context* gvr);
+
+/// Resets head tracking.
+///
+/// This API call is deprecated. Use gvr_recenter_tracking instead, which
+/// accomplishes the same effects but avoids the undesirable side-effects of
+/// a full reset (temporary loss of tracking quality).
+///
+/// Only to be used by Cardboard apps. Daydream apps must not call this. On the
+/// Daydream platform, recentering is handled automatically and should never
+/// be triggered programatically by applications. Hybrid apps that support both
+/// Cardboard and Daydream must only call this function when in Cardboard mode
+/// (that is, when the phone is paired with a Cardboard viewer), never in
+/// Daydream mode.
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be reseted.
+/// @deprecated Calls to this method can be safely replaced by calls to
+// gvr_recenter_tracking.
+void gvr_reset_tracking(gvr_context* gvr);
+
+/// Recenters the head orientation (resets the yaw to zero, leaving pitch and
+/// roll unmodified).
+///
+/// Only to be used by Cardboard apps. Daydream apps must not call this. On the
+/// Daydream platform, recentering is handled automatically and should never
+/// be triggered programatically by applications. Hybrid apps that support both
+/// Cardboard and Daydream must only call this function when in Cardboard mode
+/// (that is, when the phone is paired with a Cardboard viewer), never in
+/// Daydream mode.
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be
+/// recentered.
+void gvr_recenter_tracking(gvr_context* gvr);
+
+/// @}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Head mounted display.
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup HMD Head Mounted Display
+/// @brief Functions for managing viewer information.
+/// @{
+
+/// Sets the default viewer profile specified by viewer_profile_uri.
+/// The viewer_profile_uri that is passed in will be ignored if a valid
+/// viewer profile has already been stored on the device that the app
+/// is running on.
+///
+/// Note: This function has the potential of blocking for up to 30 seconds for
+/// each redirect if a shortened URI is passed in as argument. It will try to
+/// unroll the shortened URI for a maximum number of 5 times if the redirect
+/// continues. In that case, it is recommended to create a separate thread to
+/// call this function so that other tasks like rendering will not be blocked
+/// on this. The blocking can be avoided if a standard URI is passed in.
+///
+/// @param gvr Pointer to the gvr instance which to set the profile on.
+/// @param viewer_profile_uri A string that contains either the shortened URI or
+/// the standard URI representing the viewer profile that the app should be
+/// using. If the valid viewer profile can be found on the device, the URI
+/// that is passed in will be ignored and nothing will happen. Otherwise,
+/// gvr will look for the viewer profile specified by viewer_profile_uri,
+/// and it will be stored if found. Also, the values will be applied to gvr.
+/// A valid standard URI can be generated from this page:
+/// https://www.google.com/get/cardboard/viewerprofilegenerator/
+/// @return True if the viewer profile specified by viewer_profile_uri was
+/// successfully stored and applied, false otherwise.
+bool gvr_set_default_viewer_profile(gvr_context* gvr,
+ const char* viewer_profile_uri);
+
+/// Refreshes gvr_context with the viewer profile that is stored on the device.
+/// If it can not find the viewer profile, nothing will happen.
+///
+/// @param gvr Pointer to the gvr instance to refresh the profile on.
+void gvr_refresh_viewer_profile(gvr_context* gvr);
+
+/// Gets the name of the viewer vendor.
+///
+/// @param gvr Pointer to the gvr instance from which to get the vendor.
+/// @return A pointer to the vendor name. May be NULL if no viewer is paired.
+/// WARNING: This method guarantees the validity of the returned pointer
+/// only until the next use of the `gvr` context. The string should be
+/// copied immediately if persistence is required.
+const char* gvr_get_viewer_vendor(const gvr_context* gvr);
+
+/// Gets the name of the viewer model.
+///
+/// @param gvr Pointer to the gvr instance from which to get the name.
+/// @return A pointer to the model name. May be NULL if no viewer is paired.
+/// WARNING: This method guarantees the validity of the returned pointer
+/// only until the next use of the `gvr` context. The string should be
+/// copied immediately if persistence is required.
+const char* gvr_get_viewer_model(const gvr_context* gvr);
+
+/// Gets the type of the viewer, as defined by gvr_viewer_type.
+///
+/// @param gvr Pointer to the gvr instance from which to get the viewer type.
+/// @return The gvr_viewer_type of the currently paired viewer.
+int32_t gvr_get_viewer_type(const gvr_context* gvr);
+
+/// Gets the transformation matrix to convert from Head Space to Eye Space for
+/// the given eye.
+///
+/// @param gvr Pointer to the gvr instance from which to get the matrix.
+/// @param eye Selected gvr_eye type.
+/// @return Transformation matrix from Head Space to selected Eye Space.
+gvr_mat4f gvr_get_eye_from_head_matrix(const gvr_context* gvr,
+ const int32_t eye);
+
+/// Gets the window bounds.
+///
+/// @param gvr Pointer to the gvr instance from which to get the bounds.
+///
+/// @return Window bounds in physical pixels.
+gvr_recti gvr_get_window_bounds(const gvr_context* gvr);
+
+/// Computes the distorted point for a given point in a given eye. The
+/// distortion inverts the optical distortion caused by the lens for the eye.
+/// Due to chromatic aberration, the distortion is different for each
+/// color channel.
+///
+/// @param gvr Pointer to the gvr instance which will do the computing.
+/// @param eye The gvr_eye type (left or right).
+/// @param uv_in A point in screen eye Viewport Space in [0,1]^2 with (0, 0)
+/// in the lower left corner of the eye's viewport and (1, 1) in the
+/// upper right corner of the eye's viewport.
+/// @param uv_out A pointer to an array of (at least) 3 elements, with each
+/// element being a Point2f representing a point in render texture eye
+/// Viewport Space in [0,1]^2 with (0, 0) in the lower left corner of the
+/// eye's viewport and (1, 1) in the upper right corner of the eye's
+/// viewport.
+/// `uv_out[0]` is the corrected position of `uv_in` for the red channel
+/// `uv_out[1]` is the corrected position of `uv_in` for the green channel
+/// `uv_out[2]` is the corrected position of `uv_in` for the blue channel
+void gvr_compute_distorted_point(const gvr_context* gvr, const int32_t eye,
+ const gvr_vec2f uv_in, gvr_vec2f uv_out[3]);
+
+/// @}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+namespace gvr {
+
+/// Convenience C++ wrapper for gvr_user_prefs.
+class UserPrefs {
+ public:
+ /// Creates a C++ wrapper for a gvr_user_prefs object. Note that unlike most
+ /// of the C++ wrappers in the API, this does not take ownership, as the
+ /// gvr_user_prefs will remain valid for the lifetime of the GVR context.
+ explicit UserPrefs(const gvr_user_prefs* user_prefs)
+ : user_prefs_(user_prefs) {}
+
+ UserPrefs(UserPrefs&& other) : user_prefs_(nullptr) {
+ std::swap(user_prefs_, other.user_prefs_);
+ }
+
+ UserPrefs& operator=(UserPrefs&& other) {
+ std::swap(user_prefs_, other.user_prefs_);
+ return *this;
+ }
+
+ /// For more information, see gvr_user_prefs_get_controller_handedness().
+ ControllerHandedness GetControllerHandedness() const {
+ return static_cast<ControllerHandedness>(
+ gvr_user_prefs_get_controller_handedness(user_prefs_));
+ }
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ const gvr_user_prefs* cobj() const { return user_prefs_; }
+
+ private:
+ const gvr_user_prefs* user_prefs_;
+
+ // Disallow copy and assign.
+ UserPrefs(const UserPrefs&);
+ void operator=(const UserPrefs&);
+};
+
+/// Convenience C++ wrapper for the opaque gvr_buffer_viewport type.
+/// The constructor allocates memory, so when used in tight loops, instances
+/// should be reused.
+class BufferViewport {
+ public:
+ BufferViewport(BufferViewport&& other)
+ : viewport_(nullptr) {
+ std::swap(viewport_, other.viewport_);
+ }
+
+ BufferViewport& operator=(BufferViewport&& other) {
+ std::swap(viewport_, other.viewport_);
+ return *this;
+ }
+
+ ~BufferViewport() {
+ if (viewport_) gvr_buffer_viewport_destroy(&viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_source_fov().
+ Rectf GetSourceFov() const {
+ return gvr_buffer_viewport_get_source_fov(viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_set_source_fov().
+ void SetSourceFov(const Rectf& fov) {
+ gvr_buffer_viewport_set_source_fov(viewport_, fov);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_transform().
+ Mat4f GetTransform() const {
+ return gvr_buffer_viewport_get_transform(viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_set_transform().
+ void SetTransform(const Mat4f& transform) {
+ gvr_buffer_viewport_set_transform(viewport_, transform);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_source_uv().
+ Rectf GetSourceUv() const {
+ return gvr_buffer_viewport_get_source_uv(viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_set_source_uv().
+ void SetSourceUv(const Rectf& uv) {
+ gvr_buffer_viewport_set_source_uv(viewport_, uv);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_target_eye().
+ Eye GetTargetEye() const {
+ return static_cast<Eye>(gvr_buffer_viewport_get_target_eye(viewport_));
+ }
+
+ /// For more information, see gvr_buffer_viewport_set_target_eye().
+ void SetTargetEye(Eye eye) {
+ gvr_buffer_viewport_set_target_eye(viewport_, eye);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_source_buffer_index().
+ int32_t GetSourceBufferIndex() const {
+ return gvr_buffer_viewport_get_source_buffer_index(viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_set_source_buffer_index().
+ void SetSourceBufferIndex(int32_t buffer_index) {
+ gvr_buffer_viewport_set_source_buffer_index(viewport_, buffer_index);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_external_surface_id().
+ int32_t GetExternalSurfaceId() const {
+ return gvr_buffer_viewport_get_external_surface_id(viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_set_external_surface_id().
+ void SetExternalSurfaceId(const int32_t external_surface_id) {
+ gvr_buffer_viewport_set_external_surface_id(viewport_, external_surface_id);
+ }
+
+ /// For more information, see gvr_buffer_viewport_get_reprojection().
+ gvr_reprojection GetReprojection() const {
+ return static_cast<gvr_reprojection>(
+ gvr_buffer_viewport_get_reprojection(viewport_));
+ }
+ /// For more information, see gvr_buffer_viewport_set_reprojection().
+ void SetReprojection(gvr_reprojection reprojection) {
+ gvr_buffer_viewport_set_reprojection(viewport_, reprojection);
+ }
+
+ /// For more information, see gvr_buffer_viewport_equal().
+ bool operator==(const BufferViewport& other) const {
+ return gvr_buffer_viewport_equal(viewport_, other.viewport_) ? true : false;
+ }
+ bool operator!=(const BufferViewport& other) const {
+ return !(*this == other);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit BufferViewport(gvr_buffer_viewport* viewport)
+ : viewport_(viewport) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_buffer_viewport* cobj() { return viewport_; }
+ const gvr_buffer_viewport* cobj() const { return viewport_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_buffer_viewport* release() {
+ auto result = viewport_;
+ viewport_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ friend class GvrApi;
+ friend class BufferViewportList;
+
+ explicit BufferViewport(gvr_context* gvr)
+ : viewport_(gvr_buffer_viewport_create(gvr)) {}
+
+ gvr_buffer_viewport* viewport_;
+};
+
+/// Convenience C++ wrapper for the opaque gvr_buffer_viewport_list type. This
+/// class will automatically release the wrapped gvr_buffer_viewport_list upon
+/// destruction. It can only be created via a `GvrApi` instance, and its
+/// validity is tied to the lifetime of that instance.
+class BufferViewportList {
+ public:
+ BufferViewportList(BufferViewportList&& other)
+ : context_(nullptr), viewport_list_(nullptr) {
+ std::swap(context_, other.context_);
+ std::swap(viewport_list_, other.viewport_list_);
+ }
+
+ BufferViewportList& operator=(BufferViewportList&& other) {
+ std::swap(context_, other.context_);
+ std::swap(viewport_list_, other.viewport_list_);
+ return *this;
+ }
+
+ ~BufferViewportList() {
+ if (viewport_list_) {
+ gvr_buffer_viewport_list_destroy(&viewport_list_);
+ }
+ }
+
+ /// For more information, see gvr_get_recommended_buffer_viewports().
+ void SetToRecommendedBufferViewports() {
+ gvr_get_recommended_buffer_viewports(context_, viewport_list_);
+ }
+
+ /// For more information, see gvr_get_screen_buffer_viewports().
+ void SetToScreenBufferViewports() {
+ gvr_get_screen_buffer_viewports(context_, viewport_list_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_list_set_item().
+ void SetBufferViewport(size_t index, const BufferViewport& viewport) {
+ gvr_buffer_viewport_list_set_item(viewport_list_, index,
+ viewport.viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_list_get_item().
+ void GetBufferViewport(size_t index, BufferViewport* viewport) const {
+ gvr_buffer_viewport_list_get_item(viewport_list_, index,
+ viewport->viewport_);
+ }
+
+ /// For more information, see gvr_buffer_viewport_list_get_size().
+ size_t GetSize() const {
+ return gvr_buffer_viewport_list_get_size(viewport_list_);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ BufferViewportList(gvr_buffer_viewport_list* viewport_list,
+ gvr_context* context)
+ : context_(context),
+ viewport_list_(viewport_list) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_buffer_viewport_list* cobj() { return viewport_list_; }
+ const gvr_buffer_viewport_list* cobj() const { return viewport_list_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_buffer_viewport_list* release() {
+ auto result = viewport_list_;
+ viewport_list_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ friend class Frame;
+ friend class GvrApi;
+ friend class SwapChain;
+
+ explicit BufferViewportList(gvr_context* context)
+ : context_(context),
+ viewport_list_(gvr_buffer_viewport_list_create(context)) {}
+
+ const gvr_context* context_;
+ gvr_buffer_viewport_list* viewport_list_;
+
+ // Disallow copy and assign.
+ BufferViewportList(const BufferViewportList&) = delete;
+ void operator=(const BufferViewportList&) = delete;
+};
+
+/// Convenience C++ wrapper for gvr_buffer_spec, an opaque pixel buffer
+/// specification. Frees the underlying gvr_buffer_spec on destruction.
+class BufferSpec {
+ public:
+ BufferSpec(BufferSpec&& other)
+ : spec_(nullptr) {
+ std::swap(spec_, other.spec_);
+ }
+
+ BufferSpec& operator=(BufferSpec&& other) {
+ std::swap(spec_, other.spec_);
+ return *this;
+ }
+
+ ~BufferSpec() {
+ if (spec_) gvr_buffer_spec_destroy(&spec_);
+ }
+
+ /// Gets the buffer's size. The default value is the recommended render
+ /// target size. For more information, see gvr_buffer_spec_get_size().
+ Sizei GetSize() const {
+ return gvr_buffer_spec_get_size(spec_);
+ }
+
+ /// Sets the buffer's size. For more information, see
+ /// gvr_buffer_spec_set_size().
+ void SetSize(const Sizei& size) {
+ gvr_buffer_spec_set_size(spec_, size);
+ }
+
+ /// Sets the buffer's size to the passed width and height. For more
+ /// information, see gvr_buffer_spec_set_size().
+ ///
+ /// @param width The width in pixels. Must be greater than 0.
+ /// @param height The height in pixels. Must be greater than 0.
+ void SetSize(int32_t width, int32_t height) {
+ gvr_sizei size{width, height};
+ gvr_buffer_spec_set_size(spec_, size);
+ }
+
+ /// Gets the number of samples per pixel in the buffer. For more
+ /// information, see gvr_buffer_spec_get_samples().
+ int32_t GetSamples() const { return gvr_buffer_spec_get_samples(spec_); }
+
+ /// Sets the number of samples per pixel. For more information, see
+ /// gvr_buffer_spec_set_samples().
+ void SetSamples(int32_t num_samples) {
+ gvr_buffer_spec_set_samples(spec_, num_samples);
+ }
+
+ /// Sets the color format for this buffer. For more information, see
+ /// gvr_buffer_spec_set_color_format().
+ void SetColorFormat(ColorFormat color_format) {
+ gvr_buffer_spec_set_color_format(spec_, color_format);
+ }
+
+ /// Sets the depth and stencil format for this buffer. For more
+ /// information, see gvr_buffer_spec_set_depth_stencil_format().
+ void SetDepthStencilFormat(DepthStencilFormat depth_stencil_format) {
+ gvr_buffer_spec_set_depth_stencil_format(spec_, depth_stencil_format);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit BufferSpec(gvr_buffer_spec* spec) : spec_(spec) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_buffer_spec* cobj() { return spec_; }
+ const gvr_buffer_spec* cobj() const { return spec_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_buffer_spec* release() {
+ auto result = spec_;
+ spec_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ friend class GvrApi;
+ friend class SwapChain;
+
+ explicit BufferSpec(gvr_context* gvr) {
+ spec_ = gvr_buffer_spec_create(gvr);
+ }
+
+ gvr_buffer_spec* spec_;
+};
+
+/// Convenience C++ wrapper for gvr_frame, which represents a single frame
+/// acquired for rendering from the swap chain.
+class Frame {
+ public:
+ Frame(Frame&& other) : frame_(nullptr) {
+ std::swap(frame_, other.frame_);
+ }
+
+ Frame& operator=(Frame&& other) {
+ std::swap(frame_, other.frame_);
+ return *this;
+ }
+
+ ~Frame() {
+ // The swap chain owns the frame, so no clean-up is required.
+ }
+
+ /// For more information, see gvr_frame_get_buffer_size().
+ Sizei GetBufferSize(int32_t index) const {
+ return gvr_frame_get_buffer_size(frame_, index);
+ }
+
+ /// For more information, see gvr_frame_bind_buffer().
+ void BindBuffer(int32_t index) {
+ gvr_frame_bind_buffer(frame_, index);
+ }
+
+ /// For more information, see gvr_frame_unbind().
+ void Unbind() {
+ gvr_frame_unbind(frame_);
+ }
+
+ /// For more information, see gvr_frame_get_framebuffer_object().
+ int32_t GetFramebufferObject(int32_t index) {
+ return gvr_frame_get_framebuffer_object(frame_, index);
+ }
+
+ /// For more information, see gvr_frame_submit().
+ void Submit(const BufferViewportList& viewport_list,
+ const Mat4f& head_space_from_start_space) {
+ gvr_frame_submit(&frame_, viewport_list.viewport_list_,
+ head_space_from_start_space);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit Frame(gvr_frame* frame) : frame_(frame) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_frame* cobj() { return frame_; }
+ const gvr_frame* cobj() const { return frame_; }
+
+ /// Returns whether the wrapped gvr_frame reference is valid.
+ bool is_valid() const { return frame_ != nullptr; }
+ explicit operator bool const() { return is_valid(); }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_frame* release() {
+ auto result = frame_;
+ frame_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ friend class SwapChain;
+
+ gvr_frame* frame_;
+};
+
+/// Convenience C++ wrapper for gvr_swap_chain, which represents a queue of
+/// frames. The GvrApi object must outlive any SwapChain objects created from
+/// it.
+class SwapChain {
+ public:
+ SwapChain(SwapChain&& other)
+ : swap_chain_(nullptr) {
+ std::swap(swap_chain_, other.swap_chain_);
+ }
+
+ SwapChain& operator=(SwapChain&& other) {
+ std::swap(swap_chain_, other.swap_chain_);
+ return *this;
+ }
+
+ ~SwapChain() {
+ if (swap_chain_) gvr_swap_chain_destroy(&swap_chain_);
+ }
+
+ /// For more information, see gvr_swap_chain_get_buffer_count().
+ int32_t GetBufferCount() const {
+ return gvr_swap_chain_get_buffer_count(swap_chain_);
+ }
+
+ /// For more information, see gvr_swap_chain_get_buffer_size().
+ Sizei GetBufferSize(int32_t index) const {
+ return gvr_swap_chain_get_buffer_size(swap_chain_, index);
+ }
+
+ /// For more information, see gvr_swap_chain_resize_buffer().
+ void ResizeBuffer(int32_t index, Sizei size) {
+ gvr_swap_chain_resize_buffer(swap_chain_, index, size);
+ }
+
+ /// For more information, see gvr_swap_chain_acquire_frame().
+ /// Note that if Frame acquisition fails, the returned Frame may not be valid.
+ /// The caller should inspect the returned Frame's validity before using,
+ /// and reschedule frame acquisition upon failure.
+ Frame AcquireFrame() {
+ Frame result(gvr_swap_chain_acquire_frame(swap_chain_));
+ return result;
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit SwapChain(gvr_swap_chain* swap_chain) : swap_chain_(swap_chain) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_swap_chain* cobj() { return swap_chain_; }
+ const gvr_swap_chain* cobj() const { return swap_chain_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_swap_chain* release() {
+ auto result = swap_chain_;
+ swap_chain_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ friend class GvrApi;
+
+ SwapChain(gvr_context* gvr, const std::vector<BufferSpec>& specs) {
+ std::vector<const gvr_buffer_spec*> c_specs;
+ for (const auto& spec : specs)
+ c_specs.push_back(spec.spec_);
+ swap_chain_ = gvr_swap_chain_create(gvr, c_specs.data(),
+ static_cast<int32_t>(c_specs.size()));
+ }
+
+ gvr_swap_chain* swap_chain_;
+
+ // Disallow copy and assign.
+ SwapChain(const SwapChain&);
+ void operator=(const SwapChain&);
+};
+
+/// This is a convenience C++ wrapper for the Google VR C API.
+///
+/// This wrapper strategy prevents ABI compatibility issues between compilers
+/// by ensuring that the interface between client code and the implementation
+/// code in libgvr.so is a pure C interface. The translation from C++ calls
+/// to C calls provided by this wrapper runs entirely in the client's binary
+/// and is compiled by the client's compiler.
+///
+/// Methods in this class are only documented insofar as the C++ wrapping logic
+/// is concerned; for information about the method itself, please refer to the
+/// corresponding function in the C API.
+///
+/// Example API usage:
+///
+/// // Functionality supplied by the application in the example below has
+/// // the "app-" prefix.
+/// #ifdef __ANDROID__
+/// // On Android, the gvr_context should almost always be obtained from the
+/// // Java GvrLayout object via
+/// // GvrLayout.getGvrApi().getNativeGvrContext().
+/// std::unique_ptr<GvrApi> gvr = GvrApi::WrapNonOwned(gvr_context);
+/// #else
+/// std::unique_ptr<GvrApi> gvr = GvrApi::Create();
+/// #endif
+///
+/// gvr->InitializeGl();
+///
+/// gvr::BufferViewportList viewport_list =
+/// gvr->CreateEmptyBufferViewportList();
+/// gvr->GetRecommendedBufferViewports(&viewport_list);
+/// gvr::BufferViewport left_eye_viewport = gvr->CreateBufferViewport();
+/// gvr::BufferViewport right_eye_viewport = gvr->CreateBufferViewport();
+/// viewport_list.Get(0, &left_eye_view);
+/// viewport_list.Get(1, &right_eye_view);
+///
+/// std::vector<gvr::BufferSpec> specs;
+/// specs.push_back(gvr->CreateBufferSpec());
+/// specs[0].SetSamples(app_samples);
+/// gvr::SwapChain swap_chain = gvr->CreateSwapChain(specs);
+///
+/// while (client_app_should_render) {
+/// // A client app should be ready for the render target size to change
+/// // whenever a new QR code is scanned, or a new viewer is paired.
+/// gvr::Sizei render_target_size =
+/// gvr->GetRecommendedRenderTargetSize();
+/// swap_chain.ResizeBuffer(0, render_target_size);
+/// gvr::Frame frame = swap_chain.AcquireFrame();
+/// while (!frame) {
+/// std::this_thread::sleep_for(2ms);
+/// frame = swap_chain.AcquireFrame();
+/// }
+///
+/// // This function will depend on your render loop's implementation.
+/// gvr::ClockTimePoint next_vsync = AppGetNextVsyncTime();
+///
+/// const gvr::Mat4f head_view =
+/// gvr->GetHeadSpaceFromStartSpaceRotation(next_vsync);
+/// const gvr::Mat4f left_eye_view = MatrixMultiply(
+/// gvr->GetEyeFromHeadMatrix(kLeftEye), head_view);
+/// const gvr::Mat4f right_eye_view = MatrixMultiply(
+/// gvr->GetEyeFromHeadMatrix(kRightEye), head_view);
+///
+/// frame.BindBuffer(0);
+/// // App does its rendering to the current framebuffer here.
+/// AppDoSomeRenderingForEye(
+/// left_eye_viewport.GetSourceUv(), left_eye_view);
+/// AppDoSomeRenderingForEye(
+/// right_eye_viewport.GetSourceUv(), right_eye_view);
+/// frame.Unbind();
+/// frame.Submit(viewport_list, head_matrix);
+/// }
+///
+class GvrApi {
+ public:
+#ifdef __ANDROID__
+ /// Instantiates and returns a GvrApi instance that owns a gvr_context.
+ ///
+ /// @param env The JNIEnv associated with the current thread.
+ /// @param app_context The Android application context. This must be the
+ /// application context, NOT an Activity context (Note: from any Android
+ /// Activity in your app, you can call getApplicationContext() to
+ /// retrieve the application context).
+ /// @param class_loader The class loader to use when loading Java classes.
+ /// This must be your app's main class loader (usually accessible through
+ /// activity.getClassLoader() on any of your Activities).
+ /// @return unique_ptr to the created GvrApi instance, nullptr on failure.
+ static std::unique_ptr<GvrApi> Create(JNIEnv* env, jobject app_context,
+ jobject class_loader) {
+ gvr_context* context = gvr_create(env, app_context, class_loader);
+ if (!context) {
+ return nullptr;
+ }
+ return std::unique_ptr<GvrApi>(new GvrApi(context, true /* owned */));
+ }
+#else
+ /// Instantiates and returns a GvrApi instance that owns a gvr_context.
+ ///
+ /// @return unique_ptr to the created GvrApi instance, nullptr on failure.
+ static std::unique_ptr<GvrApi> Create() {
+ gvr_context* context = gvr_create();
+ if (!context) {
+ return nullptr;
+ }
+ return std::unique_ptr<GvrApi>(new GvrApi(context, true /* owned */));
+ }
+#endif // #ifdef __ANDROID__
+
+ ~GvrApi() {
+ if (context_ && owned_) {
+ gvr_destroy(&context_);
+ }
+ }
+
+ /// @name Error handling
+ /// @{
+
+ /// For more information, see gvr_get_error().
+ Error GetError() { return static_cast<Error>(gvr_get_error(context_)); }
+
+ /// For more information, see gvr_clear_error().
+ Error ClearError() { return static_cast<Error>(gvr_clear_error(context_)); }
+
+ /// For more information, see gvr_get_error_string().
+ static const char* GetErrorString(Error error_code) {
+ return gvr_get_error_string(error_code);
+ }
+
+ /// For more information, see gvr_get_user_prefs().
+ UserPrefs GetUserPrefs() { return UserPrefs(gvr_get_user_prefs(context_)); }
+
+ /// @}
+
+ /// @name Rendering
+ /// @{
+
+ /// For more information, see gvr_initialize_gl().
+ void InitializeGl() { gvr_initialize_gl(context_); }
+
+ /// For more information, see gvr_get_async_reprojection_enabled().
+ bool GetAsyncReprojectionEnabled() const {
+ return gvr_get_async_reprojection_enabled(context_);
+ }
+
+ /// Constructs a C++ wrapper for a gvr_buffer_viewport object. For more
+ /// information, see gvr_buffer_viewport_create().
+ ///
+ /// @return A new BufferViewport instance with memory allocated for an
+ /// underlying gvr_buffer_viewport.
+ BufferViewport CreateBufferViewport() const {
+ return BufferViewport(context_);
+ }
+
+ /// Constructs a C++ wrapper for a gvr_buffer_viewport_list object.
+ /// For more information, see gvr_buffer_viewport_list_create().
+ ///
+ /// @return A new, empty BufferViewportList instance.
+ /// Note: The validity of the returned object is closely tied to the
+ /// lifetime of the member gvr_context. The caller is responsible for
+ /// ensuring correct usage accordingly.
+ BufferViewportList CreateEmptyBufferViewportList() const {
+ return BufferViewportList(context_);
+ }
+
+ /// For more information, see gvr_get_maximum_effective_render_target_size().
+ Sizei GetMaximumEffectiveRenderTargetSize() const {
+ return gvr_get_maximum_effective_render_target_size(context_);
+ }
+
+ /// For more information, see gvr_get_screen_target_size().
+ Sizei GetScreenTargetSize() const {
+ return gvr_get_screen_target_size(context_);
+ }
+
+ /// For more information, see gvr_set_surface_size().
+ void SetSurfaceSize(Sizei surface_size_pixels) {
+ gvr_set_surface_size(context_, surface_size_pixels);
+ }
+
+ /// For more information, see gvr_distort_to_screen().
+ void DistortToScreen(int32_t texture_id,
+ const BufferViewportList& viewport_list,
+ const Mat4f& rendered_head_pose_in_start_space_matrix,
+ const ClockTimePoint& texture_presentation_time) {
+ gvr_distort_to_screen(context_, texture_id, viewport_list.viewport_list_,
+ rendered_head_pose_in_start_space_matrix,
+ texture_presentation_time);
+ }
+
+ /// For more information, see gvr_buffer_spec_create().
+ BufferSpec CreateBufferSpec() {
+ return BufferSpec(context_);
+ }
+
+ /// For more information, see gvr_swap_chain_create().
+ SwapChain CreateSwapChain(const std::vector<BufferSpec>& specs) {
+ return SwapChain(context_, specs);
+ }
+
+ /// For more information, see gvr_bind_default_framebuffer().
+ void BindDefaultFramebuffer() {
+ gvr_bind_default_framebuffer(context_);
+ }
+ /// @}
+
+ /// @name Head tracking
+ /// @{
+
+ /// For more information see gvr_get_head_space_from_start_space_rotation.
+ ///
+ /// @param time_point The time at which to calculate the head pose in start
+ /// space.
+ /// @return The matrix representation of the rotation from start space
+ /// (the space with the head pose at the last tracking reset at origin) to
+ /// head space (the space with the head at origin and axes aligned to the
+ /// view vector).
+ Mat4f GetHeadSpaceFromStartSpaceRotation(const ClockTimePoint& time_point) {
+ return gvr_get_head_space_from_start_space_rotation(context_, time_point);
+ }
+
+ /// For more information, see gvr_apply_neck_model().
+ Mat4f ApplyNeckModel(const Mat4f& head_pose_in_start_space, float factor) {
+ return gvr_apply_neck_model(context_, head_pose_in_start_space, factor);
+ }
+
+ /// For more information, see gvr_pause_tracking().
+ void PauseTracking() { gvr_pause_tracking(context_); }
+
+ /// For more information, see gvr_resume_tracking().
+ void ResumeTracking() { gvr_resume_tracking(context_); }
+
+ /// For more information, see gvr_reset_tracking().
+ void ResetTracking() { gvr_reset_tracking(context_); }
+
+ // For more information, see gvr_recenter_tracking().
+ void RecenterTracking() { gvr_recenter_tracking(context_); }
+
+ /// For more information, see gvr_get_time_point_now().
+ static ClockTimePoint GetTimePointNow() { return gvr_get_time_point_now(); }
+ /// @}
+
+ /// @name Viewer parameters
+ /// @{
+
+ /// For more information, see gvr_set_default_viewer_profile().
+ bool SetDefaultViewerProfile(const char* viewer_profile_uri) {
+ return gvr_set_default_viewer_profile(context_, viewer_profile_uri);
+ }
+
+ /// For more information, see gvr_refresh_viewer_profile().
+ void RefreshViewerProfile() { gvr_refresh_viewer_profile(context_); }
+
+ /// For more information, see gvr_get_viewer_vendor().
+ const char* GetViewerVendor() const {
+ return gvr_get_viewer_vendor(context_);
+ }
+
+ /// For more information, see gvr_get_viewer_model().
+ const char* GetViewerModel() const { return gvr_get_viewer_model(context_); }
+
+ /// For more information, see gvr_get_viewer_type().
+ ViewerType GetViewerType() const {
+ return static_cast<ViewerType>(gvr_get_viewer_type(context_));
+ }
+
+ /// For more information, see gvr_get_eye_from_head_matrix().
+ Mat4f GetEyeFromHeadMatrix(Eye eye) const {
+ return gvr_get_eye_from_head_matrix(context_, eye);
+ }
+
+ /// For more information, see gvr_get_window_bounds().
+ Recti GetWindowBounds() const { return gvr_get_window_bounds(context_); }
+
+ /// For more information, see gvr_compute_distorted_point().
+ std::array<Vec2f, 3> ComputeDistortedPoint(Eye eye, const Vec2f& uv_in) {
+ std::array<Vec2f, 3> uv_out = {{{}}};
+ gvr_compute_distorted_point(context_, eye, uv_in, uv_out.data());
+ return uv_out;
+ }
+ /// @}
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and optionally takes ownership.
+ ///
+ /// @param context C object to wrap.
+ /// @param owned Whether the wrapper will own the underlying C object.
+ explicit GvrApi(gvr_context* context, bool owned = true)
+ : context_(context), owned_(owned) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_context* cobj() { return context_; }
+ const gvr_context* cobj() const { return context_; }
+
+ /// @deprecated Use cobj() instead.
+ gvr_context* GetContext() { return context_; }
+ /// @deprecated Use cobj() instead.
+ const gvr_context* GetContext() const { return context_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_context* release() {
+ auto result = context_;
+ context_ = nullptr;
+ return result;
+ }
+
+ /// Instantiates a GvrApi instance that wraps a *non-owned* gvr_context.
+ ///
+ /// Ownership of the provided `context` remains with the caller, and they
+ /// are responsible for ensuring proper disposal of the context.
+ ///
+ /// @param context Pointer to a non-null, non-owned gvr_context instance.
+ /// @return unique_ptr to the created GvrApi instance. Never null.
+ static std::unique_ptr<GvrApi> WrapNonOwned(gvr_context* context) {
+ return std::unique_ptr<GvrApi>(new GvrApi(context, false /* owned */));
+ }
+ /// @}
+
+ private:
+ gvr_context* context_;
+
+ // Whether context_ is owned by the GvrApi instance. If owned, the context
+ // will be released upon destruction.
+ const bool owned_;
+
+ // Disallow copy and assign.
+ GvrApi(const GvrApi&);
+ void operator=(const GvrApi&);
+};
+
+} // namespace gvr
+#endif // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif // VR_GVR_CAPI_INCLUDE_GVR_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
new file mode 100644
index 0000000..eee3d0c
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
@@ -0,0 +1,854 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_AUDIO_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_AUDIO_H_
+
+#if __ANDROID__
+#include <jni.h>
+#endif // __ANDROID__
+
+#include <stdint.h>
+
+#include "vr/gvr/capi/include/gvr.h"
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+/// @defgroup Audio Spatial Audio API
+/// @brief This is the GVR Audio C API, a spatial audio rendering engine,
+/// optimized for mobile VR.
+///
+/// It allows the user to spatialize sound sources in 3D space, including
+/// distance and elevation cues. Specifically, the API is capable of playing
+/// back spatial sound in three ways:
+///
+/// - **Sound object rendering**: This allows the user to create a virtual sound
+/// source in 3D space. These sources, while spatialized, are fed with mono
+/// audio data.
+///
+/// - **Ambisonic soundfields**: Ambisonic recordings are multi-channel audio
+/// files which are spatialized all around the listener in 360 degrees. These
+/// can be thought of as recorded or pre-baked soundfields. They can be of
+/// great use for background effects which sound perfectly spatial. Examples
+/// include rain noise, crowd noise or even the sound of the ocean off to one
+/// side.
+///
+/// - **Stereo Sounds**: This allows the user to directly play back
+/// non-spatialized mono or stereo audio files. This is useful for music and
+/// other such audio.
+///
+/// **Initialization**
+///
+/// gvr_audio_context* gvr_audio_create(int32_t rendering_mode);
+///
+/// The rendering_mode argument corresponds to a `gvr_audio_rendering_mode` enum
+/// value, which specifies a rendering configuration setting:
+///
+/// - `GVR_AUDIO_RENDERING_STEREO_PANNING`:
+/// Stereo panning of all sound objects. This disables HRTF-based rendering.
+/// - `GVR_AUDIO_RENDERING_BINAURAL_LOW_QUALITY`:
+/// This renders sound objects over a virtual array of 8 loudspeakers arranged
+/// in a cube configuration around the listener’s head. HRTF-based rendering
+/// is enabled.
+/// - `GVR_AUDIO_RENDERING_BINAURAL_HIGH_QUALITY`:
+/// This renders sound objects over a virtual array of 16 loudspeakers
+/// arranged in an approximate equidistribution about the listener’s
+/// head. HRTF-based rendering is enabled.
+///
+/// For most modern phones, the high quality mode offers a good balance between
+/// performance and audio quality. To optimize the rendering performance for
+/// headphones *and* speaker playback, the stereo speaker mode can be enabled
+/// which automatically switches to stereo panning when no headphone is plugin.
+/// Note that this can lead to varying CPU usage based on headphone and speaker
+/// playback.
+///
+/// **Sound engine control**
+///
+/// Audio playback on the default audio device can be started and stopped by
+/// calling the following two methods:
+///
+/// void gvr_audio_pause(gvr_audio_context* api);
+/// void gvr_audio_resume(gvr_audio_context* api);
+///
+/// Note that:
+///
+/// void gvr_audio_update(gvr_audio_context* api);
+///
+/// must be called from the main thread at a regular rate. It is used to execute
+/// background operations outside of the audio thread.
+///
+/// **Listener position and rotation**
+///
+/// To ensure that the audio in your application reacts to listener head
+/// movement, it is important to update the listener's head orientation in the
+/// graphics callback using the head orientation matrix.
+///
+/// The following methods can be used to control the listener’s head position
+/// and orientation with the audio engine:
+///
+/// void gvr_audio_set_head_position(gvr_audio_context* api, float x,
+/// float y, float z);
+/// or
+///
+/// void gvr_audio_set_head_position_gvr(gvr_audio_context* api,
+/// const gvr_vec3f& position);
+///
+/// and
+///
+/// void gvr_audio_set_head_rotation(gvr_audio_context* api,
+/// float x, float y, float z, float w);
+/// or
+///
+/// void gvr_audio_set_head_rotation_gvr(gvr_audio_context* api,
+/// const gvr_quatf& rotation);
+///
+/// **Preloading Sounds**
+///
+/// Both mono sound files for use with Sound Objects and multi-channel Ambisonic
+/// soundfield files can be preloaded into memory before playback or
+/// alternatively streamed during playback. Preloading can be useful to reduce
+/// CPU usage especially if the same audio clip is likely to be played back many
+/// times. In this case playback latency is also reduced.
+///
+/// Sound files can be preloaded into memory by calling:
+///
+/// bool gvr_audio_preload_soundfile(gvr_audio_context* api,
+/// const char* filename);
+///
+/// Unused sound files can be unloaded with a call to:
+///
+/// void gvr_audio_unload_soundfile(gvr_audio_context* api,
+/// const char* filename);
+///
+/// NOTE: If a sound object, soundfield or stereo sound is created with a file
+/// that has not been preloaded, that audio will be streamed.
+///
+/// **Spatializtion of sound objects**
+///
+/// The GVR Audio System allows the user to create virtual sound objects which
+/// can be placed anywhere in space around the listener.
+///
+/// To create a new sound object, call:
+///
+/// gvr_audio_source_id
+/// gvr_audio_create_sound_object(gvr_audio_context* api,
+/// const char* filename);
+///
+/// This returns a handle that can be used to set properties such as the
+/// position and the volume of the sound object via calls to the following two
+/// functions:
+///
+/// void
+/// gvr_audio_set_sound_object_position(gvr_audio_context* api,
+/// gvr_audio_source_id sound_object_id,
+/// float x, float y, float z);
+///
+/// void
+/// gvr_audio_set_sound_volume(gvr_audio_context* api,
+/// gvr_audio_source_id source_id, float volume);
+///
+/// The behavior of Sound Objects with respect to their distance from the
+/// listener can be controlled via calls to the following method:
+///
+/// void gvr_audio_set_sound_object_distance_rolloff_model(
+/// gvr_audio_context* api, gvr_audio_source_id sound_object_id,
+/// int32_t rolloff_model, float min_distance, float max_distance);
+///
+/// This enables a user to choose between logarithmic and linear distance
+/// rolloff methods, or to completely disable distance rolloff effects.
+///
+///
+/// The spatialized playback of a sound object can be triggered with a call to:
+///
+/// void gvr_audio_play_sound(gvr_audio_context* api,
+/// gvr_audio_source_id source_id,
+/// bool looping_enabled);
+///
+/// and stopped with a call to:
+///
+/// void gvr_audio_stop_sound(gvr_audio_context* api,
+/// gvr_audio_source_id source_id);
+///
+/// Note that the sound object handle destroys itself at the moment the sound
+/// playback has stopped. This way, no clean up of sound object handles is
+/// needed. On subsequent calls to this function the corresponding
+/// gvr_audio_source_id no longer refers to a valid sound object.
+///
+/// The following function can be used to check if a sound object is currently
+/// active:
+///
+/// bool gvr_audio_is_sound_playing(const gvr_audio_context* api,
+/// gvr_audio_source_id source_id);
+///
+/// **Rendering of ambisonic soundfields**
+///
+/// The GVR Audio System also provides the user with the ability to play back
+/// ambisonic soundfields. Ambisonic soundfields are captured or pre-rendered
+/// 360 degree recordings. It is best to think of them as equivalent to 360
+/// degree video. While they envelop and surround the listener, they only react
+/// to the listener's rotational movement. That is, one cannot walk towards
+/// features in the soundfield. Soundfields are ideal for accompanying 360
+/// degree video playback, for introducing background and environmental effects
+/// such as rain or crowd noise, or even for pre baking 3D audio to reduce
+/// rendering costs. The GVR Audio System supports full 3D First Order
+/// Ambisonic recordings using ACN channel ordering and SN3D normalization. For
+/// more information please see our Spatial Audio specification at:
+/// https://github.com/google/spatial-media/blob/master/docs/spatial-audio-rfc.md#semantics
+///
+/// Note that Soundfield playback is directly streamed from the sound file and
+/// no sound file preloading is needed.
+///
+/// To obtain a soundfield handler, call:
+///
+/// gvr_audio_source_id gvr_audio_create_soundfield(gvr_audio_context* api,
+/// const char* filename);
+///
+/// This returns a gvr_audio_source_id handle that allows the user to begin
+/// playback of the soundfield, to alter the soundfield’s volume or to stop
+/// soundfield playback and as such destroy the object. These actions can be
+/// achieved with calls to the following functions:
+///
+/// void gvr_audio_play_sound(gvr_audio_context* api,
+/// gvr_audio_source_id source_id,
+/// bool looping_enabled);
+///
+/// void gvr_audio_set_sound_volume(gvr_audio_context* api,
+/// gvr_audio_source_id source_id,
+/// float volume);
+///
+/// void gvr_audio_stop_sound(gvr_audio_context* api,
+/// gvr_audio_source_id source_id);
+///
+/// Ambisonic soundfields can also be rotated about the listener's head in order
+/// to align the components of the soundfield with the visuals of the game/app.
+///
+/// void gvr_audio_set_soundfield_rotation(gvr_audio_context* api,
+/// gvr_audio_source_id soundfield_id,
+/// const gvr_quatf&
+/// soundfield_rotation);
+///
+/// **Direct Playback of Stereo or Mono Sounds**
+///
+/// The GVR Audio System allows the direct non-spatialized playback of both
+/// stereo and mono audio. Such audio is often used for music or sound effects
+/// that should not be spatialized.
+///
+/// A stereo sound can be created with a call to:
+///
+/// gvr_audio_source_id gvr_audio_create_stereo_sound(gvr_audio_context* api,
+/// const char* filename);
+///
+/// **Paused Sounds and Stopped Sounds**
+///
+/// When using sound sources of any of the above types, the user can ensure that
+/// the given source is currently playing before calling.
+///
+/// bool gvr_audio_is_sound_playing(gvr_audio_source_id source_id);
+///
+/// This method will return false if the source has been either paused or
+/// stopped, and true if the source is currently playing.
+///
+/// Once one is finished with a Sound Object and wish to remove it, a call can
+/// be placed to:
+///
+/// void gvr_audio_stop_sound(gvr_audio_source_id source_id);
+///
+/// Once a source has been stopped it is destroyed and the corresponding
+/// gvr_audio_source_id will be invalid. Sources which have been played with the
+/// |looping_enabled| parameter disabled will also be destroyed once playback
+/// of the full audio clip has completed.
+///
+/// To check whether a given gvr_audio_source_id corresponds to a valid source
+/// which exists and is in a playable state, a call can be made to:
+///
+/// bool gvr_audio_is_source_id_valid(gvr_audio_source_id source_id);
+///
+/// By using this pair of methods a user can differentiate between sources which
+/// have been paused and those which have ceased.
+///
+/// **Room effects**
+///
+/// The GVR Audio System provides a powerful reverb engine which can be used to
+/// create customized room effects by specifying the size of a room and a
+/// material for each surface of the room from the gvr_audio_material_name enum.
+/// Each of these surface materials has unique absorption properties which
+/// differ with frequency. The room created will be centered around the
+/// listener. Note that the Google VR Audio System uses meters as the unit of
+/// distance throughout.
+///
+/// The following methods are used to control room effects:
+///
+/// void gvr_audio_enable_room(gvr_audio_context* api, bool enable);
+///
+/// enables or disables room effects with smooth transitions.
+///
+/// and
+///
+/// void
+/// gvr_audio_set_room_properties(gvr_audio_context* api, float size_x,
+/// float size_y, float size_z,
+/// gvr_audio_material_name wall_material,
+/// gvr_audio_material_name ceiling_material,
+/// gvr_audio_material_name floor_material);
+///
+/// allows the user to describe the room based on its dimensions and its surface
+/// properties. For example, one can expect very large rooms to be more
+/// reverberant than smaller rooms, and a room with with hard surface materials
+/// such as brick to be more reverberant than one with soft absorbent materials
+/// such as heavy curtains on every surface.
+///
+/// Note that when a sound source is located outside of the listener's room,
+/// it will sound different from sources located within the room due to
+/// attenuation of both the direct sound and the reverb on that source. Sources
+/// located far outside of the listener's room will not be audible to the
+/// listener.
+///
+/// The following method can be used to subtly adjust the reverb in a room by
+/// changing the gain/attenuation on the reverb, setting a multiplier on the
+/// reverberation time to control the reverb's length, or adjusting the balance
+/// between the low and high frequency components of the reverb.
+///
+/// void gvr_audio_set_room_reverb_adjustments(gvr_audio_context* api,
+/// float gain,
+/// float time_adjust,
+/// float brightness_adjust);
+///
+/// If you are writing C++ code, you might prefer to use the C++ wrapper
+/// rather than implement this C API directly.
+///
+/// **Example usage (C++ API)**
+///
+/// Construction:
+///
+/// std::unique_ptr<gvr::AudioApi> gvr_audio_api(new gvr::AudioApi);
+/// gvr_audio_api->Init(GVR_AUDIO_RENDERING_BINAURAL_HIGH_QUALITY);
+///
+/// Update head rotation in DrawFrame():
+///
+/// head_pose_ = gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
+/// gvr_audio_api_->SetHeadPose(head_pose_);
+/// gvr_audio_api_->Update();
+///
+/// Preload sound file, create sound handle and start playback:
+///
+/// gvr_audio_api->PreloadSoundfile(kSoundFile);
+/// AudioSourceId source_id =
+/// gvr_audio_api_->CreateSoundObject("sound.wav");
+/// gvr_audio_api->SetSoundObjectPosition(source_id,
+/// position_x,
+/// position_y,
+/// position_z);
+/// gvr_audio_api->PlaySound(source_id, true /* looped playback */);
+///
+
+/// @{
+
+typedef struct gvr_audio_context_ gvr_audio_context;
+
+/// Creates and initializes a gvr_audio_context. This call also initializes
+/// the audio interface and starts the audio engine. Note that the returned
+/// instance must be deleted with gvr_audio_destroy.
+///
+#ifdef __ANDROID__
+/// @param env The JNI Env associated with the current thread.
+/// @param android_context The Android application context. This must be the
+/// application context, NOT an Activity context (Note: from any Android
+/// Activity in your app, you can call getApplicationContext() to
+/// retrieve the application context).
+/// @param class_loader The class loader to use when loading Java
+/// classes. This must be your app's main class loader (usually
+/// accessible through activity.getClassLoader() on any of your Activities).
+/// @param rendering_mode The gvr_audio_rendering_mode value which determines
+/// the rendering configuration preset. This is passed as an int32_t to
+/// ensure API compatibility.
+/// @return gvr_audio_context instance.
+gvr_audio_context* gvr_audio_create(JNIEnv* env, jobject android_context,
+ jobject class_loader,
+ int32_t rendering_mode);
+#else
+/// @param rendering_mode The gvr_audio_rendering_mode value which determines
+/// the rendering configuration preset. This is passed as an int32_t to
+/// ensure API compatibility.
+/// @return gvr_audio_context instance.
+gvr_audio_context* gvr_audio_create(int32_t rendering_mode);
+#endif // #ifdef __ANDROID__
+
+/// Destroys a gvr_audio_context that was previously created with
+/// gvr_audio_create or gvr_audio_create_android.
+///
+/// @param api Pointer to a pointer to a gvr_audio_context. The pointer
+/// will be set to NULL after destruction.
+void gvr_audio_destroy(gvr_audio_context** api);
+
+/// Resumes the VR Audio system.
+/// Call this when your app/game loses focus.
+/// Calling this when not paused is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a gvr_audio_context.
+void gvr_audio_resume(gvr_audio_context* api);
+
+/// Pauses the VR Audio system.
+/// Calling this when already paused is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a gvr_audio_context.
+void gvr_audio_pause(gvr_audio_context* api);
+
+/// This method must be called from the main thread at a regular rate. It is
+/// used to execute background operations outside of the audio thread.
+///
+/// @param api Pointer to a gvr_audio_context.
+void gvr_audio_update(gvr_audio_context* api);
+
+/// Preloads a local sound file. Note that the local file access method
+/// depends on the target platform.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename Name of the file, used as identifier.
+/// @return True on success or if file has already been preloaded.
+bool gvr_audio_preload_soundfile(gvr_audio_context* api, const char* filename);
+
+/// Unloads a previously preloaded sample from memory. Note that if the sample
+/// is currently used, the memory is freed at the moment playback stops.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename Name of the file, used as identifier.
+void gvr_audio_unload_soundfile(gvr_audio_context* api, const char* filename);
+
+/// Returns a new sound object. Note that the sample should only contain a
+/// single audio channel (stereo sources are automatically downmixed to mono).
+/// The handle automatically destroys itself at the moment the sound playback
+/// has stopped.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename The path/name of the file to be played.
+/// @return Id of new sound object. Returns kInvalidId if the sound file has not
+/// been preloaded or if the number of input channels is > 1.
+gvr_audio_source_id gvr_audio_create_sound_object(gvr_audio_context* api,
+ const char* filename);
+
+/// Returns a new ambisonic sound field. Note that the sample needs to be
+/// preloaded and must have 4 separate audio channels. The handle automatically
+/// destroys itself at the moment the sound playback has stopped.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename The path/name of the file to be played.
+/// @return Id of new soundfield. Returns kInvalidId if the sound file has not
+/// been preloaded or if the number of input channels does not match that
+/// required.
+gvr_audio_source_id gvr_audio_create_soundfield(gvr_audio_context* api,
+ const char* filename);
+
+/// Returns a new stereo non-spatialized source, which directly plays back mono
+/// or stereo audio. Note the sample needs to be preloaded and may contain only
+/// one (mono) or two (stereo) audio channels.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename The path/name of the file to be played..
+/// @return Id of new stereo non-spatialized source. Returns kInvalidId if the
+/// sound file has not been preloaded or if the number of input channels is
+/// > 2;
+gvr_audio_source_id gvr_audio_create_stereo_sound(gvr_audio_context* api,
+ const char* filename);
+
+/// Starts the playback of a sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be stopped.
+/// @param looping_enabled Enables looped audio playback.
+void gvr_audio_play_sound(gvr_audio_context* api, gvr_audio_source_id source_id,
+ bool looping_enabled);
+
+/// Pauses the playback of a sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be paused.
+void gvr_audio_pause_sound(gvr_audio_context* api,
+ gvr_audio_source_id source_id);
+
+/// Resumes the playback of a sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be resumed.
+void gvr_audio_resume_sound(gvr_audio_context* api,
+ gvr_audio_source_id source_id);
+
+/// Stops the playback of a sound and destroys the corresponding sound object
+/// or Soundfield.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be stopped.
+void gvr_audio_stop_sound(gvr_audio_context* api,
+ gvr_audio_source_id source_id);
+
+/// Checks if a sound is playing.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be checked.
+/// @return True if the sound is being played.
+bool gvr_audio_is_sound_playing(const gvr_audio_context* api,
+ gvr_audio_source_id source_id);
+
+/// Checks if a |source_id| is valid, and that the corresponding source is in a
+/// playable state. Sources that have been stopped will be reported as invalid.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be checked.
+/// @return True if the source exists and is in a playable state.
+bool gvr_audio_is_source_id_valid(const gvr_audio_context* api,
+ gvr_audio_source_id source_id);
+
+/// Repositions an existing sound object.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param sound_object_id Id of the sound object to be moved.
+/// @param x X coordinate the sound will be placed at.
+/// @param y Y coordinate the sound will be placed at.
+/// @param z Z coordinate the sound will be placed at.
+void gvr_audio_set_sound_object_position(gvr_audio_context* api,
+ gvr_audio_source_id sound_object_id,
+ float x, float y, float z);
+
+/// Sets the given ambisonic soundfields's rotation.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param soundfield_id Id of the soundfield source to be rotated.
+/// @param soundfield_rotation Quaternion representing the soundfield rotation.
+void gvr_audio_set_soundfield_rotation(gvr_audio_context* api,
+ gvr_audio_source_id soundfield_id,
+ const gvr_quatf& soundfield_rotation);
+
+/// Sets the given sound object source's distance attenuation method with
+/// minimum and maximum distances. Maximum distance must be greater than the
+/// minimum distance for the method to be set.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param sound_object_id Id of sound object source.
+/// @param rolloff_model Linear or logarithmic distance rolloff models. Note
+/// setting the rolloff model to |GVR_AUDIO_ROLLOFF_NONE| will allow
+/// distance attenuation values to be set manually.
+/// @param min_distance Minimum distance to apply distance attenuation method.
+/// @param max_distance Maximum distance to apply distance attenuation method.
+void gvr_audio_set_sound_object_distance_rolloff_model(
+ gvr_audio_context* api, gvr_audio_source_id sound_object_id,
+ int32_t rolloff_model, float min_distance, float max_distance);
+
+
+/// Changes the master volume.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param volume Volume value. Should range from 0 (mute) to 1 (max).
+void gvr_audio_set_master_volume(gvr_audio_context* api, float volume);
+
+/// Changes the volume of an existing sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be modified.
+/// @param volume Volume value. Should range from 0 (mute) to 1 (max).
+void gvr_audio_set_sound_volume(gvr_audio_context* api,
+ gvr_audio_source_id source_id, float volume);
+
+/// Sets the head pose from a matrix representation of the same.
+///
+/// @param api Pointer to a gvr_audio_context on which to set the pose.
+/// @param head_pose_matrix Matrix representing the head transform to be set.
+void gvr_audio_set_head_pose(gvr_audio_context* api,
+ const gvr_mat4f& head_pose_matrix);
+
+/// Turns on/off the room reverberation effect.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param enable True to enable room effect.
+void gvr_audio_enable_room(gvr_audio_context* api, bool enable);
+
+/// Sets the room properties describing the dimensions and surface materials of
+/// a given room.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param size_x Dimension along X axis.
+/// @param size_y Dimension along Y axis.
+/// @param size_z Dimension along Z axis.
+/// @param wall_material Surface gvr_audio_material_type for the four walls.
+/// @param ceiling_material Surface gvr_audio_material_type for the ceiling.
+/// @param floor_material Surface gvr_audio_material_type for the floor.
+void gvr_audio_set_room_properties(gvr_audio_context* api, float size_x,
+ float size_y, float size_z,
+ int32_t wall_material,
+ int32_t ceiling_material,
+ int32_t floor_material);
+
+/// Adjusts the properties of the current reverb, allowing changes to the
+/// reverb's gain, duration and low/high frequency balance.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param gain Reverb volume (linear) adjustment in range [0, 1] for
+/// attenuation, range [1, inf) for gain boost.
+/// @param time_adjust Reverb time adjustment multiplier to scale the
+/// reverberation tail length. This value should be >= 0.
+/// @param brightness_adjust Reverb brightness adjustment that controls the
+/// reverberation ratio across low and high frequency bands.
+void gvr_audio_set_room_reverb_adjustments(gvr_audio_context* api, float gain,
+ float time_adjust,
+ float brightness_adjust);
+
+/// Enables the stereo speaker mode. It enforces stereo-panning when headphones
+/// are *not* plugged into the phone. This helps to avoid HRTF-based coloring
+/// effects and reduces computational complexity when speaker playback is
+/// active. By default the stereo speaker mode optimization is disabled.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param enable True to enable the stereo speaker mode.
+void gvr_audio_enable_stereo_speaker_mode(gvr_audio_context* api, bool enable);
+
+/// @}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+// Convenience C++ wrapper.
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#include <memory>
+#include <string>
+
+namespace gvr {
+/// This is a convenience C++ wrapper for the Audio C API.
+///
+/// This wrapper strategy prevents ABI compatibility issues between compilers
+/// by ensuring that the interface between client code and the implementation
+/// code in libgvr.so is a pure C interface. The translation from C++ calls
+/// to C calls provided by this wrapper runs entirely in the client's binary
+/// and is compiled by the client's compiler.
+///
+/// Methods in this class are only documented insofar as the C++ wrapping logic
+/// is concerned; for information about the method itself, please refer to the
+/// corresponding function in the C API.
+///
+///
+/// THREADING: this class is thread-safe and reentrant after initialized
+/// with Init().
+class AudioApi {
+ public:
+ /// Creates an (uninitialized) ControllerApi object. You must initialize
+ /// it by calling Init() before interacting with it.
+ AudioApi() : context_(nullptr) {}
+
+ ~AudioApi() {
+ if (context_) {
+ gvr_audio_destroy(&context_);
+ }
+ }
+
+/// Creates and initializes a gvr_audio_context.
+/// For more information, see gvr_audio_create().
+#ifdef __ANDROID__
+ bool Init(JNIEnv* env, jobject android_context, jobject class_loader,
+ AudioRenderingMode rendering_mode) {
+ context_ =
+ gvr_audio_create(env, android_context, class_loader, rendering_mode);
+ return context_ != nullptr;
+ }
+#else
+ bool Init(AudioRenderingMode rendering_mode) {
+ context_ = gvr_audio_create(rendering_mode);
+ return context_ != nullptr;
+ }
+#endif // #ifdef __ANDROID__
+
+ /// Pauses the audio engine.
+ /// For more information, see gvr_audio_pause().
+ void Pause() { gvr_audio_pause(context_); }
+
+ /// Resumes the audio engine.
+ /// For more information, see gvr_audio_resume().
+ void Resume() { gvr_audio_resume(context_); }
+
+ /// For more information, see gvr_audio_update().
+ void Update() { gvr_audio_update(context_); }
+
+ /// Preloads a local sound file.
+ /// For more information, see gvr_audio_preload_soundfile().
+ bool PreloadSoundfile(const std::string& filename) {
+ return gvr_audio_preload_soundfile(context_, filename.c_str());
+ }
+
+ /// Unloads a previously preloaded sample from memory.
+ /// For more information, see gvr_audio_preload_soundfile().
+ void UnloadSoundfile(const std::string& filename) {
+ gvr_audio_unload_soundfile(context_, filename.c_str());
+ }
+
+ /// Returns a new sound object.
+ /// For more information, see gvr_audio_create_sound_object().
+ AudioSourceId CreateSoundObject(const std::string& filename) {
+ return gvr_audio_create_sound_object(context_, filename.c_str());
+ }
+
+ /// Returns a new sound field.
+ /// For more information, see gvr_audio_create_soundfield().
+ AudioSourceId CreateSoundfield(const std::string& filename) {
+ return gvr_audio_create_soundfield(context_, filename.c_str());
+ }
+
+ /// Returns a new stereo soound.
+ /// For more information, see gvr_audio_create_stereo_sound().
+ AudioSourceId CreateStereoSound(const std::string& filename) {
+ return gvr_audio_create_stereo_sound(context_, filename.c_str());
+ }
+
+ /// Starts the playback of a sound.
+ /// For more information, see gvr_audio_play_sound().
+ void PlaySound(AudioSourceId source_id, bool looping_enabled) {
+ gvr_audio_play_sound(context_, source_id, looping_enabled);
+ }
+
+ /// Pauses the playback of a sound.
+ /// For more information, see gvr_audio_pause_sound().
+ void PauseSound(AudioSourceId source_id) {
+ gvr_audio_pause_sound(context_, source_id);
+ }
+
+ /// Resumes the playback of a sound.
+ /// For more information, see gvr_audio_resume_sound().
+ void ResumeSound(AudioSourceId source_id) {
+ gvr_audio_resume_sound(context_, source_id);
+ }
+
+ /// Stops the playback of a sound.
+ /// For more information, see gvr_audio_stop_sound().
+ void StopSound(AudioSourceId source_id) {
+ gvr_audio_stop_sound(context_, source_id);
+ }
+
+ /// Checks if a sound is playing.
+ /// For more information, see gvr_audio_is_sound_playing().
+ bool IsSoundPlaying(AudioSourceId source_id) const {
+ return gvr_audio_is_sound_playing(context_, source_id);
+ }
+
+ /// Checks if a source is in a valid playable state.
+ /// For more information, see gvr_audio_is_source_id_valid().
+ bool IsSourceIdValid(AudioSourceId source_id) {
+ return gvr_audio_is_source_id_valid(context_, source_id);
+ }
+
+ /// Repositions an existing sound object.
+ /// For more information, see gvr_audio_set_sound_object_position().
+ void SetSoundObjectPosition(AudioSourceId sound_object_id, float x, float y,
+ float z) {
+ gvr_audio_set_sound_object_position(context_, sound_object_id, x, y, z);
+ }
+
+ void SetSoundObjectDistanceRolloffModel(
+ AudioSourceId sound_object_id,
+ gvr_audio_distance_rolloff_type rolloff_model, float min_distance,
+ float max_distance) {
+ gvr_audio_set_sound_object_distance_rolloff_model(
+ context_, sound_object_id, rolloff_model, min_distance, max_distance);
+ }
+
+ /// Rotates an existing soundfield.
+ /// For more information, see gvr_audio_set_soundfield_rotation().
+ void SetSoundfieldRotation(AudioSourceId soundfield_id,
+ const Quatf& soundfield_rotation) {
+ gvr_audio_set_soundfield_rotation(context_, soundfield_id,
+ soundfield_rotation);
+ }
+
+ /// Changes the master volume.
+ /// For more information, see gvr_audio_set_master_volume().
+ void SetMasterVolume(float volume) {
+ gvr_audio_set_master_volume(context_, volume);
+ }
+
+ /// Changes the volume of an existing sound.
+ /// For more information, see gvr_audio_set_sound_volume().
+ void SetSoundVolume(AudioSourceId source_id, float volume) {
+ gvr_audio_set_sound_volume(context_, source_id, volume);
+ }
+
+ /// Sets the head position from a matrix representation.
+ /// For more information, see gvr_audio_set_head_pose().
+ void SetHeadPose(const Mat4f& head_pose_matrix) {
+ gvr_audio_set_head_pose(context_, head_pose_matrix);
+ }
+
+ /// Turns on/off the room reverberation effect.
+ /// For more information, see gvr_audio_enable_room().
+ void EnableRoom(bool enable) { gvr_audio_enable_room(context_, enable); }
+
+ /// Sets the room properties describing the dimensions and surface materials
+ /// of a given room. For more information, see
+ /// gvr_audio_set_room_properties().
+ void SetRoomProperties(float size_x, float size_y, float size_z,
+ gvr_audio_material_type wall_material,
+ gvr_audio_material_type ceiling_material,
+ gvr_audio_material_type floor_material) {
+ gvr_audio_set_room_properties(context_, size_x, size_y, size_z,
+ wall_material, ceiling_material,
+ floor_material);
+ }
+
+ /// Adjusts the properties of the current reverb, allowing changes to the
+ /// reverb's gain, duration and low/high frequency balance. For more
+ /// information see gvr_audio_set_room_reverb_adjustments().
+ void SetRoomReverbAdjustments(float gain, float time_adjust,
+ float brightness_adjust) {
+ gvr_audio_set_room_reverb_adjustments(context_, gain, time_adjust,
+ brightness_adjust);
+ }
+
+ /// Enables the stereo speaker mode. For more information see
+ /// gvr_audio_enable_stereo_speaker_mode().
+ void EnableStereoSpeakerMode(bool enable) {
+ gvr_audio_enable_stereo_speaker_mode(context_, enable);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit AudioApi(gvr_audio_context* context)
+ : context_(context) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_audio_context* cobj() { return context_; }
+ const gvr_audio_context* cobj() const { return context_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_audio_context* Release() {
+ auto result = context_;
+ context_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ gvr_audio_context* context_;
+
+ // Disallow copy and assign:
+ AudioApi(const AudioApi&);
+ void operator=(const AudioApi&);
+};
+
+} // namespace gvr
+#endif // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif // VR_GVR_CAPI_INCLUDE_GVR_AUDIO_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
new file mode 100644
index 0000000..a67502f
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
@@ -0,0 +1,752 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_CONTROLLER_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_CONTROLLER_H_
+
+#ifdef __ANDROID__
+#include <jni.h>
+#endif
+
+#include <stdint.h>
+
+#include "vr/gvr/capi/include/gvr.h"
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @defgroup Controller Controller API
+/// @brief This is the Controller C API, which allows access to a VR controller.
+///
+/// If you are writing C++ code, you might prefer to use the C++ wrapper rather
+/// than implement this C API directly.
+///
+/// Typical initialization example:
+///
+/// // Get your gvr_context* pointer from GvrLayout:
+/// gvr_context* gvr = ......; // (get from GvrLayout in Java)
+///
+/// // Set up the API features:
+/// int32_t options = gvr_controller_get_default_options();
+///
+/// // Enable non-default options, if needed:
+/// options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL;
+///
+/// // Create and init:
+/// gvr_controller_context* context =
+/// gvr_controller_create_and_init(options, gvr);
+///
+/// // Check if init was successful.
+/// if (!context) {
+/// // Handle error.
+/// return;
+/// }
+///
+/// gvr_controller_state* state = gvr_controller_state_create();
+///
+/// // Resume:
+/// gvr_controller_resume(api);
+///
+/// Usage:
+///
+/// void DrawFrame() {
+/// gvr_controller_state_update(context, 0, state);
+/// // ... process controller state ...
+/// }
+///
+/// // When your application gets paused:
+/// void OnPause() {
+/// gvr_controller_pause(context);
+/// }
+///
+/// // When your application gets resumed:
+/// void OnResume() {
+/// gvr_controller_resume(context);
+/// }
+///
+/// To conserve battery, be sure to call gvr_controller_pause and
+/// gvr_controller_resume when your app gets paused and resumed, respectively.
+///
+/// THREADING: unless otherwise noted, all functions are thread-safe, so
+/// you can operate on the same gvr_controller_context object from multiple
+/// threads.
+/// @{
+
+/// Represents a Daydream Controller API object, used to invoke the
+/// Daydream Controller API.
+typedef struct gvr_controller_context_ gvr_controller_context;
+
+/// Returns the default features for the controller API.
+///
+/// @return The set of default features, as bit flags (an OR'ed combination of
+/// the GVR_CONTROLLER_ENABLE_* feature flags).
+int32_t gvr_controller_get_default_options();
+
+/// Creates and initializes a gvr_controller_context instance which can be used
+/// to invoke the Daydream Controller API functions. Important: after creation
+/// the API will be in the paused state (the controller will be inactive).
+/// You must call gvr_controller_resume() explicitly (typically, in your Android
+/// app's onResume() callback).
+///
+/// @param options The API options. To get the defaults, use
+/// gvr_controller_get_default_options().
+/// @param context The GVR Context object to sync with (optional).
+/// This can be nullptr. If provided, the context's state will
+/// be synchronized with the controller's state where possible. For
+/// example, when the user recenters the controller, this will
+/// automatically recenter head tracking as well.
+/// WARNING: the caller is responsible for making sure the pointer
+/// remains valid for the lifetime of this object.
+/// @return A pointer to the initialized API, or NULL if an error occurs.
+gvr_controller_context* gvr_controller_create_and_init(
+ int32_t options, gvr_context* context);
+
+#ifdef __ANDROID__
+/// Creates and initializes a gvr_controller_context instance with an explicit
+/// Android context and class loader.
+///
+/// @param env The JNI Env associated with the current thread.
+/// @param android_context The Android application context. This must be the
+/// application context, NOT an Activity context (Note: from any Android
+/// Activity in your app, you can call getApplicationContext() to
+/// retrieve the application context).
+/// @param class_loader The class loader to use when loading Java
+/// classes. This must be your app's main class loader (usually
+/// accessible through activity.getClassLoader() on any of your Activities).
+/// @param options The API options. To get the defaults, use
+/// gvr_controller_get_default_options().
+/// @param context The GVR Context object to sync with (optional).
+/// This can be nullptr. If provided, the context's state will
+/// be synchronized with the controller's state where possible. For
+/// example, when the user recenters the controller, this will
+/// automatically recenter head tracking as well.
+/// WARNING: the caller is responsible for making sure the pointer
+/// remains valid for the lifetime of this object.
+/// @return A pointer to the initialized API, or NULL if an error occurs.
+gvr_controller_context* gvr_controller_create_and_init_android(
+ JNIEnv *env, jobject android_context, jobject class_loader,
+ int32_t options, gvr_context* context);
+#endif // #ifdef __ANDROID__
+
+/// Destroys a gvr_controller_context that was previously created with
+/// gvr_controller_init.
+///
+/// @param api Pointer to a pointer to a gvr_controller_context. The pointer
+/// will be set to NULL after destruction.
+void gvr_controller_destroy(gvr_controller_context** api);
+
+/// Pauses the controller, possibly releasing resources.
+/// Call this when your app/game loses focus.
+/// Calling this when already paused is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a pointer to a gvr_controller_context.
+void gvr_controller_pause(gvr_controller_context* api);
+
+/// Resumes the controller. Call this when your app/game regains focus.
+/// Calling this when already resumed is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a pointer to a gvr_controller_context.
+void gvr_controller_resume(gvr_controller_context* api);
+
+/// Convenience to convert an API status code to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param status The gvr_controller_api_status to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_api_status_to_string(int32_t status);
+
+/// Convenience to convert an connection state to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param state The state to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_connection_state_to_string(int32_t state);
+
+/// Convenience to convert an connection state to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param button The gvr_controller_button to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_button_to_string(int32_t button);
+
+/// Creates a gvr_controller_state.
+gvr_controller_state* gvr_controller_state_create();
+
+/// Destroys a a gvr_controller_state that was previously created with
+/// gvr_controller_state_create.
+void gvr_controller_state_destroy(gvr_controller_state** state);
+
+/// Updates the controller state. Reading the controller state is not a
+/// const getter: it has side-effects. In particular, some of the
+/// gvr_controller_state fields (the ones documented as "transient") represent
+/// one-time events and will be true for only one read operation, and false
+/// in subsequente reads.
+///
+/// @param api Pointer to a pointer to a gvr_controller_context.
+/// @param flags Optional flags reserved for future use. A value of 0 should be
+/// used until corresponding flag attributes are defined and documented.
+/// @param out_state A pointer where the controller's state
+/// is to be written. This must have been allocated with
+/// gvr_controller_state_create().
+void gvr_controller_state_update(gvr_controller_context* api, int32_t flags,
+ gvr_controller_state* out_state);
+
+/// Gets the API status of the controller state. Returns one of the
+/// gvr_controller_api_status variants, but returned as an int32_t for ABI
+/// compatibility.
+int32_t gvr_controller_state_get_api_status(const gvr_controller_state* state);
+
+/// Gets the connection state of the controller. Returns one of the
+/// gvr_controller_connection_state variants, but returned as an int32_t for ABI
+/// compatibility.
+int32_t gvr_controller_state_get_connection_state(
+ const gvr_controller_state* state);
+
+/// Returns the current controller orientation, in Start Space. The Start Space
+/// is the same space as the headset space and has these three axes
+/// (right-handed):
+///
+/// * The positive X axis points to the right.
+/// * The positive Y axis points upwards.
+/// * The positive Z axis points backwards.
+///
+/// The definition of "backwards" and "to the right" are based on the position
+/// of the controller when tracking started. For Daydream, this is when the
+/// controller was first connected in the "Connect your Controller" screen
+/// which is shown when the user enters VR.
+///
+/// The definition of "upwards" is given by gravity (away from the pull of
+/// gravity). This API may not work in environments without gravity, such
+/// as space stations or near the center of the Earth.
+///
+/// Since the coordinate system is right-handed, rotations are given by the
+/// right-hand rule. For example, rotating the controller counter-clockwise
+/// on a table top as seen from above means a positive rotation about the
+/// Y axis, while clockwise would mean negative.
+///
+/// Note that this is the Start Space for the *controller*, which initially
+/// coincides with the Start Space for the headset, but they may diverge over
+/// time due to controller/headset drift. A recentering operation will bring
+/// the two spaces back into sync.
+///
+/// Remember that a quaternion expresses a rotation. Given a rotation of theta
+/// radians about the (x, y, z) axis, the corresponding quaternion (in
+/// xyzw order) is:
+///
+/// (x * sin(theta/2), y * sin(theta/2), z * sin(theta/2), cos(theta/2))
+///
+/// Here are some examples of orientations of the controller and their
+/// corresponding quaternions, all given in xyzw order:
+///
+/// * Initial pose, pointing forward and lying flat on a surface: identity
+/// quaternion (0, 0, 0, 1). Corresponds to "no rotation".
+///
+/// * Flat on table, rotated 90 degrees counter-clockwise: (0, 0.7071, 0,
+/// 0.7071). Corresponds to a +90 degree rotation about the Y axis.
+///
+/// * Flat on table, rotated 90 degrees clockwise: (0, -0.7071, 0, 0.7071).
+/// Corresponds to a -90 degree rotation about the Y axis.
+///
+/// * Flat on table, rotated 180 degrees (pointing backwards): (0, 1, 0, 0).
+/// Corresponds to a 180 degree rotation about the Y axis.
+///
+/// * Pointing straight up towards the sky: (0.7071, 0, 0, 0.7071).
+/// Corresponds to a +90 degree rotation about the X axis.
+///
+/// * Pointing straight down towards the ground: (-0.7071, 0, 0, 0.7071).
+/// Corresponds to a -90 degree rotation about the X axis.
+///
+/// * Banked 90 degrees to the left: (0, 0, 0.7071, 0.7071). Corresponds
+/// to a +90 degree rotation about the Z axis.
+///
+/// * Banked 90 degrees to the right: (0, 0, -0.7071, 0.7071). Corresponds
+/// to a -90 degree rotation about the Z axis.
+gvr_quatf gvr_controller_state_get_orientation(
+ const gvr_controller_state* state);
+
+/// Returns the current controller gyro reading, in Start Space.
+///
+/// The gyro measures the controller's angular speed in radians per second.
+/// Note that this is an angular *speed*, so it reflects how fast the
+/// controller's orientation is changing with time.
+/// In particular, if the controller is not being rotated, the angular speed
+/// will be zero on all axes, regardless of the current pose.
+///
+/// The axes are in the controller's device space. Specifically:
+///
+/// * The X axis points to the right of the controller.
+/// * The Y axis points upwards perpendicular to the top surface of the
+/// controller.
+/// * The Z axis points backwards along the body of the controller,
+/// towards its rear, where the charging port is.
+///
+/// As usual in a right-handed coordinate system, the sign of the angular
+/// velocity is given by the right-hand rule. So, for example:
+///
+/// * If the controller is flat on a table top spinning counter-clockwise
+/// as seen from above, you will read a positive angular velocity
+/// about the Y axis. Clockwise would be negative.
+/// * If the controller is initially pointing forward and lying flat and
+/// is then gradually angled up so that its tip points towards the sky,
+/// it will report a positive angular velocity about the X axis during
+/// that motion. Likewise, angling it down will report a negative angular
+/// velocity about the X axis.
+/// * If the controller is banked (rolled) to the right, this will
+/// report a negative angular velocity about the Z axis during the
+/// motion (remember the Z axis points backwards along the controller).
+/// Banking to the left will report a positive angular velocity about
+/// the Z axis.
+gvr_vec3f gvr_controller_state_get_gyro(const gvr_controller_state* state);
+
+/// Current (latest) controller accelerometer reading, in Start Space.
+///
+/// The accelerometer indicates the direction in which the controller feels
+/// an acceleration, including gravity. The reading is given in meters
+/// per second squared (m/s^2). The axes are the same as for the gyro.
+/// To have an intuition for the signs used in the accelerometer, it is useful
+/// to imagine that, when at rest, the controller is being "pushed" by a
+/// force opposite to gravity. It is as if, by the equivalency princle, it were
+/// on a frame of reference that is accelerating in the opposite direction to
+/// gravity. For example:
+///
+/// * If the controller is lying flat on a table top, it will read a positive
+/// acceleration of about 9.8 m/s^2 along the Y axis, corresponding to
+/// the acceleration of gravity (as if the table were pushing the controller
+/// upwards at 9.8 m/s^2 to counteract gravity).
+/// * If, in that situation, the controller is now accelerated upwards at
+/// 3.0 m/s^2, then the reading will be 12.8 m/s^2 along the Y axis,
+/// since the controller will now feel a stronger acceleration corresponding
+/// to the 9.8 m/s^2 plus the upwards push of 3.0 m/s^2.
+/// * If, the controller is accelerated downwards at 5.0 m/s^2, then the
+/// reading will now be 4.8 m/s^2 along the Y axis, since the controller
+/// will now feel a weaker acceleration (as the acceleration is giving in
+/// to gravity).
+/// * If you were to give in to gravity completely, letting the controller
+/// free fall towards the ground, it will read 0 on all axes, as there
+/// will be no force acting on the controller. (Please do not put your
+/// controller in a free-fall situation. This is just a theoretical
+/// example.)
+gvr_vec3f gvr_controller_state_get_accel(const gvr_controller_state* state);
+
+/// Returns whether the user is touching the touchpad.
+bool gvr_controller_state_is_touching(const gvr_controller_state* state);
+
+/// If the user is touching the touchpad, this returns the touch position in
+/// normalized coordinates, where (0,0) is the top-left of the touchpad
+/// and (1,1) is the bottom right. If the user is not touching the touchpad,
+/// then this is the position of the last touch.
+gvr_vec2f gvr_controller_state_get_touch_pos(const gvr_controller_state* state);
+
+/// Returns true if user just started touching touchpad (this is a transient
+/// event:
+/// it is true for only one frame after the event).
+bool gvr_controller_state_get_touch_down(const gvr_controller_state* state);
+
+/// Returns true if user just stopped touching touchpad (this is a transient
+/// event:
+/// it is true for only one frame after the event).
+bool gvr_controller_state_get_touch_up(const gvr_controller_state* state);
+
+/// Returns true if a recenter operation just ended (this is a transient event:
+/// it is true only for one frame after the recenter ended). If this is
+/// true then the `orientation` field is already relative to the new center.
+bool gvr_controller_state_get_recentered(const gvr_controller_state* state);
+
+/// Returns whether the recenter flow is currently in progress.
+///
+/// @deprecated Use gvr_controller_state_get_recentered instead.
+bool gvr_controller_state_get_recentering(const gvr_controller_state* state);
+
+/// Returns whether the given button is currently pressed.
+bool gvr_controller_state_get_button_state(const gvr_controller_state* state,
+
+ int32_t button);
+
+/// Returns whether the given button was just pressed (transient).
+bool gvr_controller_state_get_button_down(const gvr_controller_state* state,
+ int32_t button);
+
+/// Returns whether the given button was just released (transient).
+bool gvr_controller_state_get_button_up(const gvr_controller_state* state,
+ int32_t button);
+
+/// Returns the timestamp (nanos) when the last orientation event was received.
+int64_t gvr_controller_state_get_last_orientation_timestamp(
+ const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last gyro event was received.
+int64_t gvr_controller_state_get_last_gyro_timestamp(
+ const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last accelerometer event was
+/// received.
+int64_t gvr_controller_state_get_last_accel_timestamp(
+ const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last touch event was received.
+int64_t gvr_controller_state_get_last_touch_timestamp(
+ const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last button event was received.
+int64_t gvr_controller_state_get_last_button_timestamp(
+ const gvr_controller_state* state);
+
+// Current (latest) controller simulated position for use with an elbow model.
+gvr_vec3f gvr_controller_state_get_position(const gvr_controller_state* state);
+
+// Returns the timestamp (nanos) when the last position event was received.
+int64_t gvr_controller_state_get_last_position_timestamp(
+ const gvr_controller_state* state);
+
+/// @}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+// Convenience C++ wrapper.
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#include <memory>
+
+namespace gvr {
+/// This is a convenience C++ wrapper for the Controller C API.
+///
+/// This wrapper strategy prevents ABI compatibility issues between compilers
+/// by ensuring that the interface between client code and the implementation
+/// code in libgvr.so is a pure C interface. The translation from C++ calls
+/// to C calls provided by this wrapper runs entirely in the client's binary
+/// and is compiled by the client's compiler.
+///
+/// Methods in this class are only documented insofar as the C++ wrapping logic
+/// is concerned; for information about the method itself, please refer to the
+/// corresponding function in the C API.
+///
+/// Typical C++ initialization example:
+///
+/// std::unique_ptr<ControllerApi> controller_api(new ControllerApi);
+///
+/// // Your GVR context pointer (which can be obtained from GvrLayout)
+/// gvr_context* context = .....; // (get it from GvrLayout)
+///
+/// // Set up the options:
+/// int32_t options = ControllerApi::DefaultOptions();
+///
+/// // Enable non-default options, if you need them:
+/// options |= GVR_CONTROLLER_ENABLE_GYRO;
+///
+/// // Init the ControllerApi object:
+/// bool success = controller_api->Init(options, context);
+/// if (!success) {
+/// // Handle failure.
+/// // Do NOT call other methods (like Resume, etc) if init failed.
+/// return;
+/// }
+///
+/// // Resume the ControllerApi (if your app is on the foreground).
+/// controller_api->Resume();
+///
+/// ControllerState state;
+///
+/// Usage example:
+///
+/// void DrawFrame() {
+/// state.Update(*controller_api);
+/// // ... process controller state ...
+/// }
+///
+/// // When your application gets paused:
+/// void OnPause() {
+/// controller_api->Pause();
+/// }
+///
+/// // When your application gets resumed:
+/// void OnResume() {
+/// controller_api->Resume();
+/// }
+///
+/// To conserve battery, be sure to call Pause() and Resume() when your app
+/// gets paused and resumed, respectively. This will allow the underlying
+/// logic to unbind from the VR service and let the controller sleep when
+/// no apps are using it.
+///
+/// THREADING: this class is thread-safe and reentrant after initialized
+/// with Init().
+class ControllerApi {
+ public:
+ /// Creates an (uninitialized) ControllerApi object. You must initialize
+ /// it by calling Init() before interacting with it.
+ ControllerApi() : context_(nullptr) {}
+
+ /// Returns the default controller options.
+ static int32_t DefaultOptions() {
+ return gvr_controller_get_default_options();
+ }
+
+ // Deprecated factory-style create method.
+ // TODO(btco): remove this once no one is using it.
+ static std::unique_ptr<ControllerApi> Create() {
+ return std::unique_ptr<ControllerApi>(new ControllerApi);
+ }
+
+ /// Initializes the controller API.
+ ///
+ /// This method must be called exactly once in the lifetime of this object.
+ /// Other methods in this class may only be called after Init() returns true.
+ /// Note: this does not cause the ControllerApi to be resumed. You must call
+ /// Resume() explicitly in order to start using the controller.
+ ///
+ /// For more information see gvr_controller_create_and_init().
+ ///
+ /// @return True if initialization was successful, false if it failed.
+ /// Initialization may fail, for example, because invalid options were
+ /// supplied.
+ bool Init(int32_t options, gvr_context* context) {
+ context_ = gvr_controller_create_and_init(options, context);
+ return context_ != nullptr;
+ }
+
+#ifdef __ANDROID__
+ /// Overload of Init() with explicit Android context and class loader
+ /// (for Android only). For more information, see:
+ /// gvr_controller_create_and_init_android().
+ bool Init(JNIEnv *env, jobject android_context, jobject class_loader,
+ int32_t options, gvr_context* context) {
+ context_ = gvr_controller_create_and_init_android(
+ env, android_context, class_loader, options, context);
+ return context_ != nullptr;
+ }
+#endif // #ifdef __ANDROID__
+
+ /// Convenience overload that calls Init without a gvr_context.
+ // TODO(btco): remove once it is no longer being used.
+ bool Init(int32_t options) {
+ return Init(options, nullptr);
+ }
+
+ /// Pauses the controller.
+ /// For more information, see gvr_controller_pause().
+ void Pause() {
+ gvr_controller_pause(context_);
+ }
+
+ /// Resumes the controller.
+ /// For more information, see gvr_controller_resume().
+ void Resume() {
+ gvr_controller_resume(context_);
+ }
+
+ /// Destroys this ControllerApi instance.
+ ~ControllerApi() {
+ if (context_) gvr_controller_destroy(&context_);
+ }
+
+ /// Convenience functions to convert enums to strings.
+ /// For more information, see the corresponding functions in the C API.
+ static const char* ToString(ControllerApiStatus status) {
+ return gvr_controller_api_status_to_string(status);
+ }
+
+ static const char* ToString(ControllerConnectionState state) {
+ return gvr_controller_connection_state_to_string(state);
+ }
+
+ static const char* ToString(ControllerButton button) {
+ return gvr_controller_button_to_string(button);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit ControllerApi(gvr_controller_context* context)
+ : context_(context) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_controller_context* cobj() { return context_; }
+ const gvr_controller_context* cobj() const { return context_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_controller_context* release() {
+ auto result = context_;
+ context_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ protected:
+ gvr_controller_context* context_;
+
+ private:
+ friend class ControllerState;
+
+ // Disallow copy and assign:
+ ControllerApi(const ControllerApi&);
+ void operator=(const ControllerApi&);
+};
+
+/// Convenience C++ wrapper for the opaque gvr_controller_state type. See the
+/// gvr_controller_state functions for more information.
+class ControllerState {
+ public:
+ ControllerState() : state_(gvr_controller_state_create()) {}
+
+ ~ControllerState() {
+ if (state_) gvr_controller_state_destroy(&state_);
+ }
+
+ /// For more information, see gvr_controller_state_update().
+ void Update(const ControllerApi& api) {
+ gvr_controller_state_update(api.context_, 0, state_);
+ }
+
+ /// For more information, see gvr_controller_state_update().
+ void Update(const ControllerApi& api, int32_t flags) {
+ gvr_controller_state_update(api.context_, flags, state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_api_status().
+ ControllerApiStatus GetApiStatus() const {
+ return static_cast<ControllerApiStatus>(
+ gvr_controller_state_get_api_status(state_));
+ }
+
+ /// For more information, see gvr_controller_state_get_connection_state().
+ ControllerConnectionState GetConnectionState() const {
+ return static_cast<ControllerConnectionState>(
+ gvr_controller_state_get_connection_state(state_));
+ }
+
+ /// For more information, see gvr_controller_state_get_orientation().
+ gvr_quatf GetOrientation() const {
+ return gvr_controller_state_get_orientation(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_gyro().
+ gvr_vec3f GetGyro() const { return gvr_controller_state_get_gyro(state_); }
+
+ /// For more information, see gvr_controller_state_get_accel().
+ gvr_vec3f GetAccel() const { return gvr_controller_state_get_accel(state_); }
+
+ /// For more information, see gvr_controller_state_is_touching().
+ bool IsTouching() const { return gvr_controller_state_is_touching(state_); }
+
+ /// For more information, see gvr_controller_state_get_touch_pos().
+ gvr_vec2f GetTouchPos() const {
+ return gvr_controller_state_get_touch_pos(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_touch_down().
+ bool GetTouchDown() const {
+ return gvr_controller_state_get_touch_down(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_touch_up().
+ bool GetTouchUp() const { return gvr_controller_state_get_touch_up(state_); }
+
+ /// For more information, see gvr_controller_state_get_recentered().
+ bool GetRecentered() const {
+ return gvr_controller_state_get_recentered(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_recentering().
+ bool GetRecentering() const {
+ return gvr_controller_state_get_recentering(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_button_state().
+ bool GetButtonState(ControllerButton button) const {
+ return gvr_controller_state_get_button_state(state_, button);
+ }
+
+ /// For more information, see gvr_controller_state_get_button_down().
+ bool GetButtonDown(ControllerButton button) const {
+ return gvr_controller_state_get_button_down(state_, button);
+ }
+
+ /// For more information, see gvr_controller_state_get_button_up().
+ bool GetButtonUp(ControllerButton button) const {
+ return gvr_controller_state_get_button_up(state_, button);
+ }
+
+ /// For more information, see
+ /// gvr_controller_state_get_last_orientation_timestamp().
+ int64_t GetLastOrientationTimestamp() const {
+ return gvr_controller_state_get_last_orientation_timestamp(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_last_gyro_timestamp().
+ int64_t GetLastGyroTimestamp() const {
+ return gvr_controller_state_get_last_gyro_timestamp(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_last_accel_timestamp().
+ int64_t GetLastAccelTimestamp() const {
+ return gvr_controller_state_get_last_accel_timestamp(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_last_touch_timestamp().
+ int64_t GetLastTouchTimestamp() const {
+ return gvr_controller_state_get_last_touch_timestamp(state_);
+ }
+
+ /// For more information, see
+ /// gvr_controller_state_get_last_button_timestamp().
+ int64_t GetLastButtonTimestamp() const {
+ return gvr_controller_state_get_last_button_timestamp(state_);
+ }
+
+ /// For more information, see gvr_controller_state_get_position().
+ gvr_vec3f GetPosition() const {
+ return gvr_controller_state_get_position(state_);
+ }
+
+ /// For more information, see
+ /// gvr_controller_state_get_last_position_timestamp().
+ int64_t GetLastPositionTimestamp() const {
+ return gvr_controller_state_get_last_position_timestamp(state_);
+ }
+
+ /// @name Wrapper manipulation
+ /// @{
+ /// Creates a C++ wrapper for a C object and takes ownership.
+ explicit ControllerState(gvr_controller_state* state) : state_(state) {}
+
+ /// Returns the wrapped C object. Does not affect ownership.
+ gvr_controller_state* cobj() { return state_; }
+ const gvr_controller_state* cobj() const { return state_; }
+
+ /// Returns the wrapped C object and transfers its ownership to the caller.
+ /// The wrapper becomes invalid and should not be used.
+ gvr_controller_state* release() {
+ auto result = state_;
+ state_ = nullptr;
+ return result;
+ }
+ /// @}
+
+ private:
+ gvr_controller_state* state_;
+};
+
+} // namespace gvr
+#endif // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif // VR_GVR_CAPI_INCLUDE_GVR_CONTROLLER_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
new file mode 100644
index 0000000..ff00863
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
@@ -0,0 +1,575 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_TYPES_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_TYPES_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @defgroup types Google VR Types
+/// @brief Various types used in the Google VR NDK.
+/// @{
+
+/// Primary context for invoking Google VR APIs.
+typedef struct gvr_context_ gvr_context;
+
+/// An enum for the left and right eye.
+typedef enum {
+ GVR_LEFT_EYE = 0,
+ GVR_RIGHT_EYE,
+ GVR_NUM_EYES
+} gvr_eye;
+
+/// The type of VR viewer.
+typedef enum {
+ /// A Cardboard-compatible viewer. A typical Cardboard viewer supports a
+ /// simple touchscreen-based trigger input mechanism. On most platforms, this
+ // is the default viewer type if no viewer has been explicitly paired.
+ GVR_VIEWER_TYPE_CARDBOARD = 0,
+ /// A Daydream-compatible viewer. A typical Daydream viewer supports 3DOF
+ /// controller input (as defined in gvr_controller.h), and is intended only
+ /// for Daydream-ready platforms. It does *not* support touchscreen-based
+ /// input unless Cardboard emulation is explicitly enabled.
+ GVR_VIEWER_TYPE_DAYDREAM = 1,
+} gvr_viewer_type;
+
+/// @}
+
+/// Version information for the Google VR API.
+typedef struct gvr_version_ {
+ int32_t major;
+ int32_t minor;
+ int32_t patch;
+} gvr_version;
+
+/// An integral 2D size. Used for render target sizes.
+typedef struct gvr_sizei {
+ int32_t width;
+ int32_t height;
+} gvr_sizei;
+
+/// An integral 2D rect. Used for window bounds in pixels.
+typedef struct gvr_recti {
+ int32_t left;
+ int32_t right;
+ int32_t bottom;
+ int32_t top;
+} gvr_recti;
+
+/// A floating point 2D rect. Used for field of view, and also for ranges
+/// in texture space. When used for a field of view, all angles are in positive
+/// degrees from the optical axis.
+typedef struct gvr_rectf {
+ float left;
+ float right;
+ float bottom;
+ float top;
+} gvr_rectf;
+
+/// A floating point 2D vector.
+typedef struct gvr_vec2f {
+ float x;
+ float y;
+} gvr_vec2f;
+
+/// A floating point 3D vector.
+typedef struct gvr_vec3f {
+ float x;
+ float y;
+ float z;
+} gvr_vec3f;
+
+/// A floating point 4x4 matrix.
+typedef struct gvr_mat4f { float m[4][4]; } gvr_mat4f;
+
+/// A floating point quaternion, in JPL format.
+/// We use this simple struct in order not to impose a dependency on a
+/// particular math library. The user of this API is free to encapsulate this
+/// into any math library they want.
+typedef struct gvr_quatf {
+ /// qx, qy, qz are the vector component.
+ float qx;
+ float qy;
+ float qz;
+ /// qw is the linelar component.
+ float qw;
+} gvr_quatf;
+
+/// A *monotonic system time* representation. On Android, this is equivalent to
+/// System.nanoTime(), or clock_gettime(CLOCK_MONOTONIC). If there is any doubt
+/// about how to get the current time for the current platform, simply use
+/// gvr_get_time_point_now().
+typedef struct gvr_clock_time_point {
+ int64_t monotonic_system_time_nanos;
+} gvr_clock_time_point;
+
+/// A structure that ties together a region of a buffer, the field of view
+/// rendered into that region and a target eye index to define part of the
+/// user's field of view. The SDK implementation uses this information to
+/// transform the images generated by the app output into the final display that
+/// is sent to the screen.
+///
+/// A set of these structures will most often be generated by the API, via
+/// gvr_get_recommended_buffer_viewports() or
+/// gvr_get_screen_buffer_viewports(). However, the client may also customize
+/// these values via gvr_buffer_viewport_list_set(), constructing a custom
+/// gvr_buffer_viewport_list for use in the distortion pass.
+typedef struct gvr_buffer_viewport_ gvr_buffer_viewport;
+
+/// List of buffer viewports that completely specifies how to transform the
+/// frame's buffers into the image displayed on the screen.
+typedef struct gvr_buffer_viewport_list_ gvr_buffer_viewport_list;
+
+/// Specification of a pixel buffer. A pixel buffer can have color, depth and
+/// stencil attachments and mostly corresponds to the OpenGL concept of a
+/// framebuffer object. However, since there can be multiple such objects for
+/// each frame, we avoid calling them "framebuffers". Pixel buffers which are
+/// part of the currently acquired frame are immutable, i.e., they cannot be
+/// resized or otherwise reconfigured.
+typedef struct gvr_buffer_spec_ gvr_buffer_spec;
+
+/// Swap chain that contains some number of frames. Frames in the swap chain
+/// can be unused, in the process of being distorted and presented on the
+/// screen, or acquired and being rendered to by the application. The swap chain
+/// ensures that the most recent available frame is always shown and that the
+/// application never has to wait to render the next frame.
+typedef struct gvr_swap_chain_ gvr_swap_chain;
+
+/// A single frame acquired from the swap chain. Each frame is composed of one
+/// or more buffers, which are then lens distorted and composited into the final
+/// output. Buffers are identified by indices that correspond to the position
+/// of their gvr_buffer_spec in the list passed when constructing the swap
+/// chain.
+typedef struct gvr_frame_ gvr_frame;
+
+/// @addtogroup types
+/// @{
+
+/// Constants that represent GVR error codes.
+typedef enum {
+ GVR_ERROR_NONE = 0,
+ GVR_ERROR_CONTROLLER_CREATE_FAILED = 2,
+ GVR_ERROR_NO_FRAME_AVAILABLE = 3,
+} gvr_error;
+
+/// Controller API options (bit flags).
+enum {
+ /// Indicates that controller orientation data should be reported.
+ GVR_CONTROLLER_ENABLE_ORIENTATION = 1 << 0,
+ /// Indicates that controller touchpad data should be reported.
+ GVR_CONTROLLER_ENABLE_TOUCH = 1 << 1,
+ /// Indicates that controller gyroscope data should be reported.
+ GVR_CONTROLLER_ENABLE_GYRO = 1 << 2,
+ /// Indicates that controller accelerometer data should be reported.
+ GVR_CONTROLLER_ENABLE_ACCEL = 1 << 3,
+ /// Indicates that controller gestures should be reported.
+ GVR_CONTROLLER_ENABLE_GESTURES = 1 << 4,
+ /// Indicates that controller pose prediction should be enabled.
+ GVR_CONTROLLER_ENABLE_POSE_PREDICTION = 1 << 5,
+ /// Indicates that controller position data should be reported.
+ GVR_CONTROLLER_ENABLE_POSITION = 1 << 6,
+};
+
+/// Constants that represent the status of the controller API.
+typedef enum {
+ /// API is happy and healthy. This doesn't mean the controller itself
+ /// is connected, it just means that the underlying service is working
+ /// properly.
+ GVR_CONTROLLER_API_OK = 0,
+
+ /// Any other status represents a permanent failure that requires
+ /// external action to fix:
+
+ /// API failed because this device does not support controllers (API is too
+ /// low, or other required feature not present).
+ GVR_CONTROLLER_API_UNSUPPORTED = 1,
+ /// This app was not authorized to use the service (e.g., missing permissions,
+ /// the app is blacklisted by the underlying service, etc).
+ GVR_CONTROLLER_API_NOT_AUTHORIZED = 2,
+ /// The underlying VR service is not present.
+ GVR_CONTROLLER_API_UNAVAILABLE = 3,
+ /// The underlying VR service is too old, needs upgrade.
+ GVR_CONTROLLER_API_SERVICE_OBSOLETE = 4,
+ /// The underlying VR service is too new, is incompatible with current client.
+ GVR_CONTROLLER_API_CLIENT_OBSOLETE = 5,
+ /// The underlying VR service is malfunctioning. Try again later.
+ GVR_CONTROLLER_API_MALFUNCTION = 6,
+} gvr_controller_api_status;
+
+/// Constants that represent the state of the controller.
+typedef enum {
+ /// Controller is disconnected.
+ GVR_CONTROLLER_DISCONNECTED = 0,
+ /// Controller is scanning.
+ GVR_CONTROLLER_SCANNING = 1,
+ /// Controller is connecting.
+ GVR_CONTROLLER_CONNECTING = 2,
+ /// Controller is connected.
+ GVR_CONTROLLER_CONNECTED = 3,
+} gvr_controller_connection_state;
+
+/// Controller buttons.
+typedef enum {
+ GVR_CONTROLLER_BUTTON_NONE = 0,
+ GVR_CONTROLLER_BUTTON_CLICK = 1, ///< Touchpad Click.
+ GVR_CONTROLLER_BUTTON_HOME = 2,
+ GVR_CONTROLLER_BUTTON_APP = 3,
+ GVR_CONTROLLER_BUTTON_VOLUME_UP = 4,
+ GVR_CONTROLLER_BUTTON_VOLUME_DOWN = 5,
+
+ /// Note: there are 5 buttons on the controller, but the state arrays have
+ /// this many elements due to the inclusion of a dummy "none" button.
+ GVR_CONTROLLER_BUTTON_COUNT = 6,
+} gvr_controller_button;
+
+/// @}
+
+/// Opaque handle to controller state.
+typedef struct gvr_controller_state_ gvr_controller_state;
+
+/// @addtogroup types
+/// @{
+
+/// Rendering modes define CPU load / rendering quality balances.
+typedef enum {
+ /// Stereo panning of all Sound Objects. This disables HRTF-based rendering.
+ GVR_AUDIO_RENDERING_STEREO_PANNING = 0,
+ /// HRTF-based rendering over a virtual array of 8 loudspeakers arranged in
+ /// a cube configuration around the listener’s head.
+ GVR_AUDIO_RENDERING_BINAURAL_LOW_QUALITY = 1,
+ /// HRTF-based rendering over a virtual array of 16 loudspeakers arranged in
+ /// an approximate equidistribution about the around the listener’s head.
+ GVR_AUDIO_RENDERING_BINAURAL_HIGH_QUALITY = 2,
+} gvr_audio_rendering_mode;
+
+/// Room surface material names, used to set room properties.
+typedef enum {
+ /// Acoustically transparent material, reflects no sound.
+ GVR_AUDIO_MATERIAL_TRANSPARENT = 0,
+ /// Acoustic ceiling tiles, absorbs most frequencies.
+ GVR_AUDIO_MATERIAL_ACOUSTIC_CEILING_TILES = 1,
+ /// Bare brick, relatively reflective.
+ GVR_AUDIO_MATERIAL_BRICK_BARE = 2,
+ /// Painted brick
+ GVR_AUDIO_MATERIAL_BRICK_PAINTED = 3,
+ /// Coarse surface concrete block.
+ GVR_AUDIO_MATERIAL_CONCRETE_BLOCK_COARSE = 4,
+ /// Painted concrete block.
+ GVR_AUDIO_MATERIAL_CONCRETE_BLOCK_PAINTED = 5,
+ /// Heavy curtains.
+ GVR_AUDIO_MATERIAL_CURTAIN_HEAVY = 6,
+ /// Fiber glass insulation.
+ GVR_AUDIO_MATERIAL_FIBER_GLASS_INSULATION = 7,
+ /// Thin glass.
+ GVR_AUDIO_MATERIAL_GLASS_THIN = 8,
+ /// Thick glass.
+ GVR_AUDIO_MATERIAL_GLASS_THICK = 9,
+ /// Grass.
+ GVR_AUDIO_MATERIAL_GRASS = 10,
+ /// Linoleum on concrete.
+ GVR_AUDIO_MATERIAL_LINOLEUM_ON_CONCRETE = 11,
+ /// Marble.
+ GVR_AUDIO_MATERIAL_MARBLE = 12,
+ /// Galvanized sheet metal.
+ GVR_AUDIO_MATERIAL_METAL = 13,
+ /// Wooden parquet on concrete.
+ GVR_AUDIO_MATERIAL_PARQUET_ON_CONCRETE = 14,
+ /// Rough plaster surface.
+ GVR_AUDIO_MATERIAL_PLASTER_ROUGH = 15,
+ /// Smooth plaster surface.
+ GVR_AUDIO_MATERIAL_PLASTER_SMOOTH = 16,
+ /// Plywood panel.
+ GVR_AUDIO_MATERIAL_PLYWOOD_PANEL = 17,
+ /// Polished concrete OR tile surface.
+ GVR_AUDIO_MATERIAL_POLISHED_CONCRETE_OR_TILE = 18,
+ /// Sheet rock.
+ GVR_AUDIO_MATERIAL_SHEET_ROCK = 19,
+ /// Surface of water or ice.
+ GVR_AUDIO_MATERIAL_WATER_OR_ICE_SURFACE = 20,
+ /// Wooden ceiling.
+ GVR_AUDIO_MATERIAL_WOOD_CEILING = 21,
+ /// Wood paneling.
+ GVR_AUDIO_MATERIAL_WOOD_PANEL = 22,
+} gvr_audio_material_type;
+
+/// Distance rolloff models used for distance attenuation.
+typedef enum {
+ /// Logarithmic distance rolloff model.
+ GVR_AUDIO_ROLLOFF_LOGARITHMIC = 0,
+ /// Linear distance rolloff model.
+ GVR_AUDIO_ROLLOFF_LINEAR = 1,
+ /// No distance attenuation will be applied.
+ GVR_AUDIO_ROLLOFF_NONE = 2,
+} gvr_audio_distance_rolloff_type;
+
+/// Sound object and sound field identifier.
+typedef int32_t gvr_audio_source_id;
+
+/// Valid color formats for swap chain buffers.
+typedef enum {
+ /// Equivalent to GL_RGBA8
+ GVR_COLOR_FORMAT_RGBA_8888 = 0,
+ /// Equivalent to GL_RGB565
+ GVR_COLOR_FORMAT_RGB_565 = 1,
+} gvr_color_format_type;
+
+typedef enum {
+ /// No depth or stencil buffer.
+ GVR_DEPTH_STENCIL_FORMAT_NONE = 255,
+ /// Equivalent to GL_DEPTH_COMPONENT16.
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_16 = 0,
+ /// Equivalent to GL_DEPTH_COMPONENT24.
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_24 = 1,
+ /// Equivlaent to GL_DEPTH24_STENCIL8.
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_24_STENCIL_8 = 2,
+ /// Equivalent to GL_DEPTH_COMPONENT32F.
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F = 3,
+ /// Equivalent to GL_DEPTH_32F_STENCIL8.
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F_STENCIL_8 = 4,
+ /// Equivalent to GL_STENCIL8.
+ GVR_DEPTH_STENCIL_FORMAT_STENCIL_8 = 5,
+} gvr_depth_stencil_format_type;
+
+/// Types of asynchronous reprojection.
+typedef enum {
+ /// Do not reproject.
+ GVR_REPROJECTION_NONE = 0,
+ /// Reproject in all dimensions.
+ GVR_REPROJECTION_FULL = 1,
+} gvr_reprojection;
+
+typedef enum {
+ GVR_CONTROLLER_RIGHT_HANDED = 0,
+ GVR_CONTROLLER_LEFT_HANDED = 1,
+} gvr_controller_handedness;
+
+typedef struct gvr_user_prefs_ gvr_user_prefs;
+
+// Anonymous enum for miscellaneous integer constants.
+enum {
+ /// Constant that represents a nonexistent external surface. Pass to
+ /// gvr_buffer_viewport_set_external_surface_id() to disable sampling from
+ /// an external surface.
+ GVR_EXTERNAL_SURFACE_ID_NONE = -1,
+ /// Special index for a source buffer that has the same contents as the
+ /// external surface attached to the given viewport. Pass this to
+ /// gvr_buffer_viewport_set_source_buffer_index() to use the external surface
+ /// as the buffer contents.
+ GVR_BUFFER_INDEX_EXTERNAL_SURFACE = -1,
+};
+
+/// @}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+// These typedefs convert the C-style names to C++-style names.
+
+namespace gvr {
+
+const int32_t kControllerEnableOrientation =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_ORIENTATION);
+const int32_t kControllerEnableTouch =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_TOUCH);
+const int32_t kControllerEnableGyro =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_GYRO);
+const int32_t kControllerEnableAccel =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_ACCEL);
+const int32_t kControllerEnableGestures =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_GESTURES);
+const int32_t kControllerEnablePosePrediction =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_POSE_PREDICTION);
+const int32_t kControllerEnablePosition =
+ static_cast<int32_t>(GVR_CONTROLLER_ENABLE_POSITION);
+
+typedef gvr_controller_api_status ControllerApiStatus;
+const ControllerApiStatus kControllerApiOk =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_OK);
+const ControllerApiStatus kControllerApiUnsupported =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_UNSUPPORTED);
+const ControllerApiStatus kControllerApiNotAuthorized =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_NOT_AUTHORIZED);
+const ControllerApiStatus kControllerApiUnavailable =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_UNAVAILABLE);
+const ControllerApiStatus kControllerApiServiceObsolete =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_SERVICE_OBSOLETE);
+const ControllerApiStatus kControllerApiClientObsolete =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_CLIENT_OBSOLETE);
+const ControllerApiStatus kControllerApiMalfunction =
+ static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_MALFUNCTION);
+
+typedef gvr_controller_connection_state ControllerConnectionState;
+const ControllerConnectionState kControllerDisconnected =
+ static_cast<ControllerConnectionState>(GVR_CONTROLLER_DISCONNECTED);
+const ControllerConnectionState kControllerScanning =
+ static_cast<ControllerConnectionState>(GVR_CONTROLLER_SCANNING);
+const ControllerConnectionState kControllerConnecting =
+ static_cast<ControllerConnectionState>(GVR_CONTROLLER_CONNECTING);
+const ControllerConnectionState kControllerConnected =
+ static_cast<ControllerConnectionState>(GVR_CONTROLLER_CONNECTED);
+
+typedef gvr_controller_button ControllerButton;
+const ControllerButton kControllerButtonNone =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_NONE);
+const ControllerButton kControllerButtonClick =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_CLICK);
+const ControllerButton kControllerButtonHome =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_HOME);
+const ControllerButton kControllerButtonApp =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_APP);
+const ControllerButton kControllerButtonVolumeUp =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_VOLUME_UP);
+const ControllerButton kControllerButtonVolumeDown =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_VOLUME_DOWN);
+const ControllerButton kControllerButtonCount =
+ static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_COUNT);
+
+/// An uninitialized external surface ID.
+const int32_t kUninitializedExternalSurface = GVR_BUFFER_INDEX_EXTERNAL_SURFACE;
+/// The default source buffer index for viewports.
+const int32_t kDefaultBufferIndex = 0;
+
+typedef gvr_eye Eye;
+
+typedef gvr_viewer_type ViewerType;
+const ViewerType kViewerTypeCardboard =
+ static_cast<ViewerType>(GVR_VIEWER_TYPE_CARDBOARD);
+const ViewerType kViewerTypeDaydream =
+ static_cast<ViewerType>(GVR_VIEWER_TYPE_DAYDREAM);
+
+typedef gvr_version Version;
+typedef gvr_sizei Sizei;
+typedef gvr_recti Recti;
+typedef gvr_rectf Rectf;
+typedef gvr_vec2f Vec2f;
+typedef gvr_vec3f Vec3f;
+typedef gvr_mat4f Mat4f;
+typedef gvr_quatf Quatf;
+typedef gvr_clock_time_point ClockTimePoint;
+
+typedef gvr_vec2f ControllerVec2;
+typedef gvr_vec3f ControllerVec3;
+typedef gvr_quatf ControllerQuat;
+
+typedef gvr_audio_rendering_mode AudioRenderingMode;
+typedef gvr_audio_material_type AudioMaterialName;
+typedef gvr_audio_distance_rolloff_type AudioRolloffMethod;
+typedef gvr_audio_source_id AudioSourceId;
+
+typedef gvr_color_format_type ColorFormat;
+const ColorFormat kColorFormatRgba8888 =
+ static_cast<ColorFormat>(GVR_COLOR_FORMAT_RGBA_8888);
+const ColorFormat kColorFormatRgb565 =
+ static_cast<ColorFormat>(GVR_COLOR_FORMAT_RGB_565);
+
+typedef gvr_depth_stencil_format_type DepthStencilFormat;
+const DepthStencilFormat kDepthStencilFormatNone =
+ static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_NONE);
+const DepthStencilFormat kDepthStencilFormatDepth16 =
+ static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16);
+const DepthStencilFormat kDepthStencilFormatDepth24 =
+ static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_DEPTH_24);
+const DepthStencilFormat kDepthStencilFormatDepth24Stencil8 =
+ static_cast<DepthStencilFormat>(
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_24_STENCIL_8);
+const DepthStencilFormat kDepthStencilFormatDepth32f =
+ static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F);
+const DepthStencilFormat kDepthStencilFormatDepth32fStencil8 =
+ static_cast<DepthStencilFormat>(
+ GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F_STENCIL_8);
+const DepthStencilFormat kDepthStencilFormatStencil8 =
+ static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_STENCIL_8);
+
+typedef gvr_controller_handedness ControllerHandedness;
+const ControllerHandedness kControllerRightHanded =
+ static_cast<ControllerHandedness>(GVR_CONTROLLER_RIGHT_HANDED);
+const ControllerHandedness kControllerLeftHanded =
+ static_cast<ControllerHandedness>(GVR_CONTROLLER_LEFT_HANDED);
+
+typedef gvr_error Error;
+const Error kErrorNone = static_cast<Error>(GVR_ERROR_NONE);
+const Error kErrorControllerCreateFailed =
+ static_cast<Error>(GVR_ERROR_CONTROLLER_CREATE_FAILED);
+const Error kErrorNoFrameAvailable =
+ static_cast<Error>(GVR_ERROR_NO_FRAME_AVAILABLE);
+
+class AudioApi;
+class BufferSpec;
+class ControllerApi;
+class ControllerState;
+class Frame;
+class GvrApi;
+class BufferViewport;
+class BufferViewportList;
+class SwapChain;
+class UserPrefs;
+
+} // namespace gvr
+
+// Non-member equality operators for convenience. Since typedefs do not trigger
+// argument-dependent lookup, these operators have to be defined for the
+// underlying types.
+inline bool operator==(const gvr_vec2f& lhs, const gvr_vec2f& rhs) {
+ return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+inline bool operator!=(const gvr_vec2f& lhs, const gvr_vec2f& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_vec3f& lhs, const gvr_vec3f& rhs) {
+ return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+inline bool operator!=(const gvr_vec3f& lhs, const gvr_vec3f& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_recti& lhs, const gvr_recti& rhs) {
+ return lhs.left == rhs.left && lhs.right == rhs.right &&
+ lhs.bottom == rhs.bottom && lhs.top == rhs.top;
+}
+
+inline bool operator!=(const gvr_recti& lhs, const gvr_recti& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_rectf& lhs, const gvr_rectf& rhs) {
+ return lhs.left == rhs.left && lhs.right == rhs.right &&
+ lhs.bottom == rhs.bottom && lhs.top == rhs.top;
+}
+
+inline bool operator!=(const gvr_rectf& lhs, const gvr_rectf& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_sizei& lhs, const gvr_sizei& rhs) {
+ return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const gvr_sizei& lhs, const gvr_sizei& rhs) {
+ return !(lhs == rhs);
+}
+
+#endif // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif // VR_GVR_CAPI_INCLUDE_GVR_TYPES_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
new file mode 100644
index 0000000..6bc2b4e
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
@@ -0,0 +1,240 @@
+#ifndef VR_GVR_CAPI_SRC_GVR_EXPERIMENTAL_H_
+#define VR_GVR_CAPI_SRC_GVR_EXPERIMENTAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "vr/gvr/capi/include/gvr_types.h"
+#include "vr/gvr/capi/src/gvr_types_experimental.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// NOTE: APIs added to this file are *not* part of the core GVR library, and
+// should only be used for prototyping or experimental projects. The idea is
+// that APIs added here can be used for testing and development, graduating to
+// the core API (gvr.h or gvr_private.h) after we're ready to commit to them
+// indefinitely.
+
+// ************************************************************************** //
+// * DaydreamOS experimental APIs * //
+// ************************************************************************** //
+
+/// Gets the position and rotation from start space to head space. The head
+/// space is a space where the head is at the origin and faces the -Z direction.
+///
+/// @param gvr Pointer to the gvr instance from which to get the pose.
+/// @param time The time at which to get the head pose. The time should be in
+/// the future. If the time is not in the future, it will be clamped to now.
+/// @return A matrix representation of the position and rotation from start
+// space (the space where the head was last reset) to head space (the space
+/// with the head at the origin, and the axes aligned to the view vector).
+gvr_mat4f gvr_get_head_space_from_start_space_pose(
+ gvr_context* gvr, const gvr_clock_time_point time);
+
+/// Sets the compositor z-order of the swap chain.
+///
+/// @param swap_chain the swap chain to change.
+/// @param z_order Z order, higher values are displayed on top of lower ones,
+/// the default value is 0.
+void gvr_swap_chain_set_z_order(const gvr_swap_chain* swap_chain, int z_order);
+
+/// Creates a gvr_external_surface instance.
+/// An external surface is mainly used to pass external content (such as video
+/// frames, pre-rendered 2D Android UI) into distortion pass for compositing.
+/// The method gvr_external_surface_get_surface can be used to bridge Android
+/// components with GVR distortion pass via a traditional Android Surface
+/// instance.
+///
+/// @param gvr Pointer to the gvr instance from which to create the external
+/// surface.
+/// @return Pointer to an allocated gvr_external_surface object. The caller
+// is responsible for calling gvr_external_surface_destroy() on the
+/// returned object when it is no longer needed.
+gvr_external_surface* gvr_external_surface_create(gvr_context* gvr);
+
+/// Frees a gvr_external_surface instance and clears the pointer.
+/// Note that once a gvr_external_surface is destroyed, the Java Surface object
+/// returned from gvr_external_surface_get_surface remains to be accessible and
+/// functioning. It's up to Java's garbage collection to release all resources
+/// behind the Java Surface object.
+///
+/// @param surface Pointer to a pointer to the gvr_external_surface instance to
+/// be destroyed and nulled.
+void gvr_external_surface_destroy(gvr_external_surface** surface);
+
+/// Get an Android Surface as a Java object from the gvr_external_surface. This
+/// API is mainly used by standalone display service (aka when
+/// gvr_using_vr_display_service returns true) to access an Android Surface.
+///
+/// @param surface The gvr_external_surface associated with the Android Surface.
+/// Note that this API has to be called within a JNIEnv and is using the
+/// JNIEnv passed in during gvr_create.
+/// @return A jobject that is an instance of the 'android/view/Surface' Java
+/// class, NULL on failure. Note that the return value is really an opaque
+/// handle to a Java object and the life cycle of that object is maintained
+/// by Java (i.e. it will get garbage collected eventually). Thus, there is
+/// no need for an explicit destroy call.
+void* gvr_external_surface_get_surface(const gvr_external_surface* surface);
+
+/// Get the Surface ID associated with the gvr_external_surface. Note that the
+/// returned ID is used for internal bookkeeping only and should not be used
+/// by the app itself for lookups.
+/// @param surface The gvr_external_surface to query the ID for.
+/// @return The external surface ID associated with the gvr_external_surface.
+int32_t gvr_external_surface_get_surface_id(
+ const gvr_external_surface* surface);
+
+/// Queries whether a particular GVR feature is supported by the underlying
+/// platform.
+///
+/// @param gvr The context to query against.
+/// @param feature The gvr_feature type being queried.
+/// @return true if feature is supported, false otherwise.
+bool gvr_experimental_is_feature_supported(const gvr_context* gvr,
+ int32_t feature);
+
+/// Sets the z order of the layer to be created.
+/// Note that this API is a short-term workaround for SysUI work and is never
+/// meant to graduate as is to either gvr.h or gvr_private.h. The proper
+/// solution is tracked in b/33946428 and probably involves setting the
+/// attribute on some data structure that represents a layer.
+///
+/// @param spec Buffer specification.
+/// @param z_order Z order, higher values are displayed on top of lower ones,
+/// the default value is 0.
+void gvr_buffer_spec_set_z_order(gvr_buffer_spec* spec, int z_order);
+
+/// Sets the initial visibility of the layer to be created.
+/// Note that this API is a short-term workaround for SysUI work and is never
+/// meant to graduate as is to either gvr.h or gvr_private.h. The proper
+/// solution is tracked in b/33946428 and probably involves setting the
+/// attribute on some data structure that represents a layer.
+///
+/// @param spec Buffer specification.
+/// @param visibility Initial visibility of the layer, defaults to GVR_VISIBLE.
+/// See enum gvr_visibility for possible values.
+void gvr_buffer_spec_set_visibility(gvr_buffer_spec* spec,
+ int32_t visibility);
+
+/// Sets whether to blur layers below the layer to be created.
+/// Blurring is applied only to visible layers and only when the layer is
+/// visible.
+/// Note that this API currently is only implemented by the DreamOS
+/// implementation of GVR and is a no-op in other implementations.
+/// TODO(b/33946428): investigate the proper way to surface this feature
+/// to SysUI.
+///
+/// @param spec Buffer specification.
+/// @param blur_behind whether to blur layers behind, defaults to
+/// GVR_BLUR_BEHIND_TRUE. See enum gvr_blur_behind for possible values.
+void gvr_buffer_spec_set_blur_behind(gvr_buffer_spec* spec,
+ int32_t blur_behind);
+
+// ************************************************************************** //
+// * Daydream PlexEng experimental APIs * //
+// ************************************************************************** //
+
+// Registers a new performance event listener that will be invoked on points
+// of interest. By default no event listener is attached. If multiple event
+// listeners are attached they will all be invoked. Failures can be checked
+// with gvr_get_error().
+// @param out_handle The pointer to memory where a successfully created handle
+// will be written.
+// @param gvr The context to register callbacks for.
+// @param user_data The pointer that will be passed back on callbacks for
+// user_data.
+// @param event_callback The callback to be invoked when an event is observed.
+// On performance events callback will be invoked with the
+// user_data passed here, the gvr_perf_event_callback_type, and a float
+// value (if applicable) or -1.f.
+// @return Returns GVR_EXPERIMENTAL_ERROR_NONE if a handle was created,
+// GVR_EXPERIMENTAL_ERROR_UNIMPLEMENTED if this feature is disabled,
+// or GVR_EXPERIMENTAL_ERROR_INVALID_ARGUMENT if a null pointer was passed.
+bool gvr_experimental_register_perf_event_callback(
+ gvr_context* gvr, int* out_handle, void* user_data,
+ void (*event_callback)(void*, int, float));
+
+// Unregisters a previously registered callback by its handle. Failures can be
+// checked with gvr_get_error().
+// @param handle The handle which was returned when registering the callback.
+// @return Returns GVR_EXPERIMENTAL_ERROR_NONE if callback was unregistered,
+// GVR_EXPERIMENTAL_ERROR_INVALID_ARGUMENT if the if the handle wasn't
+// previously
+// registered. If this feature is not enabled it will return
+// GVR_EXPERIMENTAL_ERROR_UNIMPLEMENTED.
+bool gvr_experimental_unregister_perf_event_callback(gvr_context* gvr,
+ int handle);
+
+// ************************************************************************** //
+// * GVR Analytics experimental APIs * //
+// ************************************************************************** //
+// TODO(b/31634289): These functions are experimental because their main client
+// case is the performance monitoring HUD, whose form and function is still
+// under development. Consequently, the analytics API may change as the HUD's
+// needs become clearer.
+//
+// These functions will be moved into the main API (probably gvr_private.h) once
+// the HUD is ready to be shipped as part of the SDK.
+//
+// Contacts: georgelu@
+
+/// Returns whether the "Performance Monitoring" developer option is enabled.
+///
+/// @param user_prefs Pointer to the gvr_user_prefs object returned by
+/// gvr_get_user_prefs.
+/// @return True if the "Performance Monitoring" developer option is enabled.
+bool gvr_user_prefs_get_performance_monitoring_enabled(
+ const gvr_user_prefs* user_prefs);
+
+// Opaque struct returned by gvr_get_analytics that can be queried through
+// gvr_analytics_get* functions.
+//
+// Note: The struct is never actually defined since gvr_analytics is actually
+// just a static_cast of a gvr_context, similar to how gvr_user_prefs works.
+typedef struct gvr_analytics_ gvr_analytics;
+
+// Returns an opaque struct that can be queried for analytics data. The returned
+// struct remains valid as long as the context is valid.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @return An opaque struct that can be queried through gvr_analytics_*
+// functions.
+const gvr_analytics* gvr_get_analytics(gvr_context* gvr);
+
+// If the "Performance Monitoring" developer option in VR settings is enabled,
+// returns a gvr_analytics_sample* containing analytics data. Caller is
+// responsible for calling gvr_analytics_destroy_sample on the returned object.
+//
+// @param analytics gvr_analytics* returned by gvr_get_analytics.
+// @return gvr_analytics_sample* containing analytics data.
+const gvr_analytics_sample* gvr_analytics_create_sample(
+ const gvr_analytics* analytics);
+
+// Returns pointer to a buffer containing a serialized AnalyticsSample proto.
+// The buffer is valid only for the lifetime of the gvr_analytics_sample.
+//
+// @param Pointer to a gvr_analytics_sample object.
+// @return Pointer to buffer.
+const char* gvr_analytics_sample_get_buffer(const gvr_analytics_sample* sample);
+
+// Returns the length of the buffer returned by gvr_analytics_sample_get_buffer.
+//
+// @param Pointer to a gvr_analytics_sample object.
+// @return Length of buffer.
+size_t gvr_analytics_sample_get_buffer_length(
+ const gvr_analytics_sample* sample);
+
+// Destroys a gvr_analytics_sample* previously created through
+// gvr_analytics_create_sample.
+//
+// @param sample Pointer to pointer that will be set to null and whose
+// underlying gvr_analytics_sample will be destroyed.
+void gvr_analytics_destroy_sample(const gvr_analytics_sample** sample);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // VR_GVR_CAPI_SRC_GVR_EXPERIMENTAL_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_private.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_private.h
new file mode 100644
index 0000000..0a9d30e
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_private.h
@@ -0,0 +1,378 @@
+#ifndef VR_GVR_CAPI_SRC_GVR_PRIVATE_H_
+#define VR_GVR_CAPI_SRC_GVR_PRIVATE_H_
+
+#include <stddef.h>
+
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opaque handle to gvr_tracker_state object containing serialized state.
+typedef struct gvr_tracker_state_ gvr_tracker_state;
+
+// Opaque handle to gvr_display_synchronizer object for display synchronization.
+typedef struct gvr_display_synchronizer_ gvr_display_synchronizer;
+
+// Internal Google VR C API methods. These methods are exposed only to internal
+// targets, but should follow all the same backwards-compatible restrictions
+// as the public C API.
+
+/// Sets whether asynchronous reprojection is currently enabled.
+///
+/// If enabled, frames will be collected by the rendering system and
+/// asynchronously re-projected in sync with the scanout of the display. This
+/// feature may not be available on every platform, and requires a
+/// high-priority render thread with special extensions to function properly.
+///
+/// Note: On Android, this feature can be enabled solely via the GvrLayout Java
+/// instance which (indirectly) owns this gvr_context. The corresponding
+/// method call is GvrLayout.setAsyncReprojectionEnabled().
+///
+/// @param gvr Pointer to the gvr instance.
+/// @Param enabled Whether to enable async reprojection.
+/// @return Whether the setting was succesfully applied.
+bool gvr_set_async_reprojection_enabled(gvr_context* gvr, bool enabled);
+
+// Initializes necessary GL-related objects and uses the current thread and
+// GL context for racing the scanline. This function should only be called
+// by the SDK itself, not by any application, unless that application is
+// providing a high-priority thread and GL context for async reprojection.
+//
+// Note: This method is private as it is intended for use solely by the
+// hidden async reprojection implementation in ScanlineRacingRenderer.java,
+// called in onSurfaceCreated().
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_on_surface_created_reprojection_thread(gvr_context* gvr);
+
+// Renders the scanline layer. This function should only be called
+// in the same thread that gvr_initialize_gl_projection_thread was called in.
+// This function should only be called by the SDK itself, not by any
+// application, unless that application is providing a high-priority
+// thread and GL context for async reprojection.
+//
+// Note: This method is private as it is intended for use solely by the
+// hidden async reprojection implementation in ScanlineRacingRenderer.java.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_render_reprojection_thread(gvr_context* gvr);
+
+// Signals to the reprojection thread that it is paused. This is necessary
+// in case the application render thread is blocked on pending work by the
+// reprojection thread. This function will abort any blocking.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_on_pause_reprojection_thread(gvr_context* gvr);
+
+// Sets the parameters for the external surface managed by the reprojection
+// thread.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param surface_id The ID of the external Surface managed by the reprojection
+// thread. The ID is issued by the SurfaceTextureManager.
+// @param texture_id The GL texture ID associated with the external Surface.
+// @param timestamp The timestamp of the most recent frame the Surface holds.
+// @param surface_transfrom Matrix that transforms homogeneous texture coords to
+// the external surface texture space.
+void gvr_update_surface_reprojection_thread(gvr_context* gvr,
+ int32_t surface_id, int32_t texture_id, gvr_clock_time_point timestamp,
+ gvr_mat4f surface_transform);
+
+// Removes all external surfaces managed by the reprojection thread. This does
+// not destoy the surfaces: it removes tracking by the reprojection thread.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_remove_all_surfaces_reprojection_thread(gvr_context* gvr);
+
+// Reconnects the sensors when the sensor producers are created internally.
+//
+// Note: This function is not thread-safe, and should be called only on the
+// rendering thread. It is intended to be used internally by GvrLayout when
+// the target presentation display changes.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_reconnect_sensors(gvr_context* gvr);
+
+// Sets VR viewer params for the current context.
+//
+// Note: This function does not update the viewer proto in the common storage
+// location. Rather, it overrides the viewer params solely for the provided
+// gvr_context.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param serialized_viewer_params A pointer to the payload containing the
+// serialized viewer params proto.
+// @param serialized_viewer_params_size_bytes The length in bytes of the
+// serialized viewer params payload.
+// @return Whether the serialized viewer params proto was successfully applied.
+bool gvr_set_viewer_params(gvr_context* gvr,
+ const void* serialized_viewer_params,
+ size_t serialized_viewer_params_size_bytes);
+
+// Sets the lens offset.
+//
+// @param offset The offset of the lens center from the expected location in
+// screen space.
+void gvr_set_lens_offset(gvr_context* gvr, gvr_vec2f offset);
+
+// Sets display metrics for the current context.
+//
+// Note: This function does not update the phone proto in the commom storage
+// location. Rather, it overrides the internal metrics solely for the provided
+// |gvr| context.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param size_pixels The dimensions in pixels of the active display.
+// @param meters_per_pixel The density of the current display in meters/pixel.
+// @param border_size_meters The size of the border around the display
+// in meters. When the device sits on a surface in the proper
+// orientation this is the distance from the surface to the edge
+// of the display.
+void gvr_set_display_metrics(gvr_context* gvr, gvr_sizei size_pixels,
+ gvr_vec2f meters_per_pixel,
+ float border_size_meters);
+
+// Sets the display rotation offset that is applied at distortion correction
+// time to take into account the device's display orientation.
+//
+// For instance, calling this with display_output_rotation set to 1 allows
+// clients to lock their phone orientation to portrait on an Android phone and
+// still get a correctly rendered VR mode with the two eyes stacked up along the
+// longer phone dimension.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param display_output_rotation Value encoding the rotation used when
+// performing distortion correction. Supported values are:
+// 0 - Default mode. Eye viewports are positioned side-by-side along the
+// "width" dimension, with left eye in the x < 0.5 half.
+// 1 - Applies a clock-wise rotation of 90 degrees on the display when
+// doing distortion correction. Eye viewports are positioned
+// side-by-side along the "height" dimension, with left eye in the
+// y > 0.5 half.
+// Rotation modes used when performing distortion correction.
+enum {
+ GVR_PRIVATE_DISPLAY_OUTPUT_ROTATION_0 = 0,
+ GVR_PRIVATE_DISPLAY_OUTPUT_ROTATION_90 = 1,
+};
+void gvr_set_display_output_rotation(gvr_context* gvr,
+ int32_t display_output_rotation);
+
+// Gets the size of the border around the display used by the given gvr_context.
+//
+// @param gvr Pointer to the gvr_context instance.
+float gvr_get_border_size_meters(const gvr_context* gvr);
+
+// Returns whether the surface size was changed since the last call to this
+// function (it's changed with gvr_set_surface_size()).
+//
+// @param gvr Pointer to the gvr_context instance.
+// @return Whether the surface size was changed.
+bool gvr_check_surface_size_changed(gvr_context* gvr);
+
+// Returns the current surface size in pixels, or (0, 0) if the surface size
+// matches that of the active display (which is the default).
+//
+// @param gvr Pointer to the gvr_context instance.
+// @return The current surface size in pixels.
+gvr_sizei gvr_get_surface_size(const gvr_context* gvr);
+
+// Sets a handler that is called back when the back gesture is detected,
+// which is when the phone changes from landscape to portrait orientation
+// within a few seconds.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param handler The event_handler callback. May be null to clear the
+// registered event_handler.
+// @param user_data An opaque pointer to user_data which will be supplied
+// as the callback argument. The caller is responsible for ensuring the
+// validity of this data for the duration of the handler registration.
+typedef void (*event_handler)(void* user_data);
+void gvr_set_back_gesture_event_handler(gvr_context* gvr, event_handler handler,
+ void* user_data);
+
+// Internal method to pause head tracking used by GvrLayout. Disables all
+// sensors (to save power) and gets the serialized tracker state.
+//
+// @param gvr Pointer to the gvr instance for which tracking will be paused and
+// sensors disabled.
+//
+// @return Pointer to a tracker_state object containing the serialized tracker
+// state. The caller is responsible for calling destroy on the returned
+// handle.
+gvr_tracker_state* gvr_pause_tracking_get_state(gvr_context* gvr);
+
+// Internal method to resume head tracking used by GvrLayout. Re-enables all
+// sensors and sets the tracker state.
+//
+// @param gvr Pointer to the gvr instance for which tracking will be resumed.
+// serialized tracker state object.
+// @param tracker_state Pointer to a tracker_state object containing the
+// serialized tracker state object.
+void gvr_resume_tracking_set_state(
+ gvr_context* gvr, gvr_tracker_state* tracker_state);
+
+// Sets the internal flag that ignores calls to the public API's
+// gvr_pause_tracking and gvr_resume_tracking.
+// When true, the tracker is handled through GvrLayout
+// gvr_pause_tracking_private / gvr_resume_tracking_private direct calls through
+// the GvrApi instance are ignored. This is workaround to temporarily support
+// clients using GvrLayout that manually call pause/ resume tracking.
+// TODO(b/30404822) : clean this up once all existing clients move away from the
+// obsolete behavior.
+//
+// @param gvr Pointer to the gvr instance.
+// @param should_ignore Whether manual pause / resume tracker should be ignored.
+void gvr_set_ignore_manual_tracker_pause_resume(gvr_context* gvr,
+ bool should_ignore);
+
+// Creates a new tracker state object from the serialized tracker state buffer.
+//
+// @param tracker_state_buffer Pointer to buffer containing the serialized
+// tracker state.
+// @param buf_size Size of the tracker state buffer.
+//
+// @return Pointer to a tracker_state object containing the serialized tracker
+// state string. The caller is responsible for calling destroy on the returned
+// handle.
+gvr_tracker_state* gvr_tracker_state_create(const char* tracker_state_buffer,
+ size_t buf_size);
+
+// Gets the size of the buffer that is required to hold the serialized
+// gvr_tracker_state.
+//
+// @param Pointer to a gvr_tracker_state object containing the serialized
+// tracker state.
+//
+// @return Size of the buffer,
+size_t gvr_tracker_state_get_buffer_size(gvr_tracker_state* tracker_state);
+
+// Gets the buffer that holds the serialized gvr_tracker_state.
+//
+// @param Pointer to a tracker_state object containing the serialized tracker
+// state.
+//
+// @return Pointer to the buffer.
+const char* gvr_tracker_state_get_buffer(gvr_tracker_state* tracker_state);
+
+// Destroys a gvr_tracker_state instance.
+//
+// @param tracker_state Pointer to a pointer of the gvr_tracker_state instance
+// to be destroyed and nulled.
+void gvr_tracker_state_destroy(gvr_tracker_state** tracker_state);
+
+// Creates a new synchronizer instance.
+//
+// @return synchronizer Pointer to the new gvr_display_synchronizer instance.
+gvr_display_synchronizer* gvr_display_synchronizer_create();
+
+// Destroy the synchonronizer instance and null the pointer.
+//
+// @param synchronizer Pointer to a pointer to the gvr_display_synchronizer
+// instance.
+void gvr_display_synchronizer_destroy(gvr_display_synchronizer** synchronizer);
+
+// Resets the synchronizer with updated vsync timing data.
+//
+// @param synchronizer Pointer to the new gvr_display_synchronizer instance.
+// @param expected_interval_nanos The expected average time between
+// synchronization times, in nanoseconds, or 0 if unknown.
+// @param vsync_offset_nanos The duration, in nanos, such that the current sync
+// time minus the display vsync offset is the time when the physical
+// scan-out hardware begins to read data from the frame buffer.
+void gvr_display_synchronizer_reset(gvr_display_synchronizer* synchronizer,
+ int64_t expected_interval_nanos,
+ int64_t vsync_offset_nanos);
+
+// Updates the synchronizer with dispplay data for a new frame.
+//
+// @param vsync_time The new frame's vsync time.
+// @param rotation_degrees The screen rotation from sensor space to display
+// space in degrees.
+void gvr_display_synchronizer_update(gvr_display_synchronizer* synchronizer,
+ gvr_clock_time_point vsync_time,
+ int32_t rotation);
+
+// Installs the display synchronizer into a GVR context.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @param synchronizer Pointer to the gvr_display_synchronizer instance, to be
+// used by the context implementation during rendering.
+void gvr_set_display_synchronizer(gvr_context* gvr,
+ gvr_display_synchronizer* synchronizer);
+
+// Sets the current error code. Overwrites any existing error code.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @param error_code The error code to set.
+void gvr_set_error(gvr_context* gvr, int32_t error_code);
+
+// Called by the platform layer to to indicate the application is paused. (e.g.
+// On Android, this function is called by GvrLayout.OnPause().)
+//
+// @param gvr Pointer to the current gvr_context instance.
+void gvr_pause(gvr_context* gvr);
+
+// Called by the platform layer to to indicate the application has resumed.
+// (e.g. On Android, this function is called by GvrLayout.OnResume().)
+//
+// @param gvr Pointer to the current gvr_context instance.
+void gvr_resume(gvr_context* gvr);
+
+// Dumps additional data to logcat or disk to be included in bug reports.
+//
+// @param gvr Pointer to the current gvr_context instance.
+void gvr_dump_debug_data(gvr_context* gvr);
+
+// Returns true if the libgvr implementation is using the dedicated VR display
+// service, false otherwise.
+//
+// @param gvr Pointer to the current gvr_context instance.
+bool gvr_using_vr_display_service(gvr_context* gvr);
+
+// Creates a new gvr_context using the supplied tracker, only for testing.
+//
+// Note: The pose returned is *in start space*. This is *not* the same space as
+// the pose normally returned by |gvr_get_head_space_from_start_space_rotation|.
+//
+// @param tracker The test pose tracker to use.
+// @param user_data An opaque pointer to user_data which will be supplied
+// as the callback argument. The caller is responsible for ensuring the
+// validity of this data for the duration of the handler registration.
+typedef gvr_mat4f (*gvr_test_pose_tracker)(void*, gvr_clock_time_point);
+gvr_context* gvr_create_with_tracker_for_testing(gvr_test_pose_tracker tracker,
+ void* user_data);
+
+// Request resource sharing between the application's OpenGL context and the
+// scanline racing context. This must be called before gvr_initialize_gl.
+// <p>
+// This is a best effort request rather than an explicit toggle; it is a no-op
+// if the client does not enable async reprojection, or if the platform does not
+// support resource sharing.
+// <p>
+// The only OpenGL resource that we need sharing for is the framebuffer texture
+// that the app renders to, and that distortion samples from. If resource
+// sharing is disabled, then we use an EGLImage so that it can be accessed from
+// both contexts.
+// <p>
+// Also sets a callback function that is called at the end of gvr_initialize_gl,
+// while the application's OpenGL context is still active on the current thread.
+// This is used internally to notify the scanline racing renderer that the
+// application's OpenGL context has been created.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @param handler Callback that gets called when the app context becomes ready.
+// @param user_data An opaque pointer to user data which will be supplied
+// as the callback argument. The caller is responsible for ensuring the
+// validity of this data for the duration of the handler registration.
+typedef void (*gvr_egl_context_listener)(void*);
+void gvr_request_context_sharing(gvr_context* gvr,
+ gvr_egl_context_listener handler,
+ void* user_data);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // VR_GVR_CAPI_SRC_GVR_PRIVATE_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
new file mode 100644
index 0000000..1df2443
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
@@ -0,0 +1,68 @@
+#ifndef VR_GVR_CAPI_SRC_GVR_TYPES_EXPERIMENTAL_H_
+#define VR_GVR_CAPI_SRC_GVR_TYPES_EXPERIMENTAL_H_
+
+#include <string>
+
+// ************************************************************************** //
+// * DaydreamOS experimental Types * //
+// ************************************************************************** //
+
+// Visibility of a layer.
+typedef enum {
+ GVR_INVISIBLE = 0,
+ GVR_VISIBLE = 1,
+} gvr_visibility;
+
+// Whether to blur layers behind a layer.
+typedef enum {
+ GVR_BLUR_BEHIND_FALSE = 0,
+ GVR_BLUR_BEHIND_TRUE = 1,
+} gvr_blur_behind;
+
+// GVR external surface
+typedef struct gvr_external_surface_ gvr_external_surface;
+
+// ************************************************************************** //
+// * Daydream PlexEng experimental Types * //
+// ************************************************************************** //
+
+// Types of events that can have callbacks registered.
+// If documented, type will return a payload value when called, or will
+// otherwise be invoked with -1.f.
+// This enum has to be duplicated because there is no way to include from
+// /vr/gvr/render/performance_registry.h. Duplicate changes made here there.
+typedef enum {
+ // Will be invoked with value -1.f.
+ GVR_ON_ASYNC_REPROJECTION_FRAME_START = 0,
+ // Will be invoked with value -1.f.
+ GVR_ON_ASYNC_REPROJECTION_FRAME_STOP = 1,
+ // When invoked will be called with how late in microseconds the frame was.
+ GVR_ON_ASYNC_REPROJECTION_FRAME_DROP = 2,
+ // The number of types of performance events you can have.
+ // Also note that this value is considered invalid.
+ GVR_NUM_PERF_EVENT_CALLBACK_TYPES = 3,
+} gvr_perf_event_callback_type;
+
+// Types of VR-specific features which may or may not be supported on the
+// underlying platform.
+typedef enum {
+ // Asynchronous reprojection warps the app's rendered frame using the most
+ // recent head pose just before pushing the frame to the display.
+ GVR_ASYNC_REPROJECTION = 0,
+ // Head tracking with 6 degrees of freedom (position & rotation)
+ GVR_6DOF_HEAD_POSE = 1,
+} gvr_feature;
+
+// ************************************************************************** //
+// * GVR Analytics experimental APIs * //
+// ************************************************************************** //
+
+// Opaque struct returned by gvr_analytics_create_sample, used to transmit an
+// AnaylticsSample proto across the native layer.
+typedef struct gvr_analytics_sample_ {
+ // Serialized AnalyticsSample proto. Note that this is not a C string, meaning
+ // it is not null-terminated and may contain non-terminating nulls.
+ std::string serialized_proto;
+} gvr_analytics_sample;
+
+#endif // VR_GVR_CAPI_SRC_GVR_TYPES_EXPERIMENTAL_H_
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
new file mode 100644
index 0000000..1d0ba50
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
new file mode 100644
index 0000000..905ca64
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
new file mode 100644
index 0000000..d62f7ca
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
new file mode 100644
index 0000000..e342f6a
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/common_library.aar b/libs/vr/libgvr/prebuilt/lib/common_library.aar
new file mode 100644
index 0000000..13147fe
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/common_library.aar
Binary files differ
diff --git a/libs/vr/libgvr/shim_gvr.cpp b/libs/vr/libgvr/shim_gvr.cpp
new file mode 100644
index 0000000..4b074e7
--- /dev/null
+++ b/libs/vr/libgvr/shim_gvr.cpp
@@ -0,0 +1,1358 @@
+#define LOG_TAG "libgvr_shim"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl3ext.h>
+#include <algorithm>
+#include <cmath>
+
+#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 <cutils/log.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <private/dvr/buffer_hub_queue_core.h>
+#include <private/dvr/buffer_hub_queue_producer.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_client.h>
+#include <private/dvr/graphics_private.h>
+#include <private/dvr/internal_types.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/types.h>
+#include <private/dvr/video_mesh_surface_client.h>
+#include <sys/system_properties.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_ext.h>
+#include <vr/gvr/capi/include/gvr_util.h>
+#include <vr/gvr/capi/src/gvr_experimental.h>
+#include <vr/gvr/capi/src/gvr_private.h>
+
+#include <android_runtime/android_view_Surface.h>
+#include <gui/Surface.h>
+
+using android::dvr::DisplayClient;
+using android::dvr::EigenToGvrMatrix;
+using android::dvr::FieldOfView;
+using android::dvr::FovRadiansToDegrees;
+using android::dvr::GetSystemClockNs;
+using android::dvr::GvrIdentityMatrix;
+using android::dvr::GvrMatrixToPosef;
+using android::dvr::GvrToDvrFov;
+using android::dvr::GvrToEigenMatrix;
+using android::dvr::GvrToEigenRotation;
+using android::dvr::GvrTranslationMatrix;
+using android::dvr::IsEqual;
+using android::dvr::PosefToGvrMatrix;
+using android::dvr::mat3;
+using android::dvr::mat4;
+using android::dvr::Posef;
+using android::dvr::quat;
+using android::dvr::vec3;
+
+namespace {
+
+constexpr static int32_t GVR_SDK_MAJOR_VERSION = 2;
+constexpr static int32_t GVR_SDK_MINOR_VERSION = 0;
+constexpr static int32_t GVR_SDK_PATCH_VERSION = 0;
+
+// The "DaydreamOS" part has been appended to make easier to see when VrCore
+// dynamic GVR API loading is effectively working.
+static const char* kVersionString = "2.0.0 DaydreamOS";
+static const char* kViewerVendor = "Google";
+static const char* kViewerModel = "Lucid";
+
+// Experimental system property used to provide 6DoF information on 3DoF APIs.
+static const char* kForce6DofProp = "experimental.force_6dof";
+
+static constexpr int kControllerCount = 2;
+
+gvr_frame* GetFrameFromSwapChain(gvr_swap_chain* swap_chain) {
+ return reinterpret_cast<gvr_frame*>(swap_chain);
+}
+
+gvr_swap_chain* GetSwapChainForFrame(gvr_frame* frame) {
+ return reinterpret_cast<gvr_swap_chain*>(frame);
+}
+
+const gvr_swap_chain* GetSwapChainForFrame(const gvr_frame* frame) {
+ return reinterpret_cast<const gvr_swap_chain*>(frame);
+}
+
+// Returns the world to head transform as a Posef.
+Posef ToPosef(const DvrPoseAsync& pose) {
+ return Posef(
+ quat(pose.orientation[3], pose.orientation[0], pose.orientation[1],
+ pose.orientation[2]),
+ vec3(pose.translation[0], pose.translation[1], pose.translation[2]));
+}
+
+// Returns the world to head transform, with 0 position, as a gvr matrix
+gvr_mat4f Gvr6dofTo3dof(const gvr_mat4f& pose) {
+ gvr_mat4f ret = pose;
+ ret.m[0][3] = 0;
+ ret.m[1][3] = 0;
+ ret.m[2][3] = 0;
+ return ret;
+}
+
+void GvrToDvrPose(gvr_mat4f world_to_head_transform,
+ /*out*/ float32x4_t* orientation,
+ /*out */ float32x4_t* translation) {
+ Posef pose = GvrMatrixToPosef(world_to_head_transform);
+ (*orientation)[0] = pose.GetRotation().x();
+ (*orientation)[1] = pose.GetRotation().y();
+ (*orientation)[2] = pose.GetRotation().z();
+ (*orientation)[3] = pose.GetRotation().w();
+ (*translation)[0] = pose.GetPosition().x();
+ (*translation)[1] = pose.GetPosition().y();
+ (*translation)[2] = pose.GetPosition().z();
+ (*translation)[3] = 0;
+}
+
+bool MatricesAlmostEqual(const gvr_mat4f& m1, const gvr_mat4f& m2,
+ float tolerance) {
+ for (int row = 0; row < 4; ++row) {
+ for (int col = 0; col < 4; ++col) {
+ if (!IsEqual(m1.m[row][col], m2.m[row][col], tolerance))
+ return false;
+ }
+ }
+ return true;
+}
+
+gvr_mat4f FovToViewportTransform(const gvr_rectf& fov) {
+ // Depth range (1 1000) is chosen to match gvr impl in google3, which is
+ // chosen to match Unity integration.
+ return EigenToGvrMatrix(
+ GvrToDvrFov(fov).GetProjectionMatrix(1.f, 1000.f).inverse());
+}
+
+gvr_rectf ViewportTransformToFov(const gvr_mat4f& transform) {
+ return DvrToGvrFov(
+ FieldOfView::FromProjectionMatrix(GvrToEigenMatrix(transform).inverse()));
+}
+
+bool GetGlColorFormat(int32_t gvr_color_format,
+ /*out*/ GLenum* gl_color_format) {
+ switch (gvr_color_format) {
+ case GVR_COLOR_FORMAT_RGBA_8888:
+ *gl_color_format = GL_RGBA8;
+ break;
+ case GVR_COLOR_FORMAT_RGB_565:
+ *gl_color_format = GL_RGB565;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool GetGlDepthFormat(int32_t gvr_depth_format,
+ /*out*/ GLenum* gl_depth_format) {
+ switch (gvr_depth_format) {
+ case GVR_DEPTH_STENCIL_FORMAT_DEPTH_16:
+ *gl_depth_format = GL_DEPTH_COMPONENT16;
+ break;
+ case GVR_DEPTH_STENCIL_FORMAT_DEPTH_24:
+ *gl_depth_format = GL_DEPTH_COMPONENT24;
+ break;
+ case GVR_DEPTH_STENCIL_FORMAT_DEPTH_24_STENCIL_8:
+ *gl_depth_format = GL_DEPTH24_STENCIL8;
+ break;
+ case GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F:
+ *gl_depth_format = GL_DEPTH_COMPONENT32F;
+ break;
+ case GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F_STENCIL_8:
+ *gl_depth_format = GL_DEPTH32F_STENCIL8;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+// Returns true on success, false on failure. If the swap_chain already has a
+// DvrGraphicsContext and gvr buffer, they'll be freed first. If creation fails,
+// the DvrGraphicsContext in the swap_chain will be set to null and the
+// corresponding gvr buffer will be freed.
+bool CreateDvrGraphicsContextAndGvrBuffer(gvr_swap_chain* swap_chain) {
+ if (swap_chain->buffers_.empty()) {
+ ALOGE("Can't create a graphics context for an empty swap chain");
+ return false;
+ }
+
+ // We currently only render the first gvr buffer. Create a DvrGraphicsContext
+ // for the first buffer only.
+ gvr_buffer& buf = swap_chain->buffers_[0];
+ buf.FreeGl();
+
+ bool visible;
+ int z_order;
+ if (swap_chain->graphics_context_ != nullptr) {
+ visible = dvrGraphicsSurfaceGetVisible(swap_chain->graphics_context_);
+ z_order = dvrGraphicsSurfaceGetZOrder(swap_chain->graphics_context_);
+ dvrGraphicsContextDestroy(swap_chain->graphics_context_);
+ swap_chain->graphics_context_ = nullptr;
+ } else {
+ visible = buf.spec.initially_visible;
+ z_order = buf.spec.z_order;
+ }
+
+ int width = 0, height = 0;
+ GLuint texture_id = 0;
+ GLenum texture_target = 0;
+ DvrSurfaceParameter surface_params[] = {
+ DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, false),
+ DVR_SURFACE_PARAMETER_IN(CREATE_GL_CONTEXT, 0),
+ DVR_SURFACE_PARAMETER_IN(WIDTH, buf.spec.size.width),
+ DVR_SURFACE_PARAMETER_IN(HEIGHT, buf.spec.size.height),
+ DVR_SURFACE_PARAMETER_IN(BLUR_BEHIND, buf.spec.blur_behind),
+ DVR_SURFACE_PARAMETER_IN(VISIBLE, visible),
+ DVR_SURFACE_PARAMETER_IN(Z_ORDER, z_order),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &width),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &height),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_TYPE, &texture_target),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_ID, &texture_id),
+ DVR_SURFACE_PARAMETER_LIST_END,
+ };
+
+ DvrGraphicsContext* graphics_context;
+ int ret = dvrGraphicsContextCreate(surface_params, &graphics_context);
+ if (ret < 0) {
+ ALOGE("dvrGraphicsContextCreate failed: %d (%s)", ret, strerror(-ret));
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return false;
+ }
+
+ // Sanity check that the size of the buffer we allocated from the system is
+ // what we expect
+ if (buf.spec.size != gvr_sizei{width, height}) {
+ ALOGE(
+ "The created surface is the wrong size."
+ " Should be %dx%d, instead got %dx%d.",
+ buf.spec.size.width, buf.spec.size.height, width, height);
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ dvrGraphicsContextDestroy(graphics_context);
+ return false;
+ }
+
+ buf = gvr_buffer(swap_chain->context, buf.spec, texture_id, texture_target);
+ if (buf.frame_buffer == 0) {
+ dvrGraphicsContextDestroy(graphics_context);
+ return false;
+ }
+
+ swap_chain->graphics_context_ = graphics_context;
+ return true;
+}
+
+bool SwapChainResizeBuffer(gvr_swap_chain* swap_chain, int buffer_index) {
+ gvr_buffer& buf = swap_chain->buffers_[buffer_index];
+ buf.FreeGl();
+ gvr_sizei orig_size = buf.spec.size;
+ buf.spec.size = buf.requested_size;
+ bool resize_successful = false;
+ if (buffer_index == 0) {
+ resize_successful = CreateDvrGraphicsContextAndGvrBuffer(swap_chain);
+ } else {
+ buf = gvr_buffer(swap_chain->context, buf.spec, 0, GL_TEXTURE_2D);
+ resize_successful = buf.frame_buffer != 0;
+ }
+
+ if (resize_successful) {
+ // The resize was successful, so clear the resize request
+ buf.requested_size = {-1, -1};
+ } else {
+ ALOGE("Failed to resize buffer. orig_size=%dx%d requested_size=%dx%d.",
+ orig_size.width, orig_size.height, buf.requested_size.width,
+ buf.requested_size.height);
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ buf.spec.size = orig_size;
+ }
+
+ return resize_successful;
+}
+
+void WaitNextFrame(gvr_swap_chain* swap_chain, int64_t start_delay_nanos,
+ gvr_frame_schedule* out_next_frame_schedule,
+ bool called_by_app) {
+ if (called_by_app)
+ swap_chain->wait_next_frame_called_by_app_ = true;
+
+ DvrFrameSchedule dvr_schedule;
+ int ret = dvrGraphicsWaitNextFrame(swap_chain->graphics_context_,
+ start_delay_nanos, &dvr_schedule);
+ if (ret < 0) {
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return;
+ }
+ if (out_next_frame_schedule) {
+ out_next_frame_schedule->vsync_count = dvr_schedule.vsync_count;
+ out_next_frame_schedule->scheduled_finish.monotonic_system_time_nanos =
+ dvr_schedule.scheduled_frame_finish_ns;
+ }
+
+ DvrPoseAsync pose;
+ ret = dvrPoseGet(swap_chain->context->pose_client_, dvr_schedule.vsync_count,
+ &pose);
+ if (ret < 0) {
+ ALOGW("dvrPoseGet failed: %d", ret);
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return;
+ }
+
+ swap_chain->context->next_frame_6dof_pose_ = PosefToGvrMatrix(ToPosef(pose));
+
+ for (int i = 0; i < kControllerCount; ++i) {
+ ret = dvrPoseGetController(swap_chain->context->pose_client_, i,
+ dvr_schedule.vsync_count, &pose);
+ if (ret == 0) {
+ // Silently fail when there are no controllers.
+ swap_chain->context->next_frame_controller_pose_[i] =
+ PosefToGvrMatrix(ToPosef(pose).Inverse());
+ }
+ }
+}
+
+bool VerifyBufferIndex(const std::string& function_name,
+ const gvr_swap_chain* swap_chain, int index) {
+ if (index > static_cast<int32_t>(swap_chain->buffers_.size())) {
+ ALOGE("%s out of range buffer index. index=%d num_buffers=%zu.",
+ function_name.c_str(), index, swap_chain->buffers_.size());
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return false;
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+gvr_context* gvr_create(JNIEnv* env, jobject /* app_context */,
+ jobject /* class_loader */) {
+ std::unique_ptr<gvr_context> context(new gvr_context);
+
+ // Set cpu set to avoid default scheduling randomness.
+ dvrSetCpuPartition(0, "/application/performance");
+
+ context->jni_env_ = env;
+ context->pose_client_ = dvrPoseCreate();
+ if (!context->pose_client_) {
+ ALOGE("Failed to create pose client");
+ return nullptr;
+ }
+
+ context->display_client_ = DisplayClient::Create();
+ if (!context->display_client_) {
+ ALOGE("Failed to create display client");
+ return nullptr;
+ }
+
+ int ret =
+ context->display_client_->GetDisplayMetrics(&context->display_metrics_);
+ if (ret < 0) {
+ ALOGE("Failed to get display metrics: %d (%s)", ret, strerror(-ret));
+ return nullptr;
+ }
+
+ const float* left_fov = context->display_metrics_.left_fov_lrbt.data();
+ context->left_eye_viewport_transform_ =
+ FovToViewportTransform(FovRadiansToDegrees(
+ gvr_rectf{left_fov[0], left_fov[1], left_fov[2], left_fov[3]}));
+
+ const float* right_fov = context->display_metrics_.right_fov_lrbt.data();
+ context->right_eye_viewport_transform_ =
+ FovToViewportTransform(FovRadiansToDegrees(
+ gvr_rectf{right_fov[0], right_fov[1], right_fov[2], right_fov[3]}));
+
+ context->next_frame_6dof_pose_ = GvrIdentityMatrix();
+
+ for (int i = 0; i < kControllerCount; ++i) {
+ context->next_frame_controller_pose_[i] = GvrIdentityMatrix();
+ }
+
+ // Check the system property to force 6DoF when requested 3DoF.
+ char prop_buffer[PROP_VALUE_MAX];
+ if (__system_property_get(kForce6DofProp, prop_buffer) &&
+ (!strncasecmp("1", prop_buffer, PROP_VALUE_MAX) ||
+ !strncasecmp("true", prop_buffer, PROP_VALUE_MAX))) {
+ context->force_6dof_ = true;
+ }
+
+ return context.release();
+}
+
+gvr_version gvr_get_version() {
+ gvr_version version = {};
+ version.major = GVR_SDK_MAJOR_VERSION;
+ version.minor = GVR_SDK_MINOR_VERSION;
+ version.patch = GVR_SDK_PATCH_VERSION;
+ return version;
+}
+
+const char* gvr_get_version_string() { return kVersionString; }
+
+int32_t gvr_get_error(gvr_context* gvr) { return gvr->last_error_; }
+
+int32_t gvr_clear_error(gvr_context* gvr) {
+ int32_t last_error = gvr->last_error_;
+ gvr->last_error_ = GVR_ERROR_NONE;
+ return last_error;
+}
+
+const char* gvr_get_error_string(int32_t error_code) {
+ switch (error_code) {
+ case GVR_ERROR_NONE:
+ return "No error";
+ case GVR_ERROR_CONTROLLER_CREATE_FAILED:
+ return "Creation of GVR controller context failed";
+ case GVR_ERROR_NO_FRAME_AVAILABLE:
+ return "No frame available in swap chain";
+ case GVR_ERROR_INTERNAL:
+ return "Internal error";
+ default:
+ return "(Internal error: unknown error code)";
+ }
+}
+
+const gvr_user_prefs* gvr_get_user_prefs(gvr_context* gvr) {
+ return &gvr->user_prefs_;
+}
+
+int32_t gvr_user_prefs_get_controller_handedness(
+ const gvr_user_prefs* /* user_prefs */) {
+ return GVR_CONTROLLER_RIGHT_HANDED;
+}
+
+gvr_context_::~gvr_context_() {
+ for (gvr_swap_chain* swap_chain : swap_chains_)
+ swap_chain->context = nullptr;
+ if (pose_client_)
+ dvrPoseDestroy(pose_client_);
+}
+
+void gvr_destroy(gvr_context** gvr) {
+ if (!gvr || !(*gvr)) {
+ ALOGW("gvr_destroy: Invalid gvr_context pointer.");
+ return;
+ }
+ delete *gvr;
+ *gvr = nullptr;
+}
+
+void gvr_initialize_gl(gvr_context* /* gvr */) {}
+
+bool gvr_get_async_reprojection_enabled(const gvr_context* /* gvr */) {
+ return true;
+}
+
+void gvr_get_recommended_buffer_viewports(
+ const gvr_context* gvr, gvr_buffer_viewport_list* viewport_list) {
+ gvr_buffer_viewport left(
+ /*buffer_index*/ 0,
+ /*uv*/ {0, .5f, 0, 1}, gvr->left_eye_viewport_transform_, GVR_LEFT_EYE,
+ GVR_EXTERNAL_SURFACE_ID_NONE, GVR_REPROJECTION_FULL);
+
+ gvr_buffer_viewport right(
+ /*buffer_index*/ 0,
+ /*uv*/ {.5f, 1, 0, 1}, gvr->right_eye_viewport_transform_, GVR_RIGHT_EYE,
+ GVR_EXTERNAL_SURFACE_ID_NONE, GVR_REPROJECTION_FULL);
+
+ viewport_list->viewports.resize(2);
+ viewport_list->viewports[0] = left;
+ viewport_list->viewports[1] = right;
+}
+
+void gvr_get_screen_buffer_viewports(const gvr_context* gvr,
+ gvr_buffer_viewport_list* viewport_list) {
+ gvr_get_recommended_buffer_viewports(gvr, viewport_list);
+}
+
+gvr_sizei gvr_get_maximum_effective_render_target_size(const gvr_context* gvr) {
+ return gvr_sizei{
+ static_cast<int32_t>(gvr->display_metrics_.distorted_width),
+ static_cast<int32_t>(gvr->display_metrics_.distorted_height)};
+}
+
+gvr_sizei gvr_get_screen_target_size(const gvr_context* gvr) {
+ // DisplayMetrics returns native_width and native_height for the display in
+ // portrait orientation, which our device is never in. Swap the width and
+ // height to account for this.
+ return gvr_sizei{
+ static_cast<int32_t>(gvr->display_metrics_.display_native_height),
+ static_cast<int32_t>(gvr->display_metrics_.display_native_width)};
+}
+
+void gvr_set_surface_size(gvr_context* gvr,
+ gvr_sizei /* surface_size_pixels */) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_set_surface_size not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+void gvr_distort_to_screen(
+ gvr_context* gvr, int32_t /* texture_id */,
+ const gvr_buffer_viewport_list* /* viewport_list */,
+ gvr_mat4f /* head_space_from_start_space */,
+ gvr_clock_time_point /* target_presentation_time */) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_distort_to_screen not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Viewports and viewport lists
+/////////////////////////////////////////////////////////////////////////////
+
+bool gvr_buffer_viewport::operator==(const gvr_buffer_viewport_& other) const {
+ return buffer_index == other.buffer_index && uv == other.uv &&
+ eye == other.eye && external_surface_id == other.external_surface_id &&
+ reprojection == other.reprojection &&
+ MatricesAlmostEqual(transform, other.transform, 1e-5f);
+}
+
+gvr_buffer_viewport* gvr_buffer_viewport_create(gvr_context* /* gvr */) {
+ return new gvr_buffer_viewport;
+}
+
+void gvr_buffer_viewport_destroy(gvr_buffer_viewport** viewport) {
+ if (viewport) {
+ delete *viewport;
+ *viewport = nullptr;
+ }
+}
+
+gvr_rectf gvr_buffer_viewport_get_source_uv(
+ const gvr_buffer_viewport* viewport) {
+ return viewport->uv;
+}
+
+void gvr_buffer_viewport_set_source_uv(gvr_buffer_viewport* viewport,
+ gvr_rectf uv) {
+ viewport->uv = uv;
+}
+
+gvr_rectf gvr_buffer_viewport_get_source_fov(
+ const gvr_buffer_viewport* viewport) {
+ return ViewportTransformToFov(viewport->transform);
+}
+
+void gvr_buffer_viewport_set_source_fov(gvr_buffer_viewport* viewport,
+ gvr_rectf fov) {
+ viewport->transform = FovToViewportTransform(fov);
+}
+
+gvr_mat4f gvr_buffer_viewport_get_transform(
+ const gvr_buffer_viewport* viewport) {
+ return viewport->transform;
+}
+
+void gvr_buffer_viewport_set_transform(gvr_buffer_viewport* viewport,
+ gvr_mat4f transform) {
+ viewport->transform = transform;
+}
+
+int32_t gvr_buffer_viewport_get_target_eye(
+ const gvr_buffer_viewport* viewport) {
+ return viewport->eye;
+}
+
+void gvr_buffer_viewport_set_target_eye(gvr_buffer_viewport* viewport,
+ int32_t index) {
+ viewport->eye = index;
+}
+
+int32_t gvr_buffer_viewport_get_source_buffer_index(
+ const gvr_buffer_viewport* viewport) {
+ return viewport->buffer_index;
+}
+
+void gvr_buffer_viewport_set_source_buffer_index(gvr_buffer_viewport* viewport,
+ int32_t buffer_index) {
+ viewport->buffer_index = buffer_index;
+}
+
+int32_t gvr_buffer_viewport_get_external_surface_id(
+ const gvr_buffer_viewport* viewport) {
+ return viewport->external_surface_id;
+}
+
+void gvr_buffer_viewport_set_external_surface_id(gvr_buffer_viewport* viewport,
+ int32_t external_surface_id) {
+ viewport->external_surface_id = external_surface_id;
+}
+
+int32_t gvr_buffer_viewport_get_reprojection(
+ const gvr_buffer_viewport* viewport) {
+ return viewport->reprojection;
+}
+
+void gvr_buffer_viewport_set_reprojection(gvr_buffer_viewport* viewport,
+ int32_t reprojection) {
+ viewport->reprojection = static_cast<gvr_reprojection>(reprojection);
+}
+
+bool gvr_buffer_viewport_equal(const gvr_buffer_viewport* a,
+ const gvr_buffer_viewport* b) {
+ return *a == *b;
+}
+
+gvr_buffer_viewport_list* gvr_buffer_viewport_list_create(
+ const gvr_context* /* gvr */) {
+ return new gvr_buffer_viewport_list;
+}
+
+void gvr_buffer_viewport_list_destroy(
+ gvr_buffer_viewport_list** viewport_list) {
+ if (!viewport_list || !(*viewport_list)) {
+ ALOGW("gvr_buffer_viewport_list_destroy: Invalid list pointer.");
+ return;
+ }
+ delete *viewport_list;
+ *viewport_list = nullptr;
+}
+
+size_t gvr_buffer_viewport_list_get_size(
+ const gvr_buffer_viewport_list* viewport_list) {
+ return viewport_list->viewports.size();
+}
+
+void gvr_buffer_viewport_list_get_item(
+ const gvr_buffer_viewport_list* viewport_list, size_t index,
+ gvr_buffer_viewport* viewport) {
+ *viewport = viewport_list->viewports[index];
+}
+
+void gvr_buffer_viewport_list_set_item(gvr_buffer_viewport_list* viewport_list,
+ size_t index,
+ const gvr_buffer_viewport* viewport) {
+ if (index < viewport_list->viewports.size())
+ viewport_list->viewports[index] = *viewport;
+ else
+ viewport_list->viewports.push_back(*viewport);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Swapchains and frames
+/////////////////////////////////////////////////////////////////////////////
+
+gvr_buffer_spec* gvr_buffer_spec_create(gvr_context* /* gvr */) {
+ return new gvr_buffer_spec;
+}
+
+void gvr_buffer_spec_destroy(gvr_buffer_spec** spec) {
+ if (spec) {
+ delete *spec;
+ *spec = nullptr;
+ }
+}
+
+gvr_sizei gvr_buffer_spec_get_size(const gvr_buffer_spec* spec) {
+ return spec->size;
+}
+
+void gvr_buffer_spec_set_size(gvr_buffer_spec* spec, gvr_sizei size) {
+ spec->size = size;
+}
+
+int32_t gvr_buffer_spec_get_samples(const gvr_buffer_spec* spec) {
+ return spec->msaa_samples;
+}
+
+void gvr_buffer_spec_set_samples(gvr_buffer_spec* spec, int32_t num_samples) {
+ spec->msaa_samples = num_samples;
+}
+
+void gvr_buffer_spec_set_color_format(gvr_buffer_spec* spec,
+ int32_t color_format) {
+ spec->color_format = color_format;
+}
+
+void gvr_buffer_spec_set_depth_stencil_format(gvr_buffer_spec* spec,
+ int32_t depth_stencil_format) {
+ spec->depth_stencil_format = depth_stencil_format;
+}
+
+void gvr_buffer_spec_set_z_order(gvr_buffer_spec* spec, int z_order) {
+ spec->z_order = z_order;
+}
+
+void gvr_buffer_spec_set_visibility(gvr_buffer_spec* spec,
+ int32_t visibility) {
+ spec->initially_visible = (visibility != GVR_INVISIBLE);
+}
+
+void gvr_buffer_spec_set_blur_behind(gvr_buffer_spec* spec,
+ int32_t blur_behind) {
+ spec->blur_behind = (blur_behind != GVR_BLUR_BEHIND_FALSE);
+}
+
+void gvr_buffer::SetDefaults() {
+ spec = gvr_buffer_spec();
+ frame_buffer = 0;
+ color_render_buffer = 0;
+ depth_stencil_render_buffer = 0;
+ requested_size = {-1, -1};
+}
+
+gvr_buffer::gvr_buffer() { SetDefaults(); }
+
+gvr_buffer::gvr_buffer(gvr_context* gvr, const gvr_buffer_spec& spec_in,
+ GLuint texture_id, GLenum texture_target) {
+ SetDefaults();
+ spec = spec_in;
+
+ glGetError(); // Clear error state
+ glGenFramebuffers(1, &frame_buffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer);
+
+ if (texture_id == 0) {
+ GLenum gl_color_format;
+ if (!GetGlColorFormat(spec.color_format, &gl_color_format)) {
+ ALOGE("Unknown color format: %d", spec.color_format);
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ FreeGl();
+ return;
+ }
+
+ glGenRenderbuffers(1, &color_render_buffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, color_render_buffer);
+ if (spec.msaa_samples < 2) {
+ glRenderbufferStorage(GL_RENDERBUFFER, gl_color_format, spec.size.width,
+ spec.size.height);
+ } else {
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, spec.msaa_samples,
+ gl_color_format, spec.size.width,
+ spec.size.height);
+ }
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, color_render_buffer);
+ } else {
+ if (spec.msaa_samples < 2) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target, texture_id, 0);
+ } else {
+ glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target, texture_id, 0,
+ spec.msaa_samples);
+ }
+ }
+
+ if (spec.depth_stencil_format != GVR_DEPTH_STENCIL_FORMAT_NONE) {
+ GLenum gl_depth_format;
+ if (!GetGlDepthFormat(spec.depth_stencil_format, &gl_depth_format)) {
+ ALOGE("Unknown depth/stencil format: %d", spec.depth_stencil_format);
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ FreeGl();
+ return;
+ }
+
+ glGenRenderbuffers(1, &depth_stencil_render_buffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_render_buffer);
+ if (spec.msaa_samples < 2) {
+ glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, spec.size.width,
+ spec.size.height);
+ } else {
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, spec.msaa_samples,
+ gl_depth_format, spec.size.width,
+ spec.size.height);
+ }
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_render_buffer);
+ }
+
+ GLenum gl_error = glGetError();
+ if (gl_error != GL_NO_ERROR) {
+ ALOGE("GL error after creating framebuffer: %d", gl_error);
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ FreeGl();
+ return;
+ }
+
+ GLenum framebuffer_complete_result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (framebuffer_complete_result != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Framebuffer setup failed. glCheckFramebufferStatus returned %d",
+ framebuffer_complete_result);
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ FreeGl();
+ return;
+ }
+}
+
+void gvr_buffer::FreeGl() {
+ if (frame_buffer != 0) {
+ glDeleteFramebuffers(1, &frame_buffer);
+ frame_buffer = 0;
+ }
+ if (color_render_buffer != 0) {
+ glDeleteRenderbuffers(1, &color_render_buffer);
+ color_render_buffer = 0;
+ }
+ if (depth_stencil_render_buffer != 0) {
+ glDeleteRenderbuffers(1, &depth_stencil_render_buffer);
+ depth_stencil_render_buffer = 0;
+ }
+}
+
+gvr_buffer::~gvr_buffer() { FreeGl(); }
+
+gvr_buffer::gvr_buffer(gvr_buffer&& other) {
+ spec = other.spec;
+ frame_buffer = other.frame_buffer;
+ color_render_buffer = other.color_render_buffer;
+ depth_stencil_render_buffer = other.depth_stencil_render_buffer;
+ requested_size = other.requested_size;
+ other.SetDefaults();
+}
+
+gvr_buffer& gvr_buffer::operator=(gvr_buffer&& other) {
+ if (this == &other)
+ return *this;
+ spec = other.spec;
+ frame_buffer = other.frame_buffer;
+ color_render_buffer = other.color_render_buffer;
+ depth_stencil_render_buffer = other.depth_stencil_render_buffer;
+ requested_size = other.requested_size;
+ other.SetDefaults();
+ return *this;
+}
+
+gvr_swap_chain* gvr_swap_chain_create(gvr_context* gvr,
+ const gvr_buffer_spec** buffers,
+ int32_t count) {
+ if (count == 0) {
+ ALOGE("At least one buffer must be requested");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ return nullptr;
+ }
+
+ // We only support one buffer, but it's common for gvr apps to use more than
+ // one. Print an error to the log if the app requests more than one buffer,
+ // but continue on. We'll only render the first buffer in that case.
+ if (count > 1) {
+ ALOGE(
+ "Only one buffer is supported but the app requested %d."
+ " Only the first buffer will be rendered.",
+ count);
+ }
+
+ std::unique_ptr<gvr_swap_chain> swap_chain(new gvr_swap_chain(gvr));
+
+ // The first buffer gets a DvrGraphicsContext, which contains the surface we
+ // pass to displayd for rendering.
+ swap_chain->buffers_.push_back(gvr_buffer());
+ swap_chain->buffers_.back().spec = *buffers[0];
+ if (!CreateDvrGraphicsContextAndGvrBuffer(swap_chain.get()))
+ return nullptr;
+
+ // The rest of the buffers, which we don't render for now, get color render
+ // buffers.
+ for (int i = 1; i < count; ++i) {
+ swap_chain->buffers_.push_back(
+ gvr_buffer(gvr, *buffers[i], 0, GL_TEXTURE_2D));
+ if (swap_chain->buffers_.back().frame_buffer == 0)
+ return nullptr;
+ }
+
+ gvr->swap_chains_.push_back(swap_chain.get());
+ return swap_chain.release();
+}
+
+gvr_swap_chain_::~gvr_swap_chain_() {
+ if (context) {
+ auto iter = std::find(std::begin(context->swap_chains_),
+ std::end(context->swap_chains_), this);
+ if (iter != context->swap_chains_.end())
+ context->swap_chains_.erase(iter);
+ }
+ buffers_.clear();
+ if (graphics_context_ != nullptr)
+ dvrGraphicsContextDestroy(graphics_context_);
+}
+
+void gvr_swap_chain_destroy(gvr_swap_chain** swap_chain) {
+ if (!swap_chain || !(*swap_chain)) {
+ ALOGW("gvr_swap_chain_destroy: Invalid swap chain pointer.");
+ return;
+ }
+ delete *swap_chain;
+ *swap_chain = nullptr;
+}
+
+int32_t gvr_swap_chain_get_buffer_count(const gvr_swap_chain* swap_chain) {
+ return swap_chain ? static_cast<int32_t>(swap_chain->buffers_.size()) : 0;
+}
+
+gvr_sizei gvr_swap_chain_get_buffer_size(gvr_swap_chain* swap_chain,
+ int32_t index) {
+ if (!VerifyBufferIndex("gvr_swap_chain_get_buffer_size", swap_chain, index))
+ return gvr_sizei{0, 0};
+
+ gvr_buffer& buf = swap_chain->buffers_[index];
+ if (buf.requested_size != gvr_sizei{-1, -1})
+ return buf.requested_size;
+ else
+ return buf.spec.size;
+}
+
+void gvr_swap_chain_resize_buffer(gvr_swap_chain* swap_chain, int32_t index,
+ gvr_sizei size) {
+ if (!VerifyBufferIndex("gvr_swap_chain_resize_buffer", swap_chain, index))
+ return;
+
+ gvr_buffer& buf = swap_chain->buffers_[index];
+ if (size != buf.spec.size)
+ buf.requested_size = size;
+ else
+ buf.requested_size = {-1, -1};
+}
+
+gvr_frame* gvr_swap_chain_acquire_frame(gvr_swap_chain* swap_chain) {
+ if (!swap_chain)
+ return nullptr;
+
+ if (swap_chain->frame_acquired_) {
+ gvr_set_error(swap_chain->context, GVR_ERROR_NO_FRAME_AVAILABLE);
+ return nullptr;
+ }
+
+ // Resize buffers if necessary
+ for (int i = 0; i < static_cast<int>(swap_chain->buffers_.size()); ++i) {
+ gvr_buffer& buf = swap_chain->buffers_[i];
+ if (buf.requested_size != gvr_sizei{-1, -1}) {
+ if (!SwapChainResizeBuffer(swap_chain, i))
+ return nullptr;
+ }
+ }
+
+ // Only call gvr_wait_next_frame() if the app didn't call it already.
+ if (!swap_chain->wait_next_frame_called_by_app_)
+ WaitNextFrame(swap_chain, 0, nullptr, /*called_by_app*/ false);
+
+ int ret = dvrBeginRenderFrame(swap_chain->graphics_context_);
+ if (ret < 0) {
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return nullptr;
+ }
+
+ swap_chain->frame_acquired_ = true;
+ return GetFrameFromSwapChain(swap_chain);
+}
+
+void gvr_frame_bind_buffer(gvr_frame* frame, int32_t index) {
+ gvr_swap_chain* swap_chain = GetSwapChainForFrame(frame);
+ if (!VerifyBufferIndex("gvr_frame_bind_buffer", swap_chain, index))
+ return;
+ glBindFramebuffer(GL_FRAMEBUFFER, swap_chain->buffers_[index].frame_buffer);
+}
+
+void gvr_frame_unbind(gvr_frame* /* frame */) {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+gvr_sizei gvr_frame_get_buffer_size(const gvr_frame* frame, int32_t index) {
+ const gvr_swap_chain* swap_chain = GetSwapChainForFrame(frame);
+ if (!VerifyBufferIndex("gvr_frame_get_buffer_size", swap_chain, index))
+ return gvr_sizei{0, 0};
+ return swap_chain->buffers_[index].spec.size;
+}
+
+int32_t gvr_frame_get_framebuffer_object(const gvr_frame* frame,
+ int32_t index) {
+ const gvr_swap_chain* swap_chain = GetSwapChainForFrame(frame);
+ if (!VerifyBufferIndex("gvr_frame_get_framebuffer_object", swap_chain, index))
+ return 0;
+ return swap_chain->buffers_[index].frame_buffer;
+}
+
+void gvr_frame_submit(gvr_frame** frame, const gvr_buffer_viewport_list* list,
+ gvr_mat4f head_space_from_start_space) {
+ if (!frame)
+ return;
+
+ gvr_swap_chain* swap_chain = GetSwapChainForFrame(*frame);
+
+ if (!swap_chain->frame_acquired_) {
+ ALOGE("Frame was never acquired before being submitted");
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return;
+ }
+
+ *frame = nullptr;
+ swap_chain->frame_acquired_ = false;
+
+ // Currently, support for arbitrary buffer viewport configs is very limited.
+ // We assume that the first two viewports have to be the recommended color
+ // buffer viewports, followed by pairs of external external buffer viewports
+ // for video rendering.
+ gvr_buffer_viewport_list supported_viewports;
+ gvr_get_recommended_buffer_viewports(swap_chain->context,
+ &supported_viewports);
+ for (size_t i = 0; i < supported_viewports.viewports.size(); ++i) {
+ if (i >= list->viewports.size() ||
+ supported_viewports.viewports[i] != list->viewports[i]) {
+ ALOGE("Custom viewport configurations are not fully supported.");
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return;
+ }
+ }
+
+ for (size_t i = supported_viewports.viewports.size();
+ i < list->viewports.size(); ++i) {
+ int32_t external_surface_id = list->viewports[i].external_surface_id;
+ // Ignore additional custom buffer viewport for now, only those buffer
+ // viewports backed by external surfaces are supported.
+ // TODO(b/31442094, b/31771861, 28954457) Add full GVR buffer viewport
+ // support.
+ if (external_surface_id == GVR_EXTERNAL_SURFACE_ID_NONE)
+ continue;
+
+ auto surface_it = swap_chain->external_surfaces_.find(external_surface_id);
+ if (surface_it == swap_chain->external_surfaces_.end()) {
+ ALOGE("Cannot find external_surface by id: %d.", external_surface_id);
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return;
+ }
+
+ // Pass the transfrom matrix of video mesh to displayd.
+ dvrGraphicsVideoMeshSurfacePresent(
+ swap_chain->graphics_context_, surface_it->second->video_surface,
+ list->viewports[i].eye,
+ GvrToEigenMatrix(list->viewports[i].transform).data());
+ }
+
+ float32x4_t pose_orientation, pose_translation;
+ GvrToDvrPose(head_space_from_start_space, &pose_orientation,
+ &pose_translation);
+ int ret = dvrSetEdsPose(swap_chain->graphics_context_, pose_orientation,
+ pose_translation);
+ if (ret < 0)
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+
+ ret = dvrPresent(swap_chain->graphics_context_);
+ if (ret < 0) {
+ gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+ return;
+ }
+}
+
+void gvr_bind_default_framebuffer(gvr_context* /* gvr */) {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Head tracking
+/////////////////////////////////////////////////////////////////////////////
+
+gvr_clock_time_point gvr_get_time_point_now() {
+ return gvr_clock_time_point{GetSystemClockNs()};
+}
+
+gvr_mat4f gvr_get_head_space_from_start_space_rotation(
+ const gvr_context* gvr, const gvr_clock_time_point /* time */) {
+ // TODO(steventhomas): Implement prediction according to the supplied time
+ // value.
+ return gvr->force_6dof_ ? gvr->next_frame_6dof_pose_
+ : Gvr6dofTo3dof(gvr->next_frame_6dof_pose_);
+}
+
+gvr_mat4f gvr_apply_neck_model(const gvr_context* /* gvr */,
+ gvr_mat4f head_space_from_start_space_rotation,
+ float /* factor */) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_apply_neck_model not implemented.");
+ return head_space_from_start_space_rotation;
+}
+
+// This is used to turn off sensors to save power. Not relevant for our all in
+// one device.
+void gvr_pause_tracking(gvr_context* /* gvr */) {}
+
+// This is used to turn on sensors. Not relevant for our all in one device.
+void gvr_resume_tracking(gvr_context* /* gvr */) {}
+
+void gvr_reset_tracking(gvr_context* gvr) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_reset_tracking not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+void gvr_recenter_tracking(gvr_context* gvr) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_recenter_tracking not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Head mounted display
+/////////////////////////////////////////////////////////////////////////////
+
+bool gvr_set_default_viewer_profile(gvr_context* gvr,
+ const char* /* viewer_profile_uri */) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_set_default_viewer_profile not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ return false;
+}
+
+void gvr_refresh_viewer_profile(gvr_context* /* gvr */) {}
+
+const char* gvr_get_viewer_vendor(const gvr_context* /* gvr */) {
+ return kViewerVendor;
+}
+
+const char* gvr_get_viewer_model(const gvr_context* /* gvr */) {
+ return kViewerModel;
+}
+
+int32_t gvr_get_viewer_type(const gvr_context* /* gvr */) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ // In this case, we will probably need to define a new viewer type that
+ // has 6DoF support.
+ return GVR_VIEWER_TYPE_DAYDREAM;
+}
+
+gvr_mat4f gvr_get_eye_from_head_matrix(const gvr_context* gvr,
+ const int32_t eye) {
+ float eye_mult = eye == GVR_LEFT_EYE ? 1 : -1;
+ return GvrTranslationMatrix(
+ .5f * eye_mult * gvr->display_metrics_.inter_lens_distance_m, 0, 0);
+}
+
+gvr_recti gvr_get_window_bounds(const gvr_context* gvr) {
+ // Our app windows are always full screen
+ gvr_sizei screen_size = gvr_get_screen_target_size(gvr);
+ return gvr_recti{0, screen_size.width, 0, screen_size.height};
+}
+
+void gvr_compute_distorted_point(const gvr_context* /* gvr */,
+ const int32_t /* eye */,
+ const gvr_vec2f /* uv_in */,
+ gvr_vec2f /* uv_out */[3]) {
+ // TODO(leandrogracia): this needs to be properly implemented.
+ ALOGE("gvr_compute_distorted_point not implemented.");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// GVR API extension (from gvr_ext.h)
+/////////////////////////////////////////////////////////////////////////////
+
+gvr_frame_schedule* gvr_frame_schedule_create() {
+ return new gvr_frame_schedule;
+}
+
+void gvr_frame_schedule_destroy(gvr_frame_schedule** schedule) {
+ if (!schedule || !(*schedule)) {
+ ALOGW("gvr_frame_schedule_destroy: Invalid frame schedule pointer.");
+ return;
+ }
+ delete *schedule;
+ *schedule = nullptr;
+}
+
+uint32_t gvr_frame_schedule_get_vsync_count(gvr_frame_schedule* schedule) {
+ return schedule->vsync_count;
+}
+
+gvr_clock_time_point gvr_frame_schedule_get_scheduled_finish(
+ gvr_frame_schedule* schedule) {
+ return schedule->scheduled_finish;
+}
+
+void gvr_wait_next_frame(gvr_swap_chain* swap_chain, int64_t start_delay_nanos,
+ gvr_frame_schedule* out_next_frame_schedule) {
+ WaitNextFrame(swap_chain, start_delay_nanos, out_next_frame_schedule,
+ /*called_by_app*/ true);
+}
+
+gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
+ uint32_t vsync_count) {
+ DvrPoseAsync pose;
+ int ret = dvrPoseGet(gvr->pose_client_, vsync_count, &pose);
+ if (ret < 0) {
+ ALOGW("dvrPoseGet failed: %d", ret);
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ return GvrIdentityMatrix();
+ }
+
+ return PosefToGvrMatrix(ToPosef(pose));
+}
+
+gvr_mat4f gvr_get_head_space_from_start_space_pose(
+ gvr_context* gvr, const gvr_clock_time_point /* time */) {
+ // TODO(leandrogracia): implement prediction based on the provided time.
+ // We need to do the same for the 3dof version too.
+ return gvr->next_frame_6dof_pose_;
+}
+
+void gvr_swap_chain_set_z_order(const gvr_swap_chain* swap_chain, int z_order) {
+ dvrGraphicsSurfaceSetZOrder(swap_chain->graphics_context_, z_order);
+}
+
+bool gvr_experimental_is_feature_supported(const gvr_context* /* gvr */,
+ int32_t feature) {
+ switch (feature) {
+ case GVR_ASYNC_REPROJECTION:
+ case GVR_6DOF_HEAD_POSE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool gvr_experimental_register_perf_event_callback(
+ gvr_context* gvr, int* /* out_handle */, void* /* user_data */,
+ void (* /* event_callback */)(void*, int, float)) {
+ ALOGE("gvr_experimental_register_perf_event_callback not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ return false;
+}
+
+bool gvr_experimental_unregister_perf_event_callback(gvr_context* gvr,
+ int /* handle */) {
+ ALOGE("gvr_experimental_unregister_perf_event_callback not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ return false;
+}
+
+const gvr_analytics* gvr_get_analytics(gvr_context* gvr) {
+ ALOGE("gvr_get_analytics not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+ return nullptr;
+}
+
+const gvr_analytics_sample* gvr_analytics_create_sample(
+ const gvr_analytics* analytics) {
+ ALOGE("gvr_analytics_create_sample not implemented.");
+ return nullptr;
+}
+
+const char* gvr_analytics_sample_get_buffer(const gvr_analytics_sample* sample) {
+ ALOGE("gvr_analytics_sample_get_buffer not implemented.");
+ return nullptr;
+}
+
+size_t gvr_analytics_sample_get_buffer_length(
+ const gvr_analytics_sample* sample) {
+ ALOGE("gvr_analytics_sample_get_buffer_length not implemented.");
+ return 0;
+}
+
+void gvr_analytics_destroy_sample(const gvr_analytics_sample** sample) {
+ ALOGE("gvr_analytics_destroy_sample not implemented.");
+}
+
+bool gvr_user_prefs_get_performance_monitoring_enabled(
+ const gvr_user_prefs* /* user_prefs */) {
+ ALOGW("gvr_user_prefs_get_performance_monitoring_enabled not implemented.");
+ return false;
+}
+
+void gvr_enable_context_sharing(gvr_context* gvr,
+ gvr_egl_context_listener /* handler */,
+ void* /* user_data */) {
+ ALOGW("gvr_enable_context_sharing not implemented.");
+ gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+gvr_mat4f gvr_get_start_space_from_controller_space_pose(
+ gvr_context* gvr, int controller_id,
+ const gvr_clock_time_point /* time */) {
+ if (controller_id < 0 || controller_id >= kControllerCount) {
+ return GvrIdentityMatrix();
+ }
+
+ // TODO(leandrogracia): implement prediction based on the provided time.
+ // We need to do the same for the 3dof version too.
+ return gvr->next_frame_controller_pose_[controller_id];
+}
+
+gvr_external_surface* gvr_external_surface_create(gvr_context* context) {
+ // A |gvr_external_surface| is bound to a DVR Graphics context at the
+ // moment, which means we need an |gvr_swap_chain| created prior to the call
+ // of |gvr_external_surface_create|. Check whether the current GVR context
+ // has |gvr_swap_chain| created. Fail if there is no swap chain created
+ // already.
+ if (context->swap_chains_.empty()) {
+ ALOGE("gvr_external_surface_create: No swapchain has been created yet.");
+ return nullptr;
+ }
+
+ // In case there are multiple swap chains in the context, the first is
+ // implicitly chosen. Actually, this should not happen as current scanline
+ // racing based GVR implementation only supports single swap chain per GVR
+ // context.
+ if (context->swap_chains_.size() > 1) {
+ ALOGW("gvr_external_surface_create: Multiple swap chains detected. "
+ "Choosing the first one but this may yield unexpected results.");
+ }
+ gvr_swap_chain* swap_chain = context->swap_chains_[0];
+ DvrVideoMeshSurface* video_surface = dvrGraphicsVideoMeshSurfaceCreate(
+ swap_chain->graphics_context_);
+
+ if (video_surface == nullptr) {
+ ALOGE("gvr_external_surface_create: Failed to create video mesh surface.");
+ return nullptr;
+ }
+
+ gvr_external_surface* surface = new gvr_external_surface;
+ surface->id = swap_chain->next_external_surface_id_++;
+ surface->swap_chain = swap_chain;
+ surface->video_surface = video_surface;
+
+ // Insert the surface into a lookup table in swap_chain. This will be
+ // needed to by the external_surface_id in |gvr_buffer_viewport|.
+ swap_chain->external_surfaces_.insert({surface->id, surface});
+ return surface;
+}
+
+void gvr_external_surface_destroy(gvr_external_surface** surface) {
+ if (!surface || !(*surface)) {
+ ALOGW("gvr_external_surface_destroy: Invalid external surface pointer.");
+ return;
+ }
+
+ (*surface)->swap_chain->external_surfaces_.erase((*surface)->id);
+ if ((*surface)->video_surface != nullptr) {
+ dvrGraphicsVideoMeshSurfaceDestroy((*surface)->video_surface);
+ }
+
+ delete *surface;
+ *surface = nullptr;
+}
+
+void* gvr_external_surface_get_surface(const gvr_external_surface* surface) {
+ CHECK(surface->swap_chain != nullptr &&
+ surface->swap_chain->context != nullptr &&
+ surface->swap_chain->context->jni_env_ != nullptr)
+ << "gvr_external_surface_get_surface: Surface must be constructed within "
+ << "a JNIEnv. Check |gvr_create| call.";
+
+ CHECK(surface->video_surface != nullptr)
+ << "gvr_external_surface_get_surface: Invalid surface.";
+
+ std::shared_ptr<android::dvr::ProducerQueue> producer_queue =
+ surface->video_surface->client->GetProducerQueue();
+ std::shared_ptr<android::dvr::BufferHubQueueCore> core =
+ android::dvr::BufferHubQueueCore::Create(producer_queue);
+
+ return android_view_Surface_createFromIGraphicBufferProducer(
+ surface->swap_chain->context->jni_env_,
+ new android::dvr::BufferHubQueueProducer(core));
+}
+
+int32_t gvr_external_surface_get_surface_id(
+ const gvr_external_surface* surface) {
+ return surface->id;
+}
diff --git a/libs/vr/libgvr/shim_gvr_controller.cpp b/libs/vr/libgvr/shim_gvr_controller.cpp
new file mode 100644
index 0000000..54bc270
--- /dev/null
+++ b/libs/vr/libgvr/shim_gvr_controller.cpp
@@ -0,0 +1,168 @@
+#define LOG_TAG "libgvr_controller_shim"
+
+#include <cutils/log.h>
+#include <vr/gvr/capi/include/gvr_controller.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+
+gvr_controller_context* gvr_controller_create_and_init(int32_t options,
+ gvr_context* context) {
+ ALOGE("gvr_controller_create_and_init not implemented.");
+ return nullptr;
+}
+
+gvr_controller_context* gvr_controller_create_and_init_android(
+ JNIEnv* env, jobject android_context, jobject class_loader, int32_t options,
+ gvr_context* context) {
+ ALOGE("gvr_controller_create_and_init_android not implemented.");
+ return nullptr;
+}
+
+void gvr_controller_destroy(gvr_controller_context** api) {
+ ALOGE("gvr_controller_destroy not implemented.");
+}
+
+gvr_controller_state* gvr_controller_state_create() {
+ ALOGE("gvr_controller_state_create not implemented.");
+ return nullptr;
+}
+
+void gvr_controller_state_destroy(gvr_controller_state** state) {
+ ALOGE("gvr_controller_state_destroy not implemented.");
+}
+
+void gvr_controller_state_update(gvr_controller_context* api, int32_t flags,
+ gvr_controller_state* out_state) {
+ ALOGE("gvr_controller_state_update not implemented.");
+}
+
+int64_t gvr_controller_state_get_last_button_timestamp(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_last_button_timestamp not implemented.");
+ return 0;
+}
+
+bool gvr_controller_state_get_button_state(const gvr_controller_state* state,
+ int32_t button) {
+ ALOGE("gvr_controller_state_get_button_state not implemented.");
+ return false;
+}
+
+bool gvr_controller_state_get_button_down(const gvr_controller_state* state,
+ int32_t button) {
+ ALOGE("gvr_controller_state_get_button_down not implemented.");
+ return false;
+}
+
+bool gvr_controller_state_get_button_up(const gvr_controller_state* state,
+ int32_t button) {
+ ALOGE("gvr_controller_state_get_button_up not implemented.");
+ return false;
+}
+
+bool gvr_controller_state_is_touching(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_is_touching not implemented.");
+ return false;
+}
+
+gvr_vec2f gvr_controller_state_get_touch_pos(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_touch_pos not implemented.");
+ return {0.0f, 0.0f};
+}
+
+bool gvr_controller_state_get_touch_down(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_touch_down not implemented.");
+ return false;
+}
+
+bool gvr_controller_state_get_touch_up(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_touch_up not implemented.");
+ return false;
+}
+
+int64_t gvr_controller_state_get_last_touch_timestamp(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_last_touch_timestamp not implemented.");
+ return 0;
+}
+
+gvr_quatf gvr_controller_state_get_orientation(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_orientation not implemented.");
+ return {0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+int64_t gvr_controller_state_get_last_orientation_timestamp(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_last_orientation_timestamp not implemented.");
+ return 0;
+}
+
+const char* gvr_controller_api_status_to_string(int32_t status) {
+ ALOGE("gvr_controller_api_status_to_string not implemented.");
+ return nullptr;
+}
+
+const char* gvr_controller_connection_state_to_string(int32_t state) {
+ ALOGE("gvr_controller_connection_state_to_string not implemented.");
+ return nullptr;
+}
+
+const char* gvr_controller_button_to_string(int32_t button) {
+ ALOGE("gvr_controller_button_to_string not implemented.");
+ return nullptr;
+}
+
+int32_t gvr_controller_get_default_options() {
+ ALOGE("gvr_controller_get_default_options not implemented.");
+ return 0;
+}
+
+void gvr_controller_pause(gvr_controller_context* api) {
+ ALOGE("gvr_controller_pause not implemented.");
+}
+
+void gvr_controller_resume(gvr_controller_context* api) {
+ ALOGE("gvr_controller_resume not implemented.");
+}
+
+int32_t gvr_controller_state_get_api_status(const gvr_controller_state* state) {
+ return GVR_CONTROLLER_API_OK;
+}
+
+int32_t gvr_controller_state_get_connection_state(
+ const gvr_controller_state* state) {
+ return GVR_CONTROLLER_CONNECTED;
+}
+
+gvr_vec3f gvr_controller_state_get_gyro(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_gyro not implemented.");
+ return {0.0, 0.0, 0.0};
+}
+
+gvr_vec3f gvr_controller_state_get_accel(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_accel not implemented.");
+ return {0.0, 0.0, 0.0};
+}
+
+int64_t gvr_controller_state_get_last_gyro_timestamp(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_last_gyro_timestamp not implemented.");
+ return 0;
+}
+
+int64_t gvr_controller_state_get_last_accel_timestamp(
+ const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_last_accel_timestamp not implemented.");
+ return 0;
+}
+
+bool gvr_controller_state_get_recentered(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_recentered not implemented.");
+ return false;
+}
+
+bool gvr_controller_state_get_recentering(const gvr_controller_state* state) {
+ ALOGE("gvr_controller_state_get_recentering not implemented.");
+ return false;
+}
diff --git a/libs/vr/libgvr/shim_gvr_private.cpp b/libs/vr/libgvr/shim_gvr_private.cpp
new file mode 100644
index 0000000..6ab6971
--- /dev/null
+++ b/libs/vr/libgvr/shim_gvr_private.cpp
@@ -0,0 +1,234 @@
+#define LOG_TAG "libgvr_shim_private"
+
+#include <cutils/log.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/internal_types.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/src/gvr_private.h>
+
+#include <pdx/rpc/remote_method.h>
+#include "deviceparams/CardboardDevice.nolite.pb.h"
+
+bool gvr_set_async_reprojection_enabled(gvr_context* /* gvr */,
+ bool /* enabled */) {
+ return true;
+}
+
+void gvr_on_surface_created_reprojection_thread(gvr_context* /* gvr */) {}
+
+void gvr_render_reprojection_thread(gvr_context* /* gvr */) {}
+
+void gvr_on_pause_reprojection_thread(gvr_context* /* gvr */) {}
+
+void gvr_update_surface_reprojection_thread(
+ gvr_context* /* gvr */, int32_t /* surface_id */, int32_t /* texture_id */,
+ gvr_clock_time_point /* timestamp */, gvr_mat4f /* surface_transform */) {
+ ALOGE("gvr_update_surface_reprojection_thread not implemented");
+}
+
+void gvr_remove_all_surfaces_reprojection_thread(gvr_context* /* gvr */) {
+ ALOGE("gvr_remove_all_surfaces_reprojection_thread not implemented");
+}
+
+void gvr_reconnect_sensors(gvr_context* /* gvr */) {
+ ALOGE("gvr_reconnect_sensors not implemented");
+}
+
+bool gvr_set_viewer_params(gvr_context* gvr,
+ const void* serialized_viewer_params,
+ size_t serialized_viewer_params_size_bytes) {
+ std::string serialized_device_params_string(
+ reinterpret_cast<const char*>(serialized_viewer_params),
+ serialized_viewer_params_size_bytes);
+ std::unique_ptr<proto::DeviceParams> device_params(new proto::DeviceParams);
+ if (!device_params->ParseFromString(serialized_device_params_string)) {
+ LOG(ERROR) << "Invalid serialized Cardboard DeviceParams";
+ return false;
+ }
+
+ android::dvr::ViewerParams viewer_params;
+
+ viewer_params.screen_to_lens_distance =
+ device_params->screen_to_lens_distance();
+ viewer_params.inter_lens_distance = device_params->inter_lens_distance();
+ for (int i = 0; i < device_params->left_eye_field_of_view_angles_size();
+ ++i) {
+ viewer_params.left_eye_field_of_view_angles.push_back(
+ device_params->left_eye_field_of_view_angles(i));
+ }
+
+ viewer_params.vertical_alignment =
+ static_cast<android::dvr::ViewerParams::VerticalAlignmentType>(
+ device_params->vertical_alignment());
+ viewer_params.tray_to_lens_distance = device_params->tray_to_lens_distance();
+
+ // TODO(hendrikw) Leave the g and b coefficients empty until we support
+ // chromatic aberration correction.
+ for (int i = 0; i < device_params->distortion_coefficients_size(); ++i) {
+ viewer_params.distortion_coefficients_r.push_back(
+ device_params->distortion_coefficients(i));
+ }
+
+ viewer_params.screen_center_to_lens_distance =
+ viewer_params.inter_lens_distance / 2.0;
+ if (device_params->has_internal()) {
+ for (int i = 0; i < device_params->internal().eye_orientations_size();
+ ++i) {
+ viewer_params.eye_orientations.push_back(
+ static_cast<android::dvr::ViewerParams::EyeOrientation>(
+ device_params->internal().eye_orientations(i)));
+ }
+
+ if (device_params->internal().has_screen_center_to_lens_distance())
+ viewer_params.screen_center_to_lens_distance =
+ device_params->internal().screen_center_to_lens_distance();
+ }
+
+ if (device_params->has_daydream_internal()) {
+ viewer_params.daydream_internal.version =
+ device_params->daydream_internal().version();
+ for (int i = 0;
+ i < device_params->daydream_internal().alignment_markers_size(); ++i) {
+ viewer_params.daydream_internal.alignment_markers.push_back(
+ {device_params->daydream_internal().alignment_markers(i).horizontal(),
+ device_params->daydream_internal().alignment_markers(i).vertical()});
+ }
+ }
+
+ gvr->display_client_->SetViewerParams(viewer_params);
+ return true;
+}
+
+void gvr_set_lens_offset(gvr_context* /* gvr */, gvr_vec2f /* offset */) {
+ ALOGE("gvr_set_lens_offset not implemented");
+}
+
+void gvr_set_display_metrics(gvr_context* /* gvr */,
+ gvr_sizei /* size_pixels */,
+ gvr_vec2f /* meters_per_pixel */,
+ float /* border_size_meters */) {
+ ALOGE("gvr_set_display_metrics not implemented");
+}
+
+void gvr_set_display_output_rotation(gvr_context* /* gvr */,
+ int /* display_output_rotation */) {
+ ALOGE("gvr_set_display_output_rotation not implemented");
+}
+
+float gvr_get_border_size_meters(const gvr_context* /* gvr */) {
+ ALOGE("gvr_get_border_size_meters not implemented");
+ return 0.0f;
+}
+
+bool gvr_check_surface_size_changed(gvr_context* /* gvr */) { return false; }
+
+gvr_sizei gvr_get_surface_size(const gvr_context* /* gvr */) {
+ ALOGE("gvr_get_surface_size not implemented");
+ return {0, 0};
+}
+
+void gvr_set_back_gesture_event_handler(gvr_context* /* gvr */,
+ event_handler /* handler */,
+ void* /* user_data */) {
+ ALOGE("gvr_set_back_gesture_event_handler not implemented");
+}
+
+gvr_tracker_state* gvr_pause_tracking_get_state(gvr_context* /* gvr */) {
+ ALOGE("gvr_pause_tracking_get_state not implemented");
+ return nullptr;
+}
+
+void gvr_resume_tracking_set_state(gvr_context* /* gvr */,
+ gvr_tracker_state* /* tracker_state */) {
+ ALOGE("gvr_resume_tracking_set_state not implemented");
+}
+
+void gvr_set_ignore_manual_tracker_pause_resume(gvr_context* /* gvr */,
+ bool /* should_ignore */) {
+ ALOGE("gvr_set_ignore_manual_tracker_pause_resume not implemented");
+}
+
+gvr_tracker_state* gvr_tracker_state_create(
+ const char* /* tracker_state_buffer */, size_t /* buf_size */) {
+ ALOGE("gvr_tracker_state_create not implemented");
+ return nullptr;
+}
+
+size_t gvr_tracker_state_get_buffer_size(
+ gvr_tracker_state* /* tracker_state */) {
+ ALOGE("gvr_tracker_state_get_buffer_size not implemented");
+ return 0;
+}
+
+const char* gvr_tracker_state_get_buffer(
+ gvr_tracker_state* /* tracker_state */) {
+ ALOGE("gvr_tracker_state_get_buffer not implemented");
+ return nullptr;
+}
+
+void gvr_tracker_state_destroy(gvr_tracker_state** /* tracker_state */) {
+ ALOGE("gvr_tracker_state_destroy not implemented");
+}
+
+gvr_display_synchronizer* gvr_display_synchronizer_create() {
+ // We don't actually support (or need) any of the synchronizer functionality,
+ // but if we return null here the gvr setup code in the app fails. Instead
+ // return a dummy object that does nothing, which allows gvr apps to work.
+ return new gvr_display_synchronizer;
+}
+
+void gvr_display_synchronizer_destroy(gvr_display_synchronizer** synchronizer) {
+ if (synchronizer) {
+ delete *synchronizer;
+ *synchronizer = nullptr;
+ }
+}
+
+void gvr_display_synchronizer_reset(
+ gvr_display_synchronizer* /* synchronizer */,
+ int64_t /* expected_interval_nanos */, int64_t /* vsync_offset_nanos */) {}
+
+void gvr_display_synchronizer_update(
+ gvr_display_synchronizer* /* synchronizer */,
+ gvr_clock_time_point /* vsync_time */, int32_t /* rotation */) {}
+
+void gvr_set_display_synchronizer(
+ gvr_context* /* gvr */, gvr_display_synchronizer* /* synchronizer */) {}
+
+void gvr_set_error(gvr_context* gvr, int32_t error_code) {
+ if (gvr->last_error_ != GVR_ERROR_NONE) {
+ ALOGW("Overwriting existing error code: %d (%s)", gvr->last_error_,
+ gvr_get_error_string(gvr->last_error_));
+ }
+ gvr->last_error_ = error_code;
+}
+
+void gvr_pause(gvr_context* gvr) {
+ if (gvr == nullptr) {
+ ALOGW("gvr_pause called with a null gvr_context. This is a bug.");
+ return;
+ }
+ for (gvr_swap_chain* swap_chain : gvr->swap_chains_) {
+ if (swap_chain->graphics_context_)
+ dvrGraphicsSurfaceSetVisible(swap_chain->graphics_context_, 0);
+ }
+}
+
+void gvr_resume(gvr_context* gvr) {
+ if (gvr == nullptr) {
+ ALOGW("gvr_resume called with a null gvr_context. This is a bug.");
+ return;
+ }
+ for (gvr_swap_chain* swap_chain : gvr->swap_chains_) {
+ if (swap_chain->graphics_context_)
+ dvrGraphicsSurfaceSetVisible(swap_chain->graphics_context_, 1);
+ }
+}
+
+void gvr_dump_debug_data(gvr_context* /* gvr */) {}
+
+bool gvr_using_vr_display_service(gvr_context* /* gvr */) { return true; }
+
+void gvr_request_context_sharing(gvr_context* /* gvr */,
+ gvr_egl_context_listener /* handler */,
+ void* /* user_data */) {}
diff --git a/libs/vr/libimageio/Android.mk b/libs/vr/libimageio/Android.mk
new file mode 100644
index 0000000..b3b88ac
--- /dev/null
+++ b/libs/vr/libimageio/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ image_io.cpp \
+ image_io_png.cpp \
+ image_io_ppm.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+sharedLibraries := \
+ libcutils \
+ libpng
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES += $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_CFLAGS := -Wall -Wextra
+LOCAL_MODULE := libimageio
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/vr/libimageio/image_io.cpp b/libs/vr/libimageio/image_io.cpp
new file mode 100644
index 0000000..5ad6c2d
--- /dev/null
+++ b/libs/vr/libimageio/image_io.cpp
@@ -0,0 +1,92 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include <private/dvr/image_io_base.h>
+#include <private/dvr/image_io_logging.h>
+#include <private/dvr/image_io_png.h>
+#include <private/dvr/image_io_ppm.h>
+
+namespace {
+
+// Returns true if |str| ends with |suffix|.
+bool EndsWith(const std::string& str, const std::string& suffix) {
+ if (str.length() < suffix.length())
+ return false;
+
+ return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
+}
+
+// Returns lower case copy of the input string.
+std::string ToLower(std::string str) {
+ std::transform(str.begin(), str.end(), str.begin(),
+ [](char x) { return std::tolower(x); });
+ return str;
+}
+
+} // namespace
+
+std::unique_ptr<ImageIoReader> ImageIoReader::Create(const char* filename) {
+ std::unique_ptr<ImageIoReader> reader;
+ std::string filename_lower = ToLower(filename);
+
+ if (EndsWith(filename_lower, ".ppm"))
+ reader.reset(new ImageIoPpmReader(filename));
+
+ if (!reader) {
+ ALOGE("Unknown/unsupported image file format.");
+ return nullptr;
+ }
+
+ return reader;
+}
+
+std::unique_ptr<ImageIoWriter> ImageIoWriter::Create(const char* filename,
+ int width, int height,
+ const uint8_t* image) {
+ std::unique_ptr<ImageIoWriter> writer;
+ std::string filename_lower = ToLower(filename);
+
+ if (EndsWith(filename_lower, ".ppm"))
+ writer.reset(new ImageIoPpmWriter(filename, width, height, image));
+ else if (EndsWith(filename_lower, ".png"))
+ writer.reset(new ImageIoPngWriter(filename, width, height, image));
+
+ if (!writer) {
+ ALOGE("Unknown/unsupported image file format.");
+ return nullptr;
+ }
+
+ return writer;
+}
+
+extern "C" {
+
+bool image_io_write_rgb888(const char* filename, int width, int height,
+ const uint8_t* image) {
+ auto writer = ImageIoWriter::Create(filename, width, height, image);
+ if (!writer)
+ return false;
+ return writer->WriteRgb888();
+}
+
+bool image_io_read_rgb888(const char* filename, int* width, int* height,
+ uint8_t** image) {
+ auto reader = ImageIoReader::Create(filename);
+ if (!reader)
+ return false;
+ if (!reader->ReadRgb888())
+ return false;
+ *width = reader->width();
+ *height = reader->height();
+ *image = reader->ReleaseImage();
+ return true;
+}
+
+void image_io_release_buffer(uint8_t* image) { delete[] image; }
+
+} // extern "C"
diff --git a/libs/vr/libimageio/image_io_png.cpp b/libs/vr/libimageio/image_io_png.cpp
new file mode 100644
index 0000000..e0a118b
--- /dev/null
+++ b/libs/vr/libimageio/image_io_png.cpp
@@ -0,0 +1,87 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io_png.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <private/dvr/image_io_logging.h>
+
+#include "png.h"
+
+namespace {
+
+void WriteChunkCallback(png_structp out_ptr, png_bytep chunk_ptr,
+ png_size_t chunk_size) {
+ auto* writer = static_cast<ImageIoPngWriter*>(png_get_io_ptr(out_ptr));
+ const char* chunk = reinterpret_cast<const char*>(chunk_ptr);
+ writer->WriteChunk(chunk, chunk_size);
+}
+
+} // namespace
+
+ImageIoPngWriter::ImageIoPngWriter(const char* filename, int width, int height,
+ const uint8_t* image)
+ : ImageIoWriter(filename, width, height, image),
+ out_(filename_),
+ write_failed_(false) {}
+
+bool ImageIoPngWriter::WriteChunk(const char* chunk, int chunk_size) {
+ out_.write(chunk, chunk_size);
+ if (!out_) {
+ if (write_failed_) {
+ // Error was already logged once.
+ return false;
+ }
+
+ ALOGE("Failed to write .png image to %s.", filename_.c_str());
+ write_failed_ = true;
+ return false;
+ }
+ return true;
+}
+
+// Writes RGB888 image to png file.
+// Refactored from Chromium:
+// WebKit/Source/platform/image-encoders/skia/PNGImageEncoder.cpp
+bool ImageIoPngWriter::WriteRgb888() {
+ if (width_ <= 0 || height_ <= 0) {
+ ALOGE("Invalid width or height.");
+ return false;
+ }
+
+ if (!out_) {
+ ALOGE("Failed to open output file %s.", filename_.c_str());
+ return false;
+ }
+
+ png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ png_info* info = png_create_info_struct(png);
+ if (!png || !info || setjmp(png_jmpbuf(png))) {
+ png_destroy_write_struct(png ? &png : 0, info ? &info : 0);
+ return false;
+ }
+
+ png_set_compression_level(png, 3);
+ png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
+
+ png_set_write_fn(png, this, WriteChunkCallback, 0);
+ png_set_IHDR(png, info, width_, height_, 8, PNG_COLOR_TYPE_RGB, 0, 0, 0);
+ png_write_info(png, info);
+
+ unsigned char* pixels =
+ reinterpret_cast<unsigned char*>(const_cast<uint8_t*>(image_));
+ const size_t stride = width_ * 3;
+ for (int y = 0; y < height_; ++y) {
+ png_write_row(png, pixels);
+ if (write_failed_)
+ return false;
+ pixels += stride;
+ }
+
+ png_write_end(png, info);
+ png_destroy_write_struct(&png, &info);
+
+ return !write_failed_;
+}
diff --git a/libs/vr/libimageio/image_io_ppm.cpp b/libs/vr/libimageio/image_io_ppm.cpp
new file mode 100644
index 0000000..2411888
--- /dev/null
+++ b/libs/vr/libimageio/image_io_ppm.cpp
@@ -0,0 +1,93 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io_ppm.h>
+
+#include <cwctype>
+#include <fstream>
+#include <string>
+
+#include <private/dvr/image_io_logging.h>
+
+bool ImageIoPpmWriter::WriteRgb888() {
+ std::ofstream out(filename_);
+ if (!out) {
+ ALOGE("Failed to open output file %s.", filename_.c_str());
+ return false;
+ }
+
+ // Write a PPM header. See http://netpbm.sourceforge.net/doc/ppm.html for
+ // the format specification.
+ constexpr int maximum_intensity = 255;
+ out << "P6\n"
+ << width_ << "\n"
+ << height_ << "\n"
+ << maximum_intensity << "\n";
+
+ // Write out the image itself.
+ out.write(reinterpret_cast<const char*>(image_), 3 * width_ * height_);
+
+ if (!out) {
+ ALOGE("Failed to write .ppm image to %s.", filename_.c_str());
+ return false;
+ }
+ return true;
+}
+
+bool ImageIoPpmReader::ReadRgb888() {
+ std::ifstream in(filename_);
+ if (!in) {
+ ALOGE("Failed to open input file %s.", filename_.c_str());
+ return false;
+ }
+
+ // Read PPM header. See http://netpbm.sourceforge.net/doc/ppm.html for
+ // the format specification.
+ char magic_number[2];
+ in.read(magic_number, 2);
+ if (magic_number[0] != 'P' || magic_number[1] != '6') {
+ ALOGE("Failed to read PPM, not a P6 file %s.", filename_.c_str());
+ return false;
+ }
+
+ int maximum_intensity = 0;
+
+ in >> width_;
+ in >> height_;
+ in >> maximum_intensity;
+
+ char delimiter;
+ in.read(&delimiter, 1);
+
+ if (!iswspace(delimiter) || width_ <= 0 || height_ <= 0 ||
+ maximum_intensity <= 0) {
+ ALOGE("Failed to parse PPM header for %s.", filename_.c_str());
+ return false;
+ }
+
+ if (maximum_intensity != 255) {
+ ALOGE("Failed to read PPM, only 8-bit depth supported %s.",
+ filename_.c_str());
+ return false;
+ }
+
+ // Read RGB data.
+ const int data_begin = in.tellg();
+ in.seekg(0, in.end);
+ const int data_end = in.tellg();
+ in.seekg(data_begin, in.beg);
+
+ const int data_size = data_end - data_begin;
+ if (data_size != 3 * width_ * height_) {
+ ALOGE("Failed to read PPM, unexpected data size %s.", filename_.c_str());
+ return false;
+ }
+
+ image_.reset(new uint8_t[data_size]);
+ char* data = reinterpret_cast<char*>(image_.get());
+
+ const auto it_data_begin = std::istreambuf_iterator<char>(in);
+ const auto it_data_end = std::istreambuf_iterator<char>();
+ std::copy(it_data_begin, it_data_end, data);
+
+ return true;
+}
diff --git a/libs/vr/libimageio/include/CPPLINT.cfg b/libs/vr/libimageio/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libimageio/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libimageio/include/private/dvr/image_io.h b/libs/vr/libimageio/include/private/dvr/image_io.h
new file mode 100644
index 0000000..5cb115d
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io.h
@@ -0,0 +1,32 @@
+#ifndef DVR_IMAGE_IO_H_
+#define DVR_IMAGE_IO_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+// Supported filetypes.
+#define DVR_IMAGE_IO_SUPPORTED_WRITE "png, ppm"
+#define DVR_IMAGE_IO_SUPPORTED_READ "ppm"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Writes an RGB888 image to file. Intended file type is autodetected
+// based on the extension. Currently supported formats: PNG, PPM.
+bool image_io_write_rgb888(const char* filename, int width, int height,
+ const uint8_t* image);
+
+// Reads an RGB888 image from file. Image buffer needs to be released with
+// image_io_release_image. Currently supported formats: PPM.
+bool image_io_read_rgb888(const char* filename, int* width, int* height,
+ uint8_t** image);
+
+// Releases image buffer allocated within the library.
+void image_io_release_buffer(uint8_t* image);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // DVR_IMAGE_IO_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_base.h b/libs/vr/libimageio/include/private/dvr/image_io_base.h
new file mode 100644
index 0000000..009cad4
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_base.h
@@ -0,0 +1,56 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
+
+#include <memory>
+#include <string>
+
+class ImageIoReader {
+ public:
+ virtual ~ImageIoReader() {}
+
+ static std::unique_ptr<ImageIoReader> Create(const char* filename);
+
+ virtual bool ReadRgb888() = 0;
+
+ int width() const { return width_; }
+
+ int height() const { return height_; }
+
+ uint8_t* ReleaseImage() { return image_.release(); }
+
+ protected:
+ int width_;
+ int height_;
+ std::unique_ptr<uint8_t[]> image_;
+ const std::string filename_;
+
+ explicit ImageIoReader(const char* filename)
+ : width_(0), height_(0), filename_(filename) {}
+
+ ImageIoReader() = delete;
+};
+
+class ImageIoWriter {
+ public:
+ virtual ~ImageIoWriter() {}
+
+ static std::unique_ptr<ImageIoWriter> Create(const char* filename, int width,
+ int height,
+ const uint8_t* image);
+
+ virtual bool WriteRgb888() = 0;
+
+ protected:
+ const int width_;
+ const int height_;
+ const uint8_t* image_;
+ const std::string filename_;
+
+ ImageIoWriter(const char* filename, int width, int height,
+ const uint8_t* image)
+ : width_(width), height_(height), image_(image), filename_(filename) {}
+
+ ImageIoWriter() = delete;
+};
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_logging.h b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
new file mode 100644
index 0000000..3c0f2a5
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
@@ -0,0 +1,39 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
+
+// This header acts as cutils/log.h if LOG_TO_STDERR is not defined.
+// If LOG_TO_STDERR is defined, then android logging macros (such as ALOGE)
+// would log to stderr. This is useful if the code is also being used/tested on
+// a desktop.
+
+#ifdef LOG_TO_STDERR
+#include <stdarg.h>
+#include <cstdio>
+
+#ifndef LOG_TAG
+#define LOG_TAG " "
+#endif // LOG_TAG
+
+inline void LogToStderr(const char* severity, const char* fmt, ...) {
+ fprintf(stderr, "%s %s: ", LOG_TAG, severity);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+#define ALOGE(fmt, ...) LogToStderr("ERROR", fmt, ##__VA_ARGS__)
+
+#define ALOGW(fmt, ...) LogToStderr("WARNING", fmt, ##__VA_ARGS__)
+
+#define ALOGI(fmt, ...) LogToStderr("INFO", fmt, ##__VA_ARGS__)
+
+#define ALOGV(fmt, ...) LogToStderr("VERBOSE", fmt, ##__VA_ARGS__)
+
+#else // LOG_TO_STDERR
+#include <cutils/log.h>
+#endif // LOG_TO_STDERR
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_png.h b/libs/vr/libimageio/include/private/dvr/image_io_png.h
new file mode 100644
index 0000000..e3b19db
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_png.h
@@ -0,0 +1,24 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
+
+#include <fstream>
+
+#include <private/dvr/image_io_base.h>
+
+class ImageIoPngWriter : public ImageIoWriter {
+ public:
+ bool WriteRgb888() override;
+
+ bool WriteChunk(const char* chunk, int chunk_size);
+
+ private:
+ ImageIoPngWriter(const char* filename, int width, int height,
+ const uint8_t* image);
+
+ std::ofstream out_;
+ bool write_failed_;
+
+ friend class ImageIoWriter;
+};
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_ppm.h b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
new file mode 100644
index 0000000..00264bd
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
@@ -0,0 +1,28 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_
+
+#include <private/dvr/image_io_base.h>
+
+class ImageIoPpmReader : public ImageIoReader {
+ public:
+ bool ReadRgb888() override;
+
+ private:
+ explicit ImageIoPpmReader(const char* filename) : ImageIoReader(filename) {}
+
+ friend class ImageIoReader;
+};
+
+class ImageIoPpmWriter : public ImageIoWriter {
+ public:
+ bool WriteRgb888() override;
+
+ private:
+ ImageIoPpmWriter(const char* filename, int width, int height,
+ const uint8_t* image)
+ : ImageIoWriter(filename, width, height, image) {}
+
+ friend class ImageIoWriter;
+};
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
new file mode 100644
index 0000000..69300d6
--- /dev/null
+++ b/libs/vr/libpdx/Android.bp
@@ -0,0 +1,59 @@
+cc_library_static {
+ name: "libpdx",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ export_include_dirs: ["private"],
+ local_include_dirs: ["private"],
+ srcs: [
+ "client.cpp",
+ "service.cpp",
+ "status.cpp",
+ ],
+}
+
+cc_test {
+ name: "pdx_tests",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "client_tests.cpp",
+ "mock_tests.cpp",
+ "serialization_tests.cpp",
+ "service_tests.cpp",
+ "status_tests.cpp",
+ "thread_local_buffer_tests.cpp",
+ "variant_tests.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libpdx",
+ "liblog",
+ "libutils",
+ ],
+}
+
+// Code analysis target.
+cc_test {
+ name: "pdx_encoder_performance_test",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-O2",
+ ],
+ srcs: [
+ "encoder_performance_test.cpp",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+}
diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp
new file mode 100644
index 0000000..c318628
--- /dev/null
+++ b/libs/vr/libpdx/client.cpp
@@ -0,0 +1,290 @@
+#include "pdx/client.h"
+
+#define LOG_TAG "ServiceFramework"
+#include <log/log.h>
+
+#include <pdx/trace.h>
+#include "errno_guard.h"
+
+namespace android {
+namespace pdx {
+
+void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) {
+ if (channel_factory_) {
+ reconnect_timeout_ms_ = reconnect_timeout_ms;
+ auto_reconnect_enabled_ = true;
+ }
+}
+
+void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; }
+
+bool Client::IsConnected() const { return channel_.get() != nullptr; }
+
+Status<void> Client::CheckReconnect() {
+ Status<void> ret;
+ bool was_disconnected = !IsConnected();
+ if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) {
+ auto status = channel_factory_->Connect(reconnect_timeout_ms_);
+ if (!status) {
+ error_ = -status.error();
+ ret.SetError(status.error());
+ return ret;
+ }
+ channel_ = status.take();
+ }
+
+ if (!IsConnected()) {
+ ret.SetError(ESHUTDOWN);
+ } else {
+ // Call the subclass OnConnect handler. The subclass may choose to close the
+ // connection in the handler, in which case error_ will be non-zero.
+ if (was_disconnected)
+ OnConnect();
+ if (!IsConnected())
+ ret.SetError(-error_);
+ else
+ ret.SetValue();
+ }
+
+ return ret;
+}
+
+bool Client::NeedToDisconnectChannel(int error) const {
+ return error == ESHUTDOWN && auto_reconnect_enabled_;
+}
+
+void Client::CheckDisconnect(int error) {
+ if (NeedToDisconnectChannel(error))
+ Close(error);
+}
+
+Client::Client(std::unique_ptr<ClientChannel> channel)
+ : channel_{std::move(channel)} {}
+
+Client::Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms)
+ : channel_factory_{std::move(channel_factory)} {
+ auto status = channel_factory_->Connect(timeout_ms);
+ if (!status) {
+ ALOGE("Client::Client: Failed to connect to service because: %s",
+ status.GetErrorMessage().c_str());
+ error_ = -status.error();
+ } else {
+ channel_ = status.take();
+ }
+}
+
+bool Client::IsInitialized() const {
+ return IsConnected() || (channel_factory_ && auto_reconnect_enabled_);
+}
+
+void Client::OnConnect() {}
+
+int Client::error() const { return error_; }
+
+Status<void> Client::SendImpulse(int opcode) {
+ PDX_TRACE_NAME("Client::SendImpulse");
+ ErrnoGuard errno_guard;
+
+ auto status = CheckReconnect();
+ if (!status)
+ return status;
+
+ status = channel_->SendImpulse(opcode, nullptr, 0);
+ CheckDisconnect(status);
+ return status;
+}
+
+Status<void> Client::SendImpulse(int opcode, const void* buffer,
+ size_t length) {
+ PDX_TRACE_NAME("Client::SendImpulse");
+ ErrnoGuard errno_guard;
+
+ auto status = CheckReconnect();
+ if (!status)
+ return status;
+
+ status = channel_->SendImpulse(opcode, buffer, length);
+ CheckDisconnect(status);
+ return status;
+}
+
+void Client::Close(int error) {
+ ErrnoGuard errno_guard;
+ channel_.reset();
+ // Normalize error codes to negative integer space.
+ error_ = error <= 0 ? error : -error;
+}
+
+int Client::event_fd() const {
+ return IsConnected() ? channel_->event_fd() : -1;
+}
+
+LocalChannelHandle& Client::GetChannelHandle() {
+ return channel_->GetChannelHandle();
+}
+
+///////////////////////////// Transaction implementation //////////////////////
+
+Transaction::Transaction(Client& client) : client_{client} {}
+
+Transaction::~Transaction() {
+ if (state_allocated_ && client_.GetChannel())
+ client_.GetChannel()->FreeTransactionState(state_);
+}
+
+bool Transaction::EnsureStateAllocated() {
+ if (!state_allocated_ && client_.GetChannel()) {
+ state_ = client_.GetChannel()->AllocateTransactionState();
+ state_allocated_ = true;
+ }
+ return state_allocated_;
+}
+
+void Transaction::SendTransaction(int opcode, Status<void>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ *ret = client_.CheckReconnect();
+ if (!*ret)
+ return;
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ auto status = client_.GetChannel()->SendWithInt(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ if (status) {
+ ret->SetValue();
+ } else {
+ ret->SetError(status.error());
+ }
+ CheckDisconnect(status);
+}
+
+void Transaction::SendTransaction(int opcode, Status<int>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithInt(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithFileHandle(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithChannelHandle(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+FileReference Transaction::PushFileHandle(const LocalHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushFileHandle(state_, handle)
+ : -1;
+}
+
+FileReference Transaction::PushFileHandle(const BorrowedHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushFileHandle(state_, handle)
+ : -1;
+}
+
+FileReference Transaction::PushFileHandle(const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+ChannelReference Transaction::PushChannelHandle(
+ const LocalChannelHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushChannelHandle(state_, handle)
+ : -1;
+}
+
+ChannelReference Transaction::PushChannelHandle(
+ const BorrowedChannelHandle& handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated()
+ ? client_.GetChannel()->PushChannelHandle(state_, handle)
+ : -1;
+}
+
+ChannelReference Transaction::PushChannelHandle(
+ const RemoteChannelHandle& handle) {
+ return handle.value();
+}
+
+bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated() &&
+ client_.GetChannel()->GetFileHandle(state_, ref, handle);
+}
+
+bool Transaction::GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated() &&
+ client_.GetChannel()->GetChannelHandle(state_, ref, handle);
+}
+
+void Transaction::CheckDisconnect(int error) {
+ if (client_.NeedToDisconnectChannel(error)) {
+ if (state_allocated_) {
+ if (client_.GetChannel())
+ client_.GetChannel()->FreeTransactionState(state_);
+ state_ = nullptr;
+ state_allocated_ = false;
+ }
+ client_.Close(error);
+ }
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/client_tests.cpp b/libs/vr/libpdx/client_tests.cpp
new file mode 100644
index 0000000..f1fb6d1
--- /dev/null
+++ b/libs/vr/libpdx/client_tests.cpp
@@ -0,0 +1,566 @@
+#include <pdx/client.h>
+
+#include <gmock/gmock.h>
+#include <sys/eventfd.h>
+
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ClientBase;
+using android::pdx::ClientChannel;
+using android::pdx::ClientChannelFactory;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::MockClientChannel;
+using android::pdx::MockClientChannelFactory;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::Void;
+
+using testing::A;
+using testing::AnyNumber;
+using testing::ByMove;
+using testing::Invoke;
+using testing::Ne;
+using testing::Return;
+using testing::_;
+
+namespace {
+
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+inline const void* IntToConstPtr(intptr_t addr) {
+ return reinterpret_cast<const void*>(addr);
+}
+
+struct TestInterface final {
+ // Op codes.
+ enum {
+ kOpAdd = 0,
+ kOpSendFile,
+ kOpGetFile,
+ kOpPushChannel,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+ PDX_REMOTE_METHOD(SendFile, kOpSendFile, void(const LocalHandle& fd));
+ PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+ PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+
+ PDX_REMOTE_API(API, Add, SendFile, GetFile, PushChannel);
+};
+
+class SimpleClient : public ClientBase<SimpleClient> {
+ public:
+ explicit SimpleClient(std::unique_ptr<ClientChannel> channel)
+ : BASE{std::move(channel)} {}
+ SimpleClient(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms)
+ : BASE{std::move(channel_factory), timeout_ms} {
+ EnableAutoReconnect(timeout_ms);
+ }
+
+ using BASE::SendImpulse;
+ using BASE::InvokeRemoteMethod;
+ using BASE::InvokeRemoteMethodInPlace;
+ using BASE::Close;
+ using BASE::IsConnected;
+ using BASE::EnableAutoReconnect;
+ using BASE::DisableAutoReconnect;
+ using BASE::event_fd;
+ using BASE::GetChannel;
+
+ MOCK_METHOD0(OnConnect, void());
+};
+
+class FailingClient : public ClientBase<FailingClient> {
+ public:
+ explicit FailingClient(std::unique_ptr<ClientChannel> channel, int error_code)
+ : BASE{std::move(channel)} {
+ Close(error_code);
+ }
+};
+
+class ClientChannelTest : public testing::Test {
+ public:
+ ClientChannelTest()
+ : client_{SimpleClient::Create(
+ std::make_unique<testing::StrictMock<MockClientChannel>>())} {}
+
+ MockClientChannel* mock_channel() {
+ return static_cast<MockClientChannel*>(client_->GetChannel());
+ }
+
+ std::unique_ptr<SimpleClient> client_;
+};
+
+class ClientChannelFactoryTest : public testing::Test {
+ public:
+ ClientChannelFactoryTest() {
+ auto factory =
+ std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+ ON_CALL(*factory, Connect(kTimeout))
+ .WillByDefault(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+ client_ = SimpleClient::Create(std::move(factory), kTimeout);
+ }
+
+ MockClientChannel* mock_channel() {
+ return static_cast<MockClientChannel*>(client_->GetChannel());
+ }
+
+ Status<std::unique_ptr<ClientChannel>> OnConnect(int64_t /*timeout_ms*/) {
+ if (on_connect_error_)
+ return ErrorStatus(on_connect_error_);
+ std::unique_ptr<MockClientChannel> channel =
+ std::make_unique<testing::StrictMock<MockClientChannel>>();
+ if (on_connect_callback_)
+ on_connect_callback_(channel.get());
+ return Status<std::unique_ptr<ClientChannel>>{std::move(channel)};
+ }
+
+ void OnConnectCallback(std::function<void(MockClientChannel*)> callback) {
+ on_connect_callback_ = callback;
+ }
+ void SetOnConnectError(int error) { on_connect_error_ = error; }
+ void ResetOnConnectError() { on_connect_error_ = 0; }
+
+ constexpr static int64_t kTimeout = 123;
+ std::unique_ptr<SimpleClient> client_;
+ std::function<void(MockClientChannel*)> on_connect_callback_;
+ int on_connect_error_{0};
+};
+
+constexpr int64_t ClientChannelFactoryTest::kTimeout;
+
+class ClientTransactionTest : public ClientChannelTest {
+ public:
+ ClientTransactionTest() : transaction_{*client_} {}
+
+ Transaction transaction_;
+};
+
+} // anonymous namespace
+
+TEST_F(ClientChannelTest, IsInitialized) {
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelTest, CloseOnConstruction) {
+ FailingClient failed_client1{std::make_unique<MockClientChannel>(), EACCES};
+ ASSERT_FALSE(failed_client1.IsInitialized());
+ EXPECT_EQ(-EACCES, failed_client1.error());
+
+ FailingClient failed_client2{std::make_unique<MockClientChannel>(), -EACCES};
+ ASSERT_FALSE(failed_client2.IsInitialized());
+ EXPECT_EQ(-EACCES, failed_client2.error());
+
+ auto failed_client3 =
+ FailingClient::Create(std::make_unique<MockClientChannel>(), EIO);
+ ASSERT_EQ(failed_client3.get(), nullptr);
+}
+
+TEST_F(ClientChannelTest, IsConnected) {
+ EXPECT_TRUE(client_->IsConnected());
+ EXPECT_EQ(0, client_->error());
+ client_->Close(-EINVAL);
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-EINVAL, client_->error());
+}
+
+TEST_F(ClientChannelTest, event_fd) {
+ EXPECT_CALL(*mock_channel(), event_fd()).WillOnce(Return(12));
+ EXPECT_EQ(12, client_->event_fd());
+}
+
+TEST_F(ClientChannelTest, SendImpulse) {
+ EXPECT_CALL(*mock_channel(), SendImpulse(123, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(client_->SendImpulse(123));
+
+ EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+
+ const void* const kTestPtr = IntToConstPtr(1234);
+ EXPECT_CALL(*mock_channel(), SendImpulse(1, kTestPtr, 17))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(client_->SendImpulse(1, kTestPtr, 17));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodNullTransactionState) {
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(9));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+ EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::Add>(4, 5));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(3));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(3, status.get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ int fd = eventfd(0, 0);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+ _, _, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalHandle{fd})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::GetFile>();
+ ASSERT_TRUE(status);
+ EXPECT_EQ(fd, status.get().Get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+ _, _, nullptr, 0))
+ .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::GetFile>("file", 0);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ const int32_t kHandleValue = 17;
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+ _, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, kHandleValue})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalChannelHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kHandleValue, status.get().value());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+ _, nullptr, 0))
+ .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalChannelHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ LocalHandle fd{eventfd(0, 0)};
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+ nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ LocalHandle fd{eventfd(0, 0)};
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+ nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EACCES}));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_FALSE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelFactoryTest, IsInitialized) {
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, NotConnectedButInitialized) {
+ auto factory =
+ std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+ EXPECT_CALL(*factory, Connect(kTimeout))
+ .WillOnce(Return(ByMove(ErrorStatus(ESHUTDOWN))))
+ .WillOnce(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+ client_ = SimpleClient::Create(std::move(factory), kTimeout);
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_FALSE(client_->IsConnected());
+ client_->DisableAutoReconnect();
+ ASSERT_FALSE(client_->SendImpulse(17));
+ EXPECT_FALSE(client_->IsConnected());
+ client_->EnableAutoReconnect(kTimeout);
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckDisconnect) {
+ EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{ESHUTDOWN}));
+ ASSERT_FALSE(client_->SendImpulse(17));
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-ESHUTDOWN, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckReconnect) {
+ client_->Close(ESHUTDOWN);
+ ASSERT_FALSE(client_->IsConnected());
+
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CloseOnConnect) {
+ client_->Close(ESHUTDOWN);
+
+ EXPECT_CALL(*client_, OnConnect()).WillOnce(Invoke([this] {
+ client_->Close(EIO);
+ }));
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-EIO, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, DisableAutoReconnect) {
+ client_->Close(EIO);
+ ASSERT_FALSE(client_->IsConnected());
+ client_->DisableAutoReconnect();
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(ESHUTDOWN, status.error());
+ EXPECT_FALSE(client_->IsConnected());
+ client_->EnableAutoReconnect(kTimeout);
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientTransactionTest, SendNoData) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, 2, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalHandle{-1})));
+ EXPECT_TRUE(transaction_.Send<LocalHandle>(2));
+ EXPECT_CALL(*mock_channel(), SendWithChannelHandle(kTransactionState, 3,
+ nullptr, 0, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, 1})));
+ EXPECT_TRUE(transaction_.Send<LocalChannelHandle>(3));
+}
+
+TEST_F(ClientTransactionTest, SendNoState) {
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+ EXPECT_TRUE(transaction_.Send<void>(1));
+}
+
+TEST_F(ClientTransactionTest, SendBuffers) {
+ const void* const kSendBuffer = IntToConstPtr(123);
+ const size_t kSendSize = 12;
+ void* const kReceiveBuffer = IntToPtr(456);
+ const size_t kReceiveSize = 34;
+
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(1, nullptr, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 2, Ne(nullptr), 1, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(2, kSendBuffer, kSendSize, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(3, kSendBuffer, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 4, nullptr, 0, Ne(nullptr), 1))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(
+ transaction_.Send<void>(4, nullptr, 0, kReceiveBuffer, kReceiveSize));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(5, nullptr, 0, kReceiveBuffer, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 5, Ne(nullptr), 1, Ne(nullptr), 1))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(5, kSendBuffer, kSendSize, kReceiveBuffer,
+ kReceiveSize));
+}
+
+TEST_F(ClientTransactionTest, SendVector) {
+ iovec send[3] = {};
+ iovec recv[4] = {};
+
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(1, nullptr, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, send, 3, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(2, send, 3, recv, 4));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, send, 3, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(3, send, nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(4, nullptr, recv));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, send, 3, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(5, send, recv));
+}
+
+TEST_F(ClientTransactionTest, PushHandle) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_EQ(1, transaction_.PushFileHandle(LocalHandle{-1}));
+
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const BorrowedHandle&>()))
+ .WillOnce(Return(2));
+ EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}));
+
+ EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}));
+
+ EXPECT_CALL(
+ *mock_channel(),
+ PushChannelHandle(kTransactionState, A<const LocalChannelHandle&>()))
+ .WillOnce(Return(11));
+ EXPECT_EQ(11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}));
+
+ EXPECT_CALL(
+ *mock_channel(),
+ PushChannelHandle(kTransactionState, A<const BorrowedChannelHandle&>()))
+ .WillOnce(Return(12));
+ EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}));
+
+ EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}));
+}
+
+TEST_F(ClientTransactionTest, GetHandle) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+ EXPECT_CALL(*mock_channel(), GetFileHandle(kTransactionState, 1, _))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ LocalHandle file_handle;
+ EXPECT_FALSE(transaction_.GetFileHandle(1, &file_handle));
+ EXPECT_TRUE(transaction_.GetFileHandle(1, &file_handle));
+
+ EXPECT_CALL(*mock_channel(), GetChannelHandle(kTransactionState, 2, _))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ LocalChannelHandle channel_handle;
+ EXPECT_FALSE(transaction_.GetChannelHandle(2, &channel_handle));
+ EXPECT_TRUE(transaction_.GetChannelHandle(2, &channel_handle));
+}
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
new file mode 100644
index 0000000..b7d94b3
--- /dev/null
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -0,0 +1,515 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx::rpc;
+using namespace android::pdx;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using std::placeholders::_5;
+using std::placeholders::_6;
+
+namespace {
+
+constexpr size_t kMaxStaticBufferSize = 20480;
+
+// Provide numpunct facet that formats numbers with ',' as thousands separators.
+class CommaNumPunct : public std::numpunct<char> {
+ protected:
+ char do_thousands_sep() const override { return ','; }
+ std::string do_grouping() const override { return "\03"; }
+};
+
+class TestPayload : public MessagePayload<SendBuffer>,
+ public MessageWriter,
+ public MessageReader,
+ public NoOpResourceMapper {
+ public:
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ const size_t section_offset = Size();
+ Extend(size);
+ return Data() + section_offset;
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*ConstCursor(), &*ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return this; }
+};
+
+class StaticBuffer : public MessageWriter,
+ public MessageReader,
+ public NoOpResourceMapper {
+ public:
+ void Clear() {
+ read_ptr_ = buffer_;
+ write_ptr_ = 0;
+ }
+ void Rewind() { read_ptr_ = buffer_; }
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ void* ptr = buffer_ + write_ptr_;
+ write_ptr_ += size;
+ return ptr;
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {read_ptr_, std::end(buffer_)};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ read_ptr_ = static_cast<const uint8_t*>(new_start);
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return this; }
+
+ private:
+ uint8_t buffer_[kMaxStaticBufferSize];
+ const uint8_t* read_ptr_{buffer_};
+ size_t write_ptr_{0};
+};
+
+// Simple callback function to clear/reset the input/output buffers for
+// serialization. Using raw function pointer here instead of std::function to
+// minimize the overhead of invocation in the tight test loop over millions of
+// iterations.
+using ResetFunc = void(void*);
+
+// Serialization test function signature, used by the TestRunner.
+using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data);
+
+// Deserialization test function signature, used by the TestRunner.
+using DeserializeTestSignature = std::chrono::nanoseconds(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data);
+
+// Generic serialization test runner method. Takes the |value| of type T and
+// serializes it into the output buffer represented by |writer|.
+template <typename T>
+std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data, const T& value) {
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ write_reset(reset_data);
+ Serialize(value, writer);
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// Generic deserialization test runner method. Takes the |value| of type T and
+// temporarily serializes it into the output buffer, then repeatedly
+// deserializes the data back from that buffer.
+template <typename T>
+std::chrono::nanoseconds DeserializeTestRunner(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+ const T& value) {
+ write_reset(reset_data);
+ Serialize(value, writer);
+ T output_data;
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ read_reset(reset_data);
+ Deserialize(&output_data, reader);
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ if (output_data != value)
+ return start - stop; // Return negative value to indicate error.
+ return stop - start;
+}
+
+// Special version of SerializeTestRunner that doesn't perform any serialization
+// but does all the same setup steps and moves data of size |data_size| into
+// the output buffer. Useful to determine the baseline to calculate time used
+// just for serialization layer.
+std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data, size_t data_size) {
+ std::vector<uint8_t> dummy_data(data_size);
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ write_reset(reset_data);
+ memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+ dummy_data.data(), dummy_data.size());
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// Special version of DeserializeTestRunner that doesn't perform any
+// deserialization but invokes Rewind on the input buffer repeatedly.
+// Useful to determine the baseline to calculate time used just for
+// deserialization layer.
+std::chrono::nanoseconds DeserializeBaseTest(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+ size_t data_size) {
+ std::vector<uint8_t> dummy_data(data_size);
+ write_reset(reset_data);
+ memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+ dummy_data.data(), dummy_data.size());
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ read_reset(reset_data);
+ auto section = reader->GetNextReadBufferSection();
+ memcpy(dummy_data.data(), section.first, dummy_data.size());
+ reader->ConsumeReadBufferSectionData(
+ AdvancePointer(section.first, dummy_data.size()));
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// The main class that accumulates individual tests to be executed.
+class TestRunner {
+ public:
+ struct BufferInfo {
+ BufferInfo(const std::string& buffer_name, MessageReader* reader,
+ MessageWriter* writer, ResetFunc* read_reset_func,
+ ResetFunc* write_reset_func, void* reset_data)
+ : name{buffer_name},
+ reader{reader},
+ writer{writer},
+ read_reset_func{read_reset_func},
+ write_reset_func{write_reset_func},
+ reset_data{reset_data} {}
+ std::string name;
+ MessageReader* reader;
+ MessageWriter* writer;
+ ResetFunc* read_reset_func;
+ ResetFunc* write_reset_func;
+ void* reset_data;
+ };
+
+ void AddTestFunc(const std::string& name,
+ std::function<SerializeTestSignature> serialize_test,
+ std::function<DeserializeTestSignature> deserialize_test,
+ size_t data_size) {
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::move(deserialize_test), data_size);
+ }
+
+ template <typename T>
+ void AddSerializationTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ auto serialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+ &SerializeTestRunner),
+ _1, _2, _3, _4, std::forward<T>(value));
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::function<DeserializeTestSignature>{}, data_size);
+ }
+
+ template <typename T>
+ void AddDeserializationTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ auto deserialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageReader*, MessageWriter*, size_t, ResetFunc*,
+ ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+ _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+ tests_.emplace_back(name, std::function<SerializeTestSignature>{},
+ std::move(deserialize_test), data_size);
+ }
+
+ template <typename T>
+ void AddTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ if (data_size > kMaxStaticBufferSize) {
+ std::cerr << "Test '" << name << "' requires " << data_size
+ << " bytes in the serialization buffer but only "
+ << kMaxStaticBufferSize << " are available." << std::endl;
+ exit(1);
+ }
+ auto serialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+ &SerializeTestRunner),
+ _1, _2, _3, _4, value);
+ auto deserialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageReader*, MessageWriter*, size_t, ResetFunc*,
+ ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+ _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::move(deserialize_test), data_size);
+ }
+
+ std::string CenterString(std::string text, size_t column_width) {
+ if (text.size() < column_width) {
+ text = std::string((column_width - text.size()) / 2, ' ') + text;
+ }
+ return text;
+ }
+
+ void RunTests(size_t iteration_count,
+ const std::vector<BufferInfo>& buffers) {
+ using float_seconds = std::chrono::duration<double>;
+ const std::string name_column_separator = " : ";
+ const std::string buffer_column_separator = " || ";
+ const std::string buffer_timing_column_separator = " | ";
+ const size_t data_size_column_width = 6;
+ const size_t time_column_width = 9;
+ const size_t qps_column_width = 18;
+ const size_t buffer_column_width = time_column_width +
+ buffer_timing_column_separator.size() +
+ qps_column_width;
+
+ auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) {
+ return t1.name.size() < t2.name.size();
+ };
+ auto test_with_longest_name =
+ std::max_element(tests_.begin(), tests_.end(), compare_name_length);
+ size_t name_column_width = test_with_longest_name->name.size();
+
+ size_t total_width =
+ name_column_width + name_column_separator.size() +
+ data_size_column_width + buffer_column_separator.size() +
+ buffers.size() * (buffer_column_width + buffer_column_separator.size());
+
+ const std::string dbl_separator(total_width, '=');
+ const std::string separator(total_width, '-');
+
+ auto print_header = [&](const std::string& header) {
+ std::cout << dbl_separator << std::endl;
+ std::stringstream ss;
+ ss.imbue(std::locale(ss.getloc(), new CommaNumPunct));
+ ss << header << " (" << iteration_count << " iterations)";
+ std::cout << CenterString(ss.str(), total_width) << std::endl;
+ std::cout << dbl_separator << std::endl;
+ std::cout << std::setw(name_column_width) << "Test Name" << std::left
+ << name_column_separator << std::setw(data_size_column_width)
+ << CenterString("Size", data_size_column_width)
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ std::cout << std::setw(buffer_column_width)
+ << CenterString(buffer_info.name, buffer_column_width)
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ std::cout << std::setw(name_column_width) << "" << name_column_separator
+ << std::setw(data_size_column_width)
+ << CenterString("bytes", data_size_column_width)
+ << buffer_column_separator << std::left;
+ for (size_t i = 0; i < buffers.size(); i++) {
+ std::cout << std::setw(time_column_width)
+ << CenterString("Time, s", time_column_width)
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width)
+ << CenterString("QPS", qps_column_width)
+ << buffer_column_separator;
+ }
+ std::cout << std::right << std::endl;
+ std::cout << separator << std::endl;
+ };
+
+ print_header("Serialization benchmarks");
+ for (const auto& test : tests_) {
+ if (test.serialize_test) {
+ std::cout << std::setw(name_column_width) << test.name << " : "
+ << std::setw(data_size_column_width) << test.data_size
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ auto seconds =
+ std::chrono::duration_cast<float_seconds>(test.serialize_test(
+ buffer_info.writer, iteration_count,
+ buffer_info.write_reset_func, buffer_info.reset_data));
+ double qps = iteration_count / seconds.count();
+ std::cout << std::fixed << std::setprecision(3)
+ << std::setw(time_column_width) << seconds.count()
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width) << qps
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ }
+ }
+
+ print_header("Deserialization benchmarks");
+ for (const auto& test : tests_) {
+ if (test.deserialize_test) {
+ std::cout << std::setw(name_column_width) << test.name << " : "
+ << std::setw(data_size_column_width) << test.data_size
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ auto seconds =
+ std::chrono::duration_cast<float_seconds>(test.deserialize_test(
+ buffer_info.reader, buffer_info.writer, iteration_count,
+ buffer_info.read_reset_func, buffer_info.write_reset_func,
+ buffer_info.reset_data));
+ double qps = iteration_count / seconds.count();
+ std::cout << std::fixed << std::setprecision(3)
+ << std::setw(time_column_width) << seconds.count()
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width) << qps
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ }
+ }
+ std::cout << dbl_separator << std::endl;
+ }
+
+ private:
+ struct TestEntry {
+ TestEntry(const std::string& test_name,
+ std::function<SerializeTestSignature> serialize_test,
+ std::function<DeserializeTestSignature> deserialize_test,
+ size_t data_size)
+ : name{test_name},
+ serialize_test{std::move(serialize_test)},
+ deserialize_test{std::move(deserialize_test)},
+ data_size{data_size} {}
+ std::string name;
+ std::function<SerializeTestSignature> serialize_test;
+ std::function<DeserializeTestSignature> deserialize_test;
+ size_t data_size;
+ };
+
+ std::vector<TestEntry> tests_;
+};
+
+std::string GenerateContainerName(const std::string& type, size_t count) {
+ std::stringstream ss;
+ ss << type << "(" << count << ")";
+ return ss.str();
+}
+
+} // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+ const size_t iteration_count = 10000000; // 10M iterations.
+ TestRunner test_runner;
+ std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct));
+
+ // Baseline tests to figure out the overhead of buffer resizing and data
+ // transfers.
+ for (size_t len : {0, 1, 9, 66, 259}) {
+ auto serialize_base_test =
+ std::bind(&SerializeBaseTest, _1, _2, _3, _4, len);
+ auto deserialize_base_test =
+ std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len);
+ test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test),
+ std::move(deserialize_base_test), len);
+ }
+
+ // Individual serialization/deserialization tests.
+ test_runner.AddTest("bool", true);
+ test_runner.AddTest("int32_t", 12);
+
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ test_runner.AddTest(GenerateContainerName("string", len),
+ std::string(len, '*'));
+ }
+ // Serialization is too slow to handle such large strings, add this test for
+ // deserialization only.
+ test_runner.AddDeserializationTest(GenerateContainerName("string", 10240),
+ std::string(10240, '*'));
+
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ std::vector<int32_t> int_vector(len);
+ std::iota(int_vector.begin(), int_vector.end(), 0);
+ test_runner.AddTest(GenerateContainerName("vector<int32_t>", len),
+ std::move(int_vector));
+ }
+
+ std::vector<std::string> vector_of_strings = {
+ "012345678901234567890123456789", "012345678901234567890123456789",
+ "012345678901234567890123456789", "012345678901234567890123456789",
+ "012345678901234567890123456789",
+ };
+ test_runner.AddTest(
+ GenerateContainerName("vector<string>", vector_of_strings.size()),
+ std::move(vector_of_strings));
+
+ test_runner.AddTest("tuple<int, bool, string, double>",
+ std::make_tuple(123, true, std::string{"foobar"}, 1.1));
+
+ for (size_t len : {0, 1, 8, 64}) {
+ std::map<int, std::string> test_map;
+ for (size_t i = 0; i < len; i++)
+ test_map.emplace(i, std::to_string(i));
+ test_runner.AddTest(GenerateContainerName("map<int, string>", len),
+ std::move(test_map));
+ }
+
+ for (size_t len : {0, 1, 8, 64}) {
+ std::unordered_map<int, std::string> test_map;
+ for (size_t i = 0; i < len; i++)
+ test_map.emplace(i, std::to_string(i));
+ test_runner.AddTest(
+ GenerateContainerName("unordered_map<int, string>", len),
+ std::move(test_map));
+ }
+
+ // BufferWrapper can't be used with deserialization tests right now because
+ // it requires external buffer to be filled in, which is not available.
+ std::vector<std::vector<uint8_t>> data_buffers;
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ data_buffers.emplace_back(len);
+ test_runner.AddSerializationTest(
+ GenerateContainerName("BufferWrapper<uint8_t*>", len),
+ BufferWrapper<uint8_t*>(data_buffers.back().data(),
+ data_buffers.back().size()));
+ }
+
+ // Various backing buffers to run the tests on.
+ std::vector<TestRunner::BufferInfo> buffers;
+
+ Payload buffer;
+ buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer,
+ [](void* ptr) { static_cast<Payload*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<Payload*>(ptr)->Clear(); },
+ &buffer);
+
+ TestPayload tls_buffer;
+ buffers.emplace_back(
+ "TLS Buffer", &tls_buffer, &tls_buffer,
+ [](void* ptr) { static_cast<TestPayload*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<TestPayload*>(ptr)->Clear(); }, &tls_buffer);
+
+ StaticBuffer static_buffer;
+ buffers.emplace_back(
+ "Static Buffer", &static_buffer, &static_buffer,
+ [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Clear(); },
+ &static_buffer);
+
+ // Finally, run all the tests.
+ test_runner.RunTests(iteration_count, buffers);
+ return 0;
+}
diff --git a/libs/vr/libpdx/errno_guard.h b/libs/vr/libpdx/errno_guard.h
new file mode 100644
index 0000000..fc7dfdf
--- /dev/null
+++ b/libs/vr/libpdx/errno_guard.h
@@ -0,0 +1,34 @@
+#ifndef ANDROID_PDX_ERRNO_GUARD_H_
+#define ANDROID_PDX_ERRNO_GUARD_H_
+
+#include <errno.h>
+
+namespace android {
+namespace pdx {
+
+// Automatically saves and restores the system errno for API implementations to
+// prevent internal use errno from affecting API callers.
+class ErrnoGuard {
+ public:
+ ErrnoGuard() : saved_errno_(errno) {}
+ ~ErrnoGuard() { errno = saved_errno_; }
+
+ int saved_errno() const { return saved_errno_; }
+
+ private:
+ int saved_errno_;
+
+ ErrnoGuard(const ErrnoGuard&) = delete;
+ void operator=(const ErrnoGuard&) = delete;
+};
+
+// Checks |return_code| and returns either it or the negated system errno based
+// on the return code value.
+inline int ReturnCodeOrError(int return_code) {
+ return return_code < 0 ? -errno : return_code;
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_ERRNO_GUARD_H_
diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp
new file mode 100644
index 0000000..76fd154
--- /dev/null
+++ b/libs/vr/libpdx/mock_tests.cpp
@@ -0,0 +1,20 @@
+#include <gtest/gtest.h>
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/mock_message_reader.h>
+#include <pdx/mock_message_writer.h>
+#include <pdx/mock_service_dispatcher.h>
+#include <pdx/mock_service_endpoint.h>
+
+TEST(MockTypes, Instantiation) {
+ // Make sure all our interfaces are mocked out properly and mock instances
+ // can be created.
+ android::pdx::MockClientChannel client_channel;
+ android::pdx::MockClientChannelFactory client_channel_factory;
+ android::pdx::MockInputResourceMapper input_resource_mapper;
+ android::pdx::MockMessageReader message_reader;
+ android::pdx::MockOutputResourceMapper output_resource_mapper;
+ android::pdx::MockMessageWriter message_writer;
+ android::pdx::MockServiceDispatcher service_dispatcher;
+ android::pdx::MockEndpoint endpoint;
+}
diff --git a/libs/vr/libpdx/private/pdx/channel_handle.h b/libs/vr/libpdx/private/pdx/channel_handle.h
new file mode 100644
index 0000000..1e62d25
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/channel_handle.h
@@ -0,0 +1,123 @@
+#ifndef ANDROID_PDX_CHANNEL_HANDLE_H_
+#define ANDROID_PDX_CHANNEL_HANDLE_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+
+enum class ChannelHandleMode {
+ Local,
+ Borrowed,
+ Remote,
+};
+
+class ChannelManagerInterface {
+ public:
+ virtual void CloseHandle(std::int32_t handle) = 0;
+
+ protected:
+ // Nobody should be allowed to delete the instance of channel manager
+ // through this interface.
+ virtual ~ChannelManagerInterface() = default;
+};
+
+class ChannelHandleBase {
+ public:
+ ChannelHandleBase() = default;
+ ChannelHandleBase(const int32_t& value) : value_{value} {}
+
+ ChannelHandleBase(const ChannelHandleBase&) = delete;
+ ChannelHandleBase& operator=(const ChannelHandleBase&) = delete;
+
+ std::int32_t value() const { return value_; }
+ bool valid() const { return value_ >= 0; }
+ explicit operator bool() const { return valid(); }
+
+ void Close() { value_ = kEmptyHandle; }
+
+ protected:
+ // Must not be used by itself. Must be derived from.
+ ~ChannelHandleBase() = default;
+ enum : std::int32_t { kEmptyHandle = -1 };
+
+ std::int32_t value_{kEmptyHandle};
+};
+
+template <ChannelHandleMode Mode>
+class ChannelHandle : public ChannelHandleBase {
+ public:
+ ChannelHandle() = default;
+ using ChannelHandleBase::ChannelHandleBase;
+ ChannelHandle(ChannelHandle&& other) : ChannelHandleBase{other.value_} {
+ other.value_ = kEmptyHandle;
+ }
+ ~ChannelHandle() = default;
+
+ ChannelHandle Duplicate() const { return ChannelHandle{value_}; }
+
+ ChannelHandle& operator=(ChannelHandle&& other) {
+ value_ = other.value_;
+ other.value_ = kEmptyHandle;
+ return *this;
+ }
+};
+
+template <>
+class ChannelHandle<ChannelHandleMode::Local> : public ChannelHandleBase {
+ public:
+ ChannelHandle() = default;
+ ChannelHandle(ChannelManagerInterface* manager, int32_t value)
+ : ChannelHandleBase{value}, manager_{manager} {}
+
+ ChannelHandle(const ChannelHandle&) = delete;
+ ChannelHandle& operator=(const ChannelHandle&) = delete;
+
+ ChannelHandle(ChannelHandle&& other)
+ : ChannelHandleBase{other.value_}, manager_{other.manager_} {
+ other.manager_ = nullptr;
+ other.value_ = kEmptyHandle;
+ }
+
+ ChannelHandle& operator=(ChannelHandle&& other) {
+ value_ = other.value_;
+ manager_ = other.manager_;
+ other.value_ = kEmptyHandle;
+ other.manager_ = nullptr;
+ return *this;
+ }
+
+ ~ChannelHandle() {
+ if (manager_)
+ manager_->CloseHandle(value_);
+ }
+
+ ChannelHandle<ChannelHandleMode::Borrowed> Borrow() const {
+ return ChannelHandle<ChannelHandleMode::Borrowed>{value_};
+ }
+
+ void Close() {
+ if (manager_)
+ manager_->CloseHandle(value_);
+ manager_ = nullptr;
+ value_ = kEmptyHandle;
+ }
+
+ private:
+ ChannelManagerInterface* manager_{nullptr};
+};
+
+using LocalChannelHandle = ChannelHandle<ChannelHandleMode::Local>;
+using BorrowedChannelHandle = ChannelHandle<ChannelHandleMode::Borrowed>;
+using RemoteChannelHandle = ChannelHandle<ChannelHandleMode::Remote>;
+
+// ChannelReference is a 32 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local channel
+// handle by calling Transaction.GetChannelHandle().
+using ChannelReference = int32_t;
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CHANNEL_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/client.h b/libs/vr/libpdx/private/pdx/client.h
new file mode 100644
index 0000000..4eafe76
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client.h
@@ -0,0 +1,286 @@
+#ifndef ANDROID_PDX_CLIENT_H_
+#define ANDROID_PDX_CLIENT_H_
+
+#include <errno.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <pdx/channel_handle.h>
+#include <pdx/client_channel.h>
+#include <pdx/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class Transaction;
+
+/*
+ * Base class of client-side service API classes.
+ */
+class Client {
+ public:
+ static const int64_t kInfiniteTimeout = -1;
+
+ virtual ~Client() = default;
+
+ /*
+ * Returns true if the Client instance successfully initialized, false
+ * otherwise. Subclasses that can fail to initialize must override this and
+ * AND their initialization result with this base class method's result.
+ *
+ * This method is not intended to perform initialization, only to report
+ * the status of the initialization.
+ */
+ virtual bool IsInitialized() const;
+
+ /*
+ * Returns the error code describing the Client initialization failure, or 0
+ * if there was no failure.
+ */
+ int error() const;
+
+ // Returns a reference to IPC channel handle.
+ LocalChannelHandle& GetChannelHandle();
+
+ protected:
+ friend Transaction;
+ explicit Client(std::unique_ptr<ClientChannel> channel);
+ explicit Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms = kInfiniteTimeout);
+
+ /*
+ * Called by Client::Connect() after successfully connecting to the service
+ * endpoint. Subclasses may override this method to perform additional setup,
+ * including sending messages to complete the connection process.
+ *
+ * Subclasses may call Client::Close() within this method to terminate the
+ * connection; Client::Connect() returns the negated error passed to
+ * Client::Close() when this happens.
+ */
+ virtual void OnConnect();
+
+ enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 };
+
+ Status<void> SendImpulse(int opcode);
+ Status<void> SendImpulse(int opcode, const void* buffer, size_t length);
+
+ /*
+ * Remote method call API using pdx::rpc serialization.
+ * Include pdx/rpc/remote_method.h to use these methods.
+ */
+ template <typename RemoteMethodType, typename... Args>
+ Status<typename RemoteMethodType::Return> InvokeRemoteMethod(Args&&... args);
+
+ template <typename RemoteMethodType, typename ReturnType, typename... Args>
+ Status<void> InvokeRemoteMethodInPlace(ReturnType* return_value,
+ Args&&... args);
+
+ /*
+ * Close the endpoint file descriptor and optionally indicate an error, which
+ * may be retrieved through error(). Subclasses may use this in their
+ * constructor to signal failure during initialization or at other times
+ * during operation.
+ */
+ void Close(int error);
+
+ /*
+ * Returns true if the client is connected to the service, false otherwise.
+ */
+ bool IsConnected() const;
+
+ /*
+ * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1)
+ * for no timeout. Auto-reconnect can only be enabled if the Client class
+ * was constructed with a ClientChannelFactory.
+ */
+ void EnableAutoReconnect(int64_t reconnect_timeout_ms);
+
+ /*
+ * Disables auto-reconnect.
+ */
+ void DisableAutoReconnect();
+
+ int event_fd() const;
+ ClientChannel* GetChannel() const { return channel_.get(); }
+
+ private:
+ Client(const Client&) = delete;
+ void operator=(const Client&) = delete;
+
+ Status<void> CheckReconnect();
+ bool NeedToDisconnectChannel(int error) const;
+ void CheckDisconnect(int error);
+
+ template <typename T>
+ inline void CheckDisconnect(const Status<T>& status) {
+ if (!status)
+ CheckDisconnect(status.error());
+ }
+
+ std::unique_ptr<ClientChannel> channel_;
+ int error_{0};
+
+ // Reconnection state.
+ std::unique_ptr<ClientChannelFactory> channel_factory_;
+ int64_t reconnect_timeout_ms_{0};
+ bool auto_reconnect_enabled_{false};
+};
+
+/*
+ * Utility template base class for client-side service API classes. Handles
+ * initialization checks during allocation and automatically cleans up on
+ * failure.
+ *
+ * @tparam T Type of the class extending this one.
+ * @tparam C Client class to wrap. Defaults to the Client class.
+ */
+template <typename T, typename ParentClient = Client>
+class ClientBase : public ParentClient {
+ public:
+ // Type of the client this class wraps.
+ using ClientType = ParentClient;
+
+ static_assert(std::is_base_of<Client, ParentClient>::value,
+ "The provided parent client is not a Client subclass.");
+
+ /*
+ * Allocates a new instance of the superclass and checks for successful
+ * initialization.
+ *
+ * The variadic arguments must expand to match one of type T's constructors
+ * and are passed through unchanged. If a timeout is desired, subclasses are
+ * responsible for passing this through to the appropriate ClientBase
+ * constructor.
+ *
+ * Returns a unique_ptr to the new instance on success, or an empty unique_ptr
+ * otherwise.
+ */
+ template <typename... Args>
+ static inline std::unique_ptr<T> Create(Args&&... args) {
+ std::unique_ptr<T> client(new T(std::forward<Args>(args)...));
+ if (client->IsInitialized())
+ return client;
+ else
+ return nullptr;
+ }
+
+ protected:
+ /*
+ * Type of the base class. Useful for referencing the base class type and
+ * constructor in subclasses. Subclasses with non-public constructors
+ * must declare BASE a friend.
+ */
+ using BASE = ClientBase<T, ParentClient>;
+
+ /*
+ * Type of the unique_ptr deleter. Useful for friend declarations.
+ */
+ using deleter_type = typename std::unique_ptr<T>::deleter_type;
+
+ using ParentClient::ParentClient;
+};
+
+class Transaction final : public OutputResourceMapper,
+ public InputResourceMapper {
+ public:
+ Transaction(Client& client);
+ ~Transaction();
+
+ template <typename T>
+ Status<T> Send(int opcode) {
+ return SendVector<T>(opcode, nullptr, 0, nullptr, 0);
+ }
+
+ template <typename T>
+ Status<T> Send(int opcode, const void* send_buffer, size_t send_length,
+ void* receive_buffer, size_t receive_length) {
+ const bool send = (send_buffer && send_length);
+ const bool receive = (receive_buffer && receive_length);
+ const iovec send_vector = {const_cast<void*>(send_buffer), send_length};
+ const iovec receive_vector = {receive_buffer, receive_length};
+ return SendVector<T>(opcode, send ? &send_vector : nullptr, send ? 1 : 0,
+ receive ? &receive_vector : nullptr, receive ? 1 : 0);
+ }
+
+ template <typename T>
+ Status<T> SendVector(int opcode, const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count) {
+ Status<T> ret;
+ SendTransaction(opcode, &ret, send_vector, send_count, receive_vector,
+ receive_count);
+ return ret;
+ }
+
+ template <typename T, size_t send_count, size_t receive_count>
+ Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+ const iovec (&receive_vector)[receive_count]) {
+ return SendVector<T>(opcode, send_vector, send_count, receive_vector,
+ receive_count);
+ }
+
+ template <typename T, size_t send_count>
+ Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+ std::nullptr_t) {
+ return SendVector<T>(opcode, send_vector, send_count, nullptr, 0);
+ }
+
+ template <typename T, size_t receive_count>
+ Status<T> SendVector(int opcode, std::nullptr_t,
+ const iovec (&receive_vector)[receive_count]) {
+ return SendVector<T>(opcode, nullptr, 0, receive_vector, receive_count);
+ }
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override;
+ FileReference PushFileHandle(const BorrowedHandle& handle) override;
+ FileReference PushFileHandle(const RemoteHandle& handle) override;
+ ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ private:
+ bool EnsureStateAllocated();
+ void SendTransaction(int opcode, Status<void>* ret, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count);
+ void SendTransaction(int opcode, Status<int>* ret, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count);
+ void SendTransaction(int opcode, Status<LocalHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+ void SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+ void CheckDisconnect(int error);
+
+ template <typename T>
+ inline void CheckDisconnect(const Status<T>& status) {
+ if (!status)
+ CheckDisconnect(status.error());
+ }
+
+ Client& client_;
+ void* state_{nullptr};
+ bool state_allocated_{false};
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
new file mode 100644
index 0000000..e7ea475
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class ClientChannel {
+ public:
+ virtual ~ClientChannel() = default;
+
+ // Returns a tag that uniquely identifies a specific underlying IPC transport.
+ virtual uint32_t GetIpcTag() const = 0;
+
+ virtual int event_fd() const = 0;
+ virtual LocalChannelHandle& GetChannelHandle() = 0;
+ virtual void* AllocateTransactionState() = 0;
+ virtual void FreeTransactionState(void* state) = 0;
+
+ virtual Status<void> SendImpulse(int opcode, const void* buffer,
+ size_t length) = 0;
+
+ virtual Status<int> SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) = 0;
+ virtual Status<LocalHandle> SendWithFileHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+ virtual Status<LocalChannelHandle> SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+
+ virtual FileReference PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ void* transaction_state, const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) = 0;
+ virtual bool GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const = 0;
+ virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle) const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel_factory.h b/libs/vr/libpdx/private/pdx/client_channel_factory.h
new file mode 100644
index 0000000..a82ab70
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel_factory.h
@@ -0,0 +1,21 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+
+#include <pdx/client_channel.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class ClientChannelFactory {
+ public:
+ virtual ~ClientChannelFactory() = default;
+
+ virtual Status<std::unique_ptr<ClientChannel>> Connect(
+ int64_t timeout_ms) const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/file_handle.h b/libs/vr/libpdx/private/pdx/file_handle.h
new file mode 100644
index 0000000..b3c3ad7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/file_handle.h
@@ -0,0 +1,141 @@
+#ifndef ANDROID_PDX_FILE_HANDLE_H_
+#define ANDROID_PDX_FILE_HANDLE_H_
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace android {
+namespace pdx {
+
+enum class FileHandleMode {
+ Local,
+ Remote,
+ Borrowed,
+};
+
+// Manages ownership, sharing, and lifetime of file descriptors.
+template <FileHandleMode Mode>
+class FileHandle {
+ public:
+ static constexpr int kEmptyFileHandle = -1;
+
+ // Constructs an empty FileHandle object.
+ FileHandle() : fd_(kEmptyFileHandle) {}
+
+ // Constructs a FileHandle from an integer file descriptor and takes
+ // ownership.
+ explicit FileHandle(int fd) : fd_(fd) {}
+
+ // Constructs a FileHandle by opening |path|. The arguments follow the
+ // semantics of open().
+ FileHandle(const std::string& path, int flags, mode_t mode = 0) {
+ fd_ = open(path.c_str(), flags, mode);
+ }
+
+ // Constructs a FileHandle by opening |path| relative to |dir_fd|, following
+ // the semantics of openat().
+ FileHandle(const int directory_fd, const std::string& path, int flags,
+ mode_t mode = 0) {
+ fd_ = openat(directory_fd, path.c_str(), flags, mode);
+ }
+
+ // Move constructor that assumes ownership of the file descriptor, leaving the
+ // other FileHandle object empty.
+ FileHandle(FileHandle&& other) {
+ fd_ = other.fd_;
+ other.fd_ = kEmptyFileHandle;
+ }
+
+ // Returns a FileHandle as a duplicate handle of |fd|. Borrowed handles and
+ // handles in remote handle space are not duplicated.
+ static FileHandle AsDuplicate(const int fd) {
+ if (Mode == FileHandleMode::Local)
+ return FileHandle(dup(fd));
+ else
+ return FileHandle(fd);
+ }
+
+ // Destructor closes the file descriptor when non-empty.
+ ~FileHandle() { Close(); }
+
+ // Move assignment operator that assumes ownership of the underlying file
+ // descriptor, leaving the other FileHandle object empty.
+ FileHandle& operator=(FileHandle&& other) {
+ if (this != &other) {
+ Reset(other.fd_);
+ other.fd_ = kEmptyFileHandle;
+ }
+ return *this;
+ }
+
+ // Resets the underlying file handle to |fd|.
+ void Reset(int fd) {
+ Close();
+ fd_ = fd;
+ }
+
+ // Closes the underlying file descriptor when non-empty.
+ void Close() {
+ if (IsValid() && Mode == FileHandleMode::Local)
+ close(fd_);
+ fd_ = kEmptyFileHandle;
+ }
+
+ // Return the internal fd, passing ownership to the caller.
+ int Release() {
+ int release_fd = fd_;
+ fd_ = kEmptyFileHandle;
+ return release_fd;
+ }
+
+ // Duplicates the underlying file descriptor and returns a FileHandle that
+ // owns the new file descriptor. File descriptors are not duplicated when Mode
+ // is Remote or Borrowed.
+ FileHandle Duplicate() const {
+ return FileHandle(Mode == FileHandleMode::Local ? dup(fd_) : fd_);
+ }
+
+ FileHandle<FileHandleMode::Borrowed> Borrow() const {
+ return FileHandle<FileHandleMode::Borrowed>(Get());
+ }
+
+ // Gets the underlying file descriptor value.
+ int Get() const { return fd_; }
+ bool IsValid() const { return fd_ >= 0; }
+ explicit operator bool() const { return IsValid(); }
+
+ private:
+ int fd_;
+
+ FileHandle(const FileHandle&) = delete;
+ void operator=(const FileHandle&) = delete;
+};
+
+// Alias for a file handle in the local process' handle space.
+using LocalHandle = FileHandle<FileHandleMode::Local>;
+
+// Alias for a file handle in another process' handle space. Handles returned
+// from pushing a file object or channel must be stored in this type of handle
+// class, which doesn't close the underlying file descriptor. The underlying
+// file descriptor in this wrapper should not be passed to close() because doing
+// so would close an unrelated file descriptor in the local handle space.
+using RemoteHandle = FileHandle<FileHandleMode::Remote>;
+
+// Alias for borrowed handles in the local process' handle space. A borrowed
+// file handle is not close() because this wrapper does not own the underlying
+// file descriptor. Care must be take to ensure that a borrowed file handle
+// remains valid during use.
+using BorrowedHandle = FileHandle<FileHandleMode::Borrowed>;
+
+// FileReference is a 16 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local file
+// handle by calling Transaction.GetFileHandle() on client side and
+// Message.GetFileHandle() on service side.
+using FileReference = int16_t;
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_FILE_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/message_reader.h b/libs/vr/libpdx/private/pdx/message_reader.h
new file mode 100644
index 0000000..bc280cf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_reader.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_READER_H_
+#define ANDROID_PDX_MESSAGE_READER_H_
+
+#include <memory>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class InputResourceMapper {
+ public:
+ virtual bool GetFileHandle(FileReference ref, LocalHandle* handle) = 0;
+ virtual bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) = 0;
+
+ protected:
+ virtual ~InputResourceMapper() = default;
+};
+
+class MessageReader {
+ public:
+ // Pointers to start/end of the region in the read buffer.
+ using BufferSection = std::pair<const void*, const void*>;
+
+ virtual BufferSection GetNextReadBufferSection() = 0;
+ virtual void ConsumeReadBufferSectionData(const void* new_start) = 0;
+ virtual InputResourceMapper* GetInputResourceMapper() = 0;
+
+ protected:
+ virtual ~MessageReader() = default;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/message_writer.h b/libs/vr/libpdx/private/pdx/message_writer.h
new file mode 100644
index 0000000..0cb6e40
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_writer.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MESSAGE_WRITER_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class OutputResourceMapper {
+ public:
+ virtual FileReference PushFileHandle(const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(const BorrowedHandle& handle) = 0;
+ virtual FileReference PushFileHandle(const RemoteHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) = 0;
+
+ protected:
+ virtual ~OutputResourceMapper() = default;
+};
+
+class MessageWriter {
+ public:
+ virtual void* GetNextWriteBufferSection(size_t size) = 0;
+ virtual OutputResourceMapper* GetOutputResourceMapper() = 0;
+
+ protected:
+ virtual ~MessageWriter() = default;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
new file mode 100644
index 0000000..7780ee3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannel : public ClientChannel {
+ public:
+ MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+ MOCK_CONST_METHOD0(event_fd, int());
+ MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&());
+ MOCK_METHOD0(AllocateTransactionState, void*());
+ MOCK_METHOD1(FreeTransactionState, void(void* state));
+ MOCK_METHOD3(SendImpulse,
+ Status<void>(int opcode, const void* buffer, size_t length));
+ MOCK_METHOD6(SendWithInt,
+ Status<int>(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count));
+ MOCK_METHOD6(SendWithFileHandle,
+ Status<LocalHandle>(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count));
+ MOCK_METHOD6(SendWithChannelHandle,
+ Status<LocalChannelHandle>(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count));
+ MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+ const LocalHandle& handle));
+ MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+ const BorrowedHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(void* transaction_state,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(void* transaction_state,
+ const BorrowedChannelHandle& handle));
+ MOCK_CONST_METHOD3(GetFileHandle,
+ bool(void* transaction_state, FileReference ref,
+ LocalHandle* handle));
+ MOCK_CONST_METHOD3(GetChannelHandle,
+ bool(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle));
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
new file mode 100644
index 0000000..0190f5e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannelFactory : public ClientChannelFactory {
+ public:
+ MOCK_CONST_METHOD1(
+ Connect, Status<std::unique_ptr<ClientChannel>>(int64_t timeout_ms));
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_reader.h b/libs/vr/libpdx/private/pdx/mock_message_reader.h
new file mode 100644
index 0000000..85e96ef
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_reader.h
@@ -0,0 +1,27 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_READER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_READER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_reader.h>
+
+namespace android {
+namespace pdx {
+
+class MockInputResourceMapper : public InputResourceMapper {
+ public:
+ MOCK_METHOD2(GetFileHandle, bool(FileReference ref, LocalHandle* handle));
+ MOCK_METHOD2(GetChannelHandle,
+ bool(ChannelReference ref, LocalChannelHandle* handle));
+};
+
+class MockMessageReader : public MessageReader {
+ public:
+ MOCK_METHOD0(GetNextReadBufferSection, BufferSection());
+ MOCK_METHOD1(ConsumeReadBufferSectionData, void(const void* new_start));
+ MOCK_METHOD0(GetInputResourceMapper, InputResourceMapper*());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_writer.h b/libs/vr/libpdx/private/pdx/mock_message_writer.h
new file mode 100644
index 0000000..3c513d7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_writer.h
@@ -0,0 +1,32 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_writer.h>
+
+namespace android {
+namespace pdx {
+
+class MockOutputResourceMapper : public OutputResourceMapper {
+ public:
+ MOCK_METHOD1(PushFileHandle, FileReference(const LocalHandle& handle));
+ MOCK_METHOD1(PushFileHandle, FileReference(const BorrowedHandle& handle));
+ MOCK_METHOD1(PushFileHandle, FileReference(const RemoteHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ ChannelReference(const LocalChannelHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ ChannelReference(const BorrowedChannelHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ ChannelReference(const RemoteChannelHandle& handle));
+};
+
+class MockMessageWriter : public MessageWriter {
+ public:
+ MOCK_METHOD1(GetNextWriteBufferSection, void*(size_t size));
+ MOCK_METHOD0(GetOutputResourceMapper, OutputResourceMapper*());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
new file mode 100644
index 0000000..9b51d30
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
@@ -0,0 +1,24 @@
+#ifndef ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+
+class MockServiceDispatcher : public ServiceDispatcher {
+ public:
+ MOCK_METHOD1(AddService, int(const std::shared_ptr<Service>& service));
+ MOCK_METHOD1(RemoveService, int(const std::shared_ptr<Service>& service));
+ MOCK_METHOD0(ReceiveAndDispatch, int());
+ MOCK_METHOD1(ReceiveAndDispatch, int(int timeout));
+ MOCK_METHOD0(EnterDispatchLoop, int());
+ MOCK_METHOD1(SetCanceled, void(bool cancel));
+ MOCK_CONST_METHOD0(IsCanceled, bool());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
new file mode 100644
index 0000000..ead74d5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
@@ -0,0 +1,66 @@
+#ifndef ANDROID_PDX_MOCK_ENDPOINT_H_
+#define ANDROID_PDX_MOCK_ENDPOINT_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+
+class MockEndpoint : public Endpoint {
+ public:
+ MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+ MOCK_METHOD1(SetService, int(Service* service));
+ MOCK_METHOD2(SetChannel, int(int channel_id, Channel* channel));
+ MOCK_METHOD1(CloseChannel, int(int channel_id));
+ MOCK_METHOD3(ModifyChannelEvents,
+ int(int channel_id, int clear_mask, int set_mask));
+ MOCK_METHOD4(PushChannel,
+ Status<RemoteChannelHandle>(Message* message, int flags,
+ Channel* channel, int* channel_id));
+ MOCK_METHOD3(CheckChannel,
+ Status<int>(const Message* message, ChannelReference ref,
+ Channel** channel));
+ MOCK_METHOD1(DefaultHandleMessage, int(const MessageInfo& info));
+ MOCK_METHOD1(MessageReceive, int(Message* message));
+ MOCK_METHOD2(MessageReply, int(Message* message, int return_code));
+ MOCK_METHOD2(MessageReplyFd, int(Message* message, unsigned int push_fd));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ int(Message* message, const LocalChannelHandle& handle));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ int(Message* message, const BorrowedChannelHandle& handle));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ int(Message* message, const RemoteChannelHandle& handle));
+ MOCK_METHOD3(ReadMessageData, ssize_t(Message* message, const iovec* vector,
+ size_t vector_length));
+ MOCK_METHOD3(WriteMessageData, ssize_t(Message* message, const iovec* vector,
+ size_t vector_length));
+ MOCK_METHOD2(PushFileHandle,
+ FileReference(Message* message, const LocalHandle& handle));
+ MOCK_METHOD2(PushFileHandle,
+ FileReference(Message* message, const BorrowedHandle& handle));
+ MOCK_METHOD2(PushFileHandle,
+ FileReference(Message* message, const RemoteHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(Message* message,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(Message* message,
+ const BorrowedChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(Message* message,
+ const RemoteChannelHandle& handle));
+ MOCK_CONST_METHOD2(GetFileHandle,
+ LocalHandle(Message* message, FileReference ref));
+ MOCK_CONST_METHOD2(GetChannelHandle,
+ LocalChannelHandle(Message* message,
+ ChannelReference ref));
+ MOCK_METHOD0(AllocateMessageState, void*());
+ MOCK_METHOD1(FreeMessageState, void(void* state));
+ MOCK_METHOD0(Cancel, int());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
new file mode 100644
index 0000000..e006284
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
@@ -0,0 +1,184 @@
+#ifndef ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+#define ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/serialization.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides automatic serialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+// ArgumentEncoder<int(int, float)> encoder(writer);
+// encoder.EncodeArguments(1, 1.0);
+
+template <typename T>
+class ArgumentEncoder;
+
+// Specialization of ArgumentEncoder for void return types.
+template <typename... Args>
+class ArgumentEncoder<void(Args...)> {
+ public:
+ explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+ // Serializes the arguments as a tuple.
+ void EncodeArguments(Args... args) {
+ Serialize(std::forward_as_tuple(args...), writer_);
+ }
+
+ private:
+ MessageWriter* writer_;
+};
+
+// Specialization of ArgumentEncoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentEncoder<Return(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ReturnType = typename std::decay<Return>::type;
+
+ explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+ // Serializes the arguments as a tuple.
+ void EncodeArguments(Args... args) {
+ Serialize(std::forward_as_tuple(args...), writer_);
+ }
+
+ // Serializes the return value for rvalue references.
+ void EncodeReturn(const ReturnType& return_value) {
+ Serialize(return_value, writer_);
+ }
+
+ private:
+ MessageWriter* writer_;
+};
+
+// Utility to build an ArgumentEncoder from a function pointer and a message
+// writer.
+template <typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (*)(Args...), MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a method pointer and a message
+// writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (Class::*)(Args...), MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a const method pointer and a
+// message writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (Class::*)(Args...) const, MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a function type and a message
+// writer.
+template <typename Signature>
+inline ArgumentEncoder<Signature> MakeArgumentEncoder(MessageWriter* writer) {
+ return ArgumentEncoder<Signature>(writer);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Provides automatic deserialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+// auto decoder = MakeArgumentDecoder<std::string(void)>(reader);
+// ErrorType error = decoder.DecodeReturn(&return_value);
+
+template <typename T>
+class ArgumentDecoder;
+
+// Specialization of ArgumentDecoder for void return types.
+template <typename... Args>
+class ArgumentDecoder<void(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+
+ explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+ // Deserializes arguments into a tuple.
+ ArgsTupleType DecodeArguments(ErrorType* error) {
+ ArgsTupleType value;
+ *error = Deserialize(&value, reader_);
+ return value;
+ }
+
+ private:
+ MessageReader* reader_;
+};
+
+// Specialization of ArgumentDecoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentDecoder<Return(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+ using ReturnType = typename std::decay<Return>::type;
+
+ explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+ // Deserializes arguments into a tuple.
+ ArgsTupleType DecodeArguments(ErrorType* error) {
+ ArgsTupleType value;
+ *error = Deserialize(&value, reader_);
+ return value;
+ }
+
+ // Deserializes the return value.
+ ErrorType DecodeReturn(ReturnType* value) {
+ return Deserialize(value, reader_);
+ }
+
+ private:
+ MessageReader* reader_;
+};
+
+// Utility to build an ArgumentDecoder from a function pointer and a message
+// reader.
+template <typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (*)(Args...), MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a method pointer and a message
+// reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (Class::*)(Args...), MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a const method pointer and a
+// message reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (Class::*)(Args...) const, MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a function type and a message
+// reader.
+template <typename Signature>
+inline ArgumentDecoder<Signature> MakeArgumentDecoder(MessageReader* reader) {
+ return ArgumentDecoder<Signature>(reader);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
new file mode 100644
index 0000000..93d87f3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
@@ -0,0 +1,111 @@
+#ifndef ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+#define ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C array buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::vector, and may be substituted for std::vector during
+// serialization and deserialization. This substitution makes handling of C
+// arrays more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::vector arguments or return values.
+template <typename T>
+class ArrayWrapper {
+ public:
+ // Define types in the style of STL containers to support STL operators.
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ ArrayWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ ArrayWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ ArrayWrapper(pointer buffer, size_type size)
+ : ArrayWrapper(buffer, size, size) {}
+
+ ArrayWrapper(const ArrayWrapper& other) { *this = other; }
+
+ ArrayWrapper(ArrayWrapper&& other) { *this = std::move(other); }
+
+ ArrayWrapper& operator=(const ArrayWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ ArrayWrapper& operator=(ArrayWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ // Moves the end marker to |size|, clamping the end marker to the max capacity
+ // of the underlying array. This method does not change the size of the
+ // underlying array.
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+ArrayWrapper<T> WrapArray(T* buffer, SizeType size) {
+ return ArrayWrapper<T>(buffer, size);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
new file mode 100644
index 0000000..aa86531
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
@@ -0,0 +1,177 @@
+#ifndef ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+#define ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class supports serialization of
+// buffers as raw bytes.
+template <typename T>
+class BufferWrapper;
+
+template <typename T>
+class BufferWrapper<T*> {
+ public:
+ // Define types in the style of STL containers to support STL operators.
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ BufferWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ BufferWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ BufferWrapper(pointer buffer, size_type size)
+ : BufferWrapper(buffer, size, size) {}
+
+ BufferWrapper(const BufferWrapper& other) { *this = other; }
+
+ BufferWrapper(BufferWrapper&& other) { *this = std::move(other); }
+
+ BufferWrapper& operator=(const BufferWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ BufferWrapper& operator=(BufferWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+template <typename T, typename Allocator>
+class BufferWrapper<std::vector<T, Allocator>> {
+ public:
+ using BufferType = typename std::vector<T, Allocator>;
+ using value_type = typename BufferType::value_type;
+ using size_type = typename BufferType::size_type;
+ using reference = typename BufferType::reference;
+ using const_reference = typename BufferType::const_reference;
+ using pointer = typename BufferType::pointer;
+ using const_pointer = typename BufferType::const_pointer;
+ using iterator = typename BufferType::iterator;
+ using const_iterator = typename BufferType::const_iterator;
+
+ BufferWrapper() {}
+ BufferWrapper(const BufferType& buffer) : buffer_(buffer) {}
+ BufferWrapper(const BufferType& buffer, const Allocator& allocator)
+ : buffer_(buffer, allocator) {}
+ BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {}
+ BufferWrapper(BufferType&& buffer, const Allocator& allocator)
+ : buffer_(std::move(buffer), allocator) {}
+ BufferWrapper(const BufferWrapper&) = default;
+ BufferWrapper(BufferWrapper&&) = default;
+ BufferWrapper& operator=(const BufferWrapper&) = default;
+ BufferWrapper& operator=(BufferWrapper&&) = default;
+
+ pointer data() { return buffer_.data(); }
+ const_pointer data() const { return buffer_.data(); }
+
+ iterator begin() { return buffer_.begin(); }
+ iterator end() { return buffer_.end(); }
+ const_iterator begin() const { return buffer_.begin(); }
+ const_iterator end() const { return buffer_.end(); }
+
+ size_type size() const { return buffer_.size(); }
+ size_type max_size() const { return buffer_.capacity(); }
+ size_type capacity() const { return buffer_.capacity(); }
+
+ void resize(size_type size) { buffer_.resize(size); }
+ void reserve(size_type size) { buffer_.reserve(size); }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ BufferType& buffer() { return buffer_; }
+ const BufferType& buffer() const { return buffer_; }
+
+ private:
+ BufferType buffer_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+BufferWrapper<T*> WrapBuffer(T* buffer, SizeType size) {
+ return BufferWrapper<T*>(buffer, size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<std::uint8_t*> WrapBuffer(void* buffer, SizeType size) {
+ return BufferWrapper<std::uint8_t*>(static_cast<std::uint8_t*>(buffer), size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<const std::uint8_t*> WrapBuffer(const void* buffer,
+ SizeType size) {
+ return BufferWrapper<const std::uint8_t*>(
+ static_cast<const std::uint8_t*>(buffer), size);
+}
+
+template <typename T, typename Allocator = std::allocator<T>>
+BufferWrapper<std::vector<T, Allocator>> WrapBuffer(
+ std::vector<T, Allocator>&& buffer) {
+ return BufferWrapper<std::vector<T, Allocator>>(
+ std::forward<std::vector<T, Allocator>>(buffer));
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
new file mode 100644
index 0000000..5ce34f8
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+#define ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Copies const, void, and reference qualifiers from type T to type U, such that
+// the new type U' carries the same cv-reference qualifiers as T, with the same
+// underlying type as U.
+template <typename T, typename U>
+class CopyCVReference {
+ private:
+ using R = typename std::remove_reference<T>::type;
+ using U1 =
+ typename std::conditional<std::is_const<R>::value,
+ typename std::add_const<U>::type, U>::type;
+ using U2 =
+ typename std::conditional<std::is_volatile<R>::value,
+ typename std::add_volatile<U1>::type, U1>::type;
+ using U3 =
+ typename std::conditional<std::is_lvalue_reference<T>::value,
+ typename std::add_lvalue_reference<U2>::type,
+ U2>::type;
+ using U4 =
+ typename std::conditional<std::is_rvalue_reference<T>::value,
+ typename std::add_rvalue_reference<U3>::type,
+ U3>::type;
+
+ public:
+ using Type = U4;
+};
+
+template <typename T, typename U>
+using CopyCVReferenceType = typename CopyCVReference<T, U>::Type;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
new file mode 100644
index 0000000..b6e2980
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
@@ -0,0 +1,47 @@
+#ifndef ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+#define ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Allocator adaptor that interposes construct() calls to convert value
+// initialization into default initialization. All standard containers
+// value-initialize their elements when constructed with a single size_type
+// argument or when grown by a call to resize. This allocator avoids potentially
+// costly value-initialization in these situations for value types that are
+// default constructible. As a consequence, elements of non-class types are left
+// uninitialized; this is desirable when using std::vector as a resizable
+// buffer, for example.
+template <typename T, typename Allocator = std::allocator<T>>
+class DefaultInitializationAllocator : public Allocator {
+ typedef std::allocator_traits<Allocator> AllocatorTraits;
+
+ public:
+ template <typename U>
+ struct rebind {
+ using other = DefaultInitializationAllocator<
+ U, typename AllocatorTraits::template rebind_alloc<U>>;
+ };
+
+ using Allocator::Allocator;
+
+ template <typename U>
+ void construct(U* pointer) noexcept(
+ std::is_nothrow_default_constructible<U>::value) {
+ ::new (static_cast<void*>(pointer)) U;
+ }
+ template <typename U, typename... Args>
+ void construct(U* pointer, Args&&... args) {
+ AllocatorTraits::construct(static_cast<Allocator&>(*this), pointer,
+ std::forward<Args>(args)...);
+ }
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/encoding.h b/libs/vr/libpdx/private/pdx/rpc/encoding.h
new file mode 100644
index 0000000..f51d807
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/encoding.h
@@ -0,0 +1,616 @@
+#ifndef ANDROID_PDX_RPC_ENCODING_H_
+#define ANDROID_PDX_RPC_ENCODING_H_
+
+#include <array>
+#include <cstdint>
+#include <cstring>
+#include <map>
+#include <numeric>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+#include "array_wrapper.h"
+#include "buffer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This library uses a subset, or profile, of MessagePack (http://msgpack.org)
+// to encode supported data types during serialization and to verify the
+// expected data types during deserialization. One notable deviation from the
+// MessagePack specification is that little-endian byte order is used for
+// multi-byte numeric types to avoid unnecessary conversion on nearly all
+// relevant architectures.
+//
+// Some data types, integers for example, support multiple encoding strategies.
+// This library attempts to optimize for space based on the value of such types.
+// However, during decode all valid encodings for a given type are accepted.
+
+// Prefix byte for type encodings. This is the complete list of prefix bytes
+// from the MessagePack specification, even though not all types are used by
+// this library.
+enum EncodingPrefix {
+ ENCODING_TYPE_POSITIVE_FIXINT = 0x00,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN = 0x00,
+ ENCODING_TYPE_POSITIVE_FIXINT_MAX = 0x7f,
+ ENCODING_TYPE_POSITIVE_FIXINT_MASK = 0x7f,
+ ENCODING_TYPE_FIXMAP = 0x80,
+ ENCODING_TYPE_FIXMAP_MIN = 0x80,
+ ENCODING_TYPE_FIXMAP_MAX = 0x8f,
+ ENCODING_TYPE_FIXMAP_MASK = 0x0f,
+ ENCODING_TYPE_FIXARRAY = 0x90,
+ ENCODING_TYPE_FIXARRAY_MIN = 0x90,
+ ENCODING_TYPE_FIXARRAY_MAX = 0x9f,
+ ENCODING_TYPE_FIXARRAY_MASK = 0x0f,
+ ENCODING_TYPE_FIXSTR = 0xa0,
+ ENCODING_TYPE_FIXSTR_MIN = 0xa0,
+ ENCODING_TYPE_FIXSTR_MAX = 0xbf,
+ ENCODING_TYPE_FIXSTR_MASK = 0x1f,
+ ENCODING_TYPE_NIL = 0xc0,
+ ENCODING_TYPE_RESERVED = 0xc1,
+ ENCODING_TYPE_FALSE = 0xc2,
+ ENCODING_TYPE_TRUE = 0xc3,
+ ENCODING_TYPE_BIN8 = 0xc4,
+ ENCODING_TYPE_BIN16 = 0xc5,
+ ENCODING_TYPE_BIN32 = 0xc6,
+ ENCODING_TYPE_EXT8 = 0xc7,
+ ENCODING_TYPE_EXT16 = 0xc8,
+ ENCODING_TYPE_EXT32 = 0xc9,
+ ENCODING_TYPE_FLOAT32 = 0xca,
+ ENCODING_TYPE_FLOAT64 = 0xcb,
+ ENCODING_TYPE_UINT8 = 0xcc,
+ ENCODING_TYPE_UINT16 = 0xcd,
+ ENCODING_TYPE_UINT32 = 0xce,
+ ENCODING_TYPE_UINT64 = 0xcf,
+ ENCODING_TYPE_INT8 = 0xd0,
+ ENCODING_TYPE_INT16 = 0xd1,
+ ENCODING_TYPE_INT32 = 0xd2,
+ ENCODING_TYPE_INT64 = 0xd3,
+ ENCODING_TYPE_FIXEXT1 = 0xd4,
+ ENCODING_TYPE_FIXEXT2 = 0xd5,
+ ENCODING_TYPE_FIXEXT4 = 0xd6,
+ ENCODING_TYPE_FIXEXT8 = 0xd7,
+ ENCODING_TYPE_FIXEXT16 = 0xd8,
+ ENCODING_TYPE_STR8 = 0xd9,
+ ENCODING_TYPE_STR16 = 0xda,
+ ENCODING_TYPE_STR32 = 0xdb,
+ ENCODING_TYPE_ARRAY16 = 0xdc,
+ ENCODING_TYPE_ARRAY32 = 0xdd,
+ ENCODING_TYPE_MAP16 = 0xde,
+ ENCODING_TYPE_MAP32 = 0xdf,
+ ENCODING_TYPE_NEGATIVE_FIXINT = 0xe0,
+ ENCODING_TYPE_NEGATIVE_FIXINT_MIN = 0xe0,
+ ENCODING_TYPE_NEGATIVE_FIXINT_MAX = 0xff,
+};
+
+// Base encoding classes grouping multi-strategy encodings.
+enum EncodingClass {
+ ENCODING_CLASS_BOOL,
+ ENCODING_CLASS_NIL,
+ ENCODING_CLASS_INT,
+ ENCODING_CLASS_UINT,
+ ENCODING_CLASS_FLOAT,
+ ENCODING_CLASS_ARRAY,
+ ENCODING_CLASS_MAP,
+ ENCODING_CLASS_STRING,
+ ENCODING_CLASS_BINARY,
+ ENCODING_CLASS_EXTENSION,
+};
+
+// Encoding prefixes are unsigned bytes.
+typedef std::uint8_t EncodingType;
+
+// Extension encoding types defined by this library.
+enum EncodingExtType : int8_t {
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR,
+ ENCODING_EXT_TYPE_CHANNEL_HANDLE,
+};
+
+// Encoding predicates. Determines whether the given encoding is of a specific
+// type.
+inline constexpr bool IsFixintEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUnsignedFixintEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt8Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt8Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt16Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt16Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_INT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_UINT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_INT32:
+ case ENCODING_TYPE_INT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_UINT32:
+ case ENCODING_TYPE_UINT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixmapEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixarrayEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixstrEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixextEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXEXT1:
+ case ENCODING_TYPE_FIXEXT2:
+ case ENCODING_TYPE_FIXEXT4:
+ case ENCODING_TYPE_FIXEXT8:
+ case ENCODING_TYPE_FIXEXT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFloat32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FLOAT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFloat64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FLOAT32:
+ case ENCODING_TYPE_FLOAT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsBoolEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FALSE:
+ case ENCODING_TYPE_TRUE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr std::size_t GetFixstrSize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXSTR_MASK;
+}
+
+inline constexpr std::size_t GetFixarraySize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXARRAY_MASK;
+}
+
+inline constexpr std::size_t GetFixmapSize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXMAP_MASK;
+}
+
+inline constexpr std::size_t GetFixextSize(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXEXT1:
+ return 1;
+ case ENCODING_TYPE_FIXEXT2:
+ return 2;
+ case ENCODING_TYPE_FIXEXT4:
+ return 4;
+ case ENCODING_TYPE_FIXEXT8:
+ return 8;
+ case ENCODING_TYPE_FIXEXT16:
+ return 16;
+ default:
+ return 0; // Invalid fixext size.
+ }
+}
+
+// Gets the size of the encoding in bytes, not including external payload data.
+inline constexpr std::size_t GetEncodingSize(EncodingType encoding) {
+ switch (encoding) {
+ // Encoding is fully contained within the type value.
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+ case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+ case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+ case ENCODING_TYPE_NIL:
+ case ENCODING_TYPE_RESERVED:
+ case ENCODING_TYPE_FALSE:
+ case ENCODING_TYPE_TRUE:
+ return 1;
+
+ // Encoding type followed by one-byte size or immediate value.
+ case ENCODING_TYPE_BIN8:
+ case ENCODING_TYPE_EXT8:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_STR8:
+ // Encoding type followed by one-byte extension type.
+ case ENCODING_TYPE_FIXEXT1:
+ case ENCODING_TYPE_FIXEXT2:
+ case ENCODING_TYPE_FIXEXT4:
+ case ENCODING_TYPE_FIXEXT8:
+ case ENCODING_TYPE_FIXEXT16:
+ return 2;
+
+ // Encoding type followed by two-byte size or immediate value.
+ case ENCODING_TYPE_BIN16:
+ case ENCODING_TYPE_EXT16:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_STR16:
+ case ENCODING_TYPE_ARRAY16:
+ case ENCODING_TYPE_MAP16:
+ return 3;
+
+ // Encoding type followed by four-byte size or immediate value.
+ case ENCODING_TYPE_BIN32:
+ case ENCODING_TYPE_EXT32:
+ case ENCODING_TYPE_FLOAT32:
+ case ENCODING_TYPE_UINT32:
+ case ENCODING_TYPE_INT32:
+ case ENCODING_TYPE_STR32:
+ case ENCODING_TYPE_ARRAY32:
+ case ENCODING_TYPE_MAP32:
+ return 5;
+
+ // Encoding type followed by eight-byte immediate value.
+ case ENCODING_TYPE_FLOAT64:
+ case ENCODING_TYPE_UINT64:
+ case ENCODING_TYPE_INT64:
+ return 9;
+
+ default:
+ return 0;
+ }
+}
+
+// Encoding for standard types. Each supported data type has an associated
+// encoding or set of encodings. These functions determine the MessagePack
+// encoding based on the data type, value, and size of their arguments.
+
+inline constexpr EncodingType EncodeArrayType(std::size_t size) {
+ if (size < (1U << 4))
+ return ENCODING_TYPE_FIXARRAY | (size & ENCODING_TYPE_FIXARRAY_MASK);
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_ARRAY16;
+ else
+ return ENCODING_TYPE_ARRAY32;
+}
+
+inline constexpr EncodingType EncodeMapType(std::size_t size) {
+ if (size < (1U << 4))
+ return ENCODING_TYPE_FIXMAP | (size & ENCODING_TYPE_FIXMAP_MASK);
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_MAP16;
+ else
+ return ENCODING_TYPE_MAP32;
+}
+
+inline constexpr EncodingType EncodeStringType(std::size_t size) {
+ if (size < (1U << 5))
+ return ENCODING_TYPE_FIXSTR | (size & ENCODING_TYPE_FIXSTR_MASK);
+ else if (size < (1U << 8))
+ return ENCODING_TYPE_STR8;
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_STR16;
+ else
+ return ENCODING_TYPE_STR32;
+}
+
+inline constexpr EncodingType EncodeBinType(std::size_t size) {
+ if (size < (1U << 8))
+ return ENCODING_TYPE_BIN8;
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_BIN16;
+ else
+ return ENCODING_TYPE_BIN32;
+}
+
+inline EncodingType EncodeType(const EmptyVariant& /*empty*/) {
+ return ENCODING_TYPE_NIL;
+}
+
+// Variant is encoded as a single-element map, with the type index as the key.
+template <typename... Types>
+inline EncodingType EncodeType(const Variant<Types...>& /*variant*/) {
+ return EncodeMapType(1);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const StringWrapper<T>& value) {
+ return EncodeStringType(value.length());
+}
+
+inline constexpr EncodingType EncodeType(const std::string& value) {
+ return EncodeStringType(value.length());
+}
+
+template <typename T, std::size_t Size>
+inline constexpr EncodingType EncodeType(const std::array<T, Size>& /*value*/) {
+ return EncodeArrayType(Size);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const ArrayWrapper<T>& value) {
+ return EncodeArrayType(value.size());
+}
+
+template <typename T, typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::vector<T, Allocator>& value) {
+ return EncodeArrayType(value.size());
+}
+
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::map<Key, T, Compare, Allocator>& value) {
+ return EncodeMapType(value.size());
+}
+
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value) {
+ return EncodeMapType(value.size());
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const BufferWrapper<T>& value) {
+ // BIN size is in bytes.
+ return EncodeBinType(value.size() *
+ sizeof(typename BufferWrapper<T>::value_type));
+}
+
+template <typename T, typename U>
+inline constexpr EncodingType EncodeType(const std::pair<T, U>& /*value*/) {
+ return EncodeArrayType(2);
+}
+
+template <typename... T>
+inline constexpr EncodingType EncodeType(const std::tuple<T...>& /*value*/) {
+ return EncodeArrayType(sizeof...(T));
+}
+
+// FileHandle is encoded as a FIXEXT2 with a type code for "FileDescriptor"
+// and a signed 16-bit index into the pushed fd array. Empty file descriptor
+// have an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr EncodingType EncodeType(const FileHandle<Mode>& /*fd*/) {
+ return ENCODING_TYPE_FIXEXT2;
+}
+
+// ChannelHandle is encoded as a FIXEXT4 with a type of
+// ENCODING_EXT_TYPE_CHANNEL_HANDLE and a signed 32-bit value representing
+// a client channel in a remote process. Empty handle has a value of -1.
+template <ChannelHandleMode Mode>
+inline constexpr EncodingType EncodeType(
+ const ChannelHandle<Mode>& /*handle*/) {
+ return ENCODING_TYPE_FIXEXT4;
+}
+
+inline constexpr EncodingType EncodeType(const bool& value) {
+ return value ? ENCODING_TYPE_TRUE : ENCODING_TYPE_FALSE;
+}
+
+// Type 'char' is a little bit special in that it is distinct from 'signed char'
+// and 'unsigned char'. Treating it as an unsigned 8-bit value is safe for
+// encoding purposes and nicely handles 7-bit ASCII encodings as FIXINT.
+inline constexpr EncodingType EncodeType(const char& value) {
+ if (value < static_cast<char>(1 << 7))
+ return value;
+ else
+ return ENCODING_TYPE_UINT8;
+}
+
+inline constexpr EncodingType EncodeType(const uint8_t& value) {
+ if (value < (1U << 7))
+ return value;
+ else
+ return ENCODING_TYPE_UINT8;
+}
+inline constexpr EncodingType EncodeType(const int8_t& value) {
+ if (value >= -32)
+ return value;
+ else
+ return ENCODING_TYPE_INT8;
+}
+inline constexpr EncodingType EncodeType(const uint16_t& value) {
+ if (value < (1U << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1U << 8))
+ return ENCODING_TYPE_UINT8;
+ else
+ return ENCODING_TYPE_UINT16;
+}
+inline constexpr EncodingType EncodeType(const int16_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127)
+ return ENCODING_TYPE_INT8;
+ else
+ return ENCODING_TYPE_INT16;
+}
+inline constexpr EncodingType EncodeType(const uint32_t& value) {
+ if (value < (1U << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1U << 8))
+ return ENCODING_TYPE_UINT8;
+ else if (value < (1U << 16))
+ return ENCODING_TYPE_UINT16;
+ else
+ return ENCODING_TYPE_UINT32;
+}
+inline constexpr EncodingType EncodeType(const int32_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127)
+ return ENCODING_TYPE_INT8;
+ else if (value >= -32768 && value <= 32767)
+ return ENCODING_TYPE_INT16;
+ else
+ return ENCODING_TYPE_INT32;
+}
+inline constexpr EncodingType EncodeType(const uint64_t& value) {
+ if (value < (1ULL << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1ULL << 8))
+ return ENCODING_TYPE_UINT8;
+ else if (value < (1ULL << 16))
+ return ENCODING_TYPE_UINT16;
+ else if (value < (1ULL << 32))
+ return ENCODING_TYPE_UINT32;
+ else
+ return ENCODING_TYPE_UINT64;
+}
+inline constexpr EncodingType EncodeType(const int64_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127) // Effectively [-128, -32).
+ return ENCODING_TYPE_INT8;
+ else if (value >= -32768 && value <= 32767)
+ return ENCODING_TYPE_INT16;
+ else if (value >= -2147483648 && value <= 2147483647)
+ return ENCODING_TYPE_INT32;
+ else
+ return ENCODING_TYPE_INT64;
+}
+
+inline constexpr EncodingType EncodeType(const float& /*value*/) {
+ return ENCODING_TYPE_FLOAT32;
+}
+
+inline constexpr EncodingType EncodeType(const double& /*value*/) {
+ return ENCODING_TYPE_FLOAT64;
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ENCODING_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/enumeration.h b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
new file mode 100644
index 0000000..7a35d31
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_PDX_RPC_ENUMERATION_H_
+#define ANDROID_PDX_RPC_ENUMERATION_H_
+
+#include <pdx/rpc/sequence.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility for manipulating lists of types. Provides operations to lookup an
+// element by type or index.
+
+namespace detail {
+
+// Helper type that captures type and index for each element of a type
+// enumeration.
+template <std::size_t I, typename T>
+struct IndexedElement {
+ using Type = T;
+ static constexpr std::size_t Index = I;
+};
+
+// Helper type that captures an IndexSequence and corresponding list of types.
+template <typename Is, typename... Ts>
+struct ElementIndexer;
+
+// Partial specialization that generates an instantiation of IndexElement<I, T>
+// for each element of a type enumeration using inheritance. Once a type
+// enumeration is instantiated this way the compiler is able to deduce either I
+// or T from the other using the method below.
+template <std::size_t... Is, typename... Ts>
+struct ElementIndexer<IndexSequence<Is...>, Ts...> : IndexedElement<Is, Ts>... {
+};
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given T.
+template <typename T, std::size_t I>
+static IndexedElement<I, T> SelectElementByType(IndexedElement<I, T>);
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given I.
+template <std::size_t I, typename T>
+static IndexedElement<I, T> SelectElementByIndex(IndexedElement<I, T>);
+
+} // namespace detail
+
+// Deduces the IndexedElement<I, T> given T and a type sequence Ts. This may be
+// used to determine the index of T within Ts at compile time.
+template <typename T, typename... Ts>
+using ElementForType = decltype(detail::SelectElementByType<T>(
+ detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+// Deduces the IndexedElement<I, T> given I and a type sequence Ts. This may be
+// used to determine the type of the element at index I within Ts at compile
+// time. Tuple operations may also be used to accomplish the same task, however
+// this implementation is provided here for symmetry.
+template <std::size_t I, typename... Ts>
+using ElementForIndex = decltype(detail::SelectElementByIndex<I>(
+ detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ENUMERATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/find_replace.h b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
new file mode 100644
index 0000000..b4b086b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
@@ -0,0 +1,45 @@
+#ifndef ANDROID_PDX_RPC_FIND_REPLACE_H_
+#define ANDROID_PDX_RPC_FIND_REPLACE_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/copy_cv_reference.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to capture types to find and replace.
+template <typename Find, typename Replace>
+struct FindReplace;
+
+template <typename T, typename U>
+using IsSameBaseType = typename std::is_same<typename std::decay<T>::type,
+ typename std::decay<U>::type>;
+
+// Replaces the type Subject with type Replace if type Subject is the same type
+// as type Find, excluding cv-reference qualifiers in the match.
+template <typename Find, typename Replace, typename Subject>
+using ReplaceType =
+ typename std::conditional<IsSameBaseType<Find, Subject>::value,
+ CopyCVReferenceType<Subject, Replace>,
+ Subject>::type;
+
+// Determines whether the type Find (excluding cv-reference qualifiers) is in
+// the given parameter pack.
+template <typename Find, typename... Types>
+struct ContainsType : std::true_type {};
+
+template <typename Find, typename First, typename... Rest>
+struct ContainsType<Find, First, Rest...>
+ : std::conditional<IsSameBaseType<Find, First>::value, std::true_type,
+ ContainsType<Find, Rest...>>::type {};
+
+template <typename Find>
+struct ContainsType<Find> : std::false_type {};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_FIND_REPLACE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/function_traits.h b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
new file mode 100644
index 0000000..5fdad72
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+#define ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/type_operators.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type to capture return and argument types of a function signature.
+// Examples:
+// typedef SignatureType<int(int)> SignatureType;
+// using SignatureType = SignatureType<int(int)>;
+template <typename T>
+using SignatureType = T;
+
+// Utility class to extract return and argument types from function types.
+// Provides nested types for return value, arguments, and full signature. Also
+// provides accessor types for individual arguments, argument-arity, and type
+// substitution.
+template <typename T>
+struct FunctionTraits;
+
+template <typename Return_, typename... Args_>
+struct FunctionTraits<Return_(Args_...)> {
+ using Return = Return_;
+ using Args = std::tuple<Args_...>;
+ using Signature = SignatureType<Return_(Args_...)>;
+
+ enum : std::size_t { Arity = sizeof...(Args_) };
+
+ template <std::size_t Index>
+ using Arg = typename std::tuple_element<Index, Args>::type;
+
+ template <typename... Params>
+ using RewriteArgs =
+ SignatureType<Return_(ConditionalRewrite<Args_, Params>...)>;
+
+ template <typename ReturnType, typename... Params>
+ using RewriteSignature =
+ SignatureType<ConditionalRewrite<Return_, ReturnType>(
+ ConditionalRewrite<Args_, Params>...)>;
+
+ template <typename ReturnType>
+ using RewriteReturn =
+ SignatureType<ConditionalRewrite<Return_, ReturnType>(Args_...)>;
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
new file mode 100644
index 0000000..aeae9d3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -0,0 +1,148 @@
+#ifndef ANDROID_PDX_RPC_MACROS_H_
+#define ANDROID_PDX_RPC_MACROS_H_
+
+// Macros to apply other macros over all elements in a list.
+//
+// For example, for a macro A(x) and B(x, y):
+// - FOR_EACH(A, 1, 2, 3) -> A(1) A(2) A(3).
+// - FOR_EACH_BINARY(B, z, 1, 2, 3) -> B(z, 1) B(z, 2) B(z, 3)
+// - FOR_EACH_LIST(A, 1, 2, 3) -> A(1), B(2), C(3)
+// - FOR_EACH_BINARY_LIST(B, z, 1, 2, 3) -> B(z, 1), B(z, 2), B(z, 3)
+//
+// Empty lists are supported and will produce no output.
+
+// Recursive expansion macros.
+#define _PDX_EXPAND0(...) __VA_ARGS__
+#define _PDX_EXPAND1(...) _PDX_EXPAND0(_PDX_EXPAND0(_PDX_EXPAND0(__VA_ARGS__)))
+#define _PDX_EXPAND2(...) _PDX_EXPAND1(_PDX_EXPAND1(_PDX_EXPAND1(__VA_ARGS__)))
+#define _PDX_EXPAND3(...) _PDX_EXPAND2(_PDX_EXPAND2(_PDX_EXPAND2(__VA_ARGS__)))
+#define _PDX_EXPAND4(...) _PDX_EXPAND3(_PDX_EXPAND3(_PDX_EXPAND3(__VA_ARGS__)))
+#define _PDX_EXPAND(...) _PDX_EXPAND4(_PDX_EXPAND4(_PDX_EXPAND4(__VA_ARGS__)))
+
+// Required to workaround a bug in the VC++ preprocessor.
+#define _PDX_INDIRECT_EXPAND(macro, args) macro args
+
+// Defines a step separation for macro expansion.
+#define _PDX_SEPARATOR
+
+// Clears any remaining contents wrapped in parentheses.
+#define _PDX_CLEAR(...)
+
+// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
+
+// Returns the first argument of a list.
+#define _PDX_FIRST_ARG(first, ...) first
+
+// Returns the second argument of a list.
+#define _PDX_SECOND_ARG(_, second, ...) second
+
+// Expands the arguments and introduces a separator.
+#define _PDX_EXPAND_NEXT_FUNC(_, next_func, ...) \
+ _PDX_INDIRECT_EXPAND(_PDX_SECOND_ARG, (_, next_func)) \
+ _PDX_SEPARATOR
+
+// Returns next_func if the next element is not (), or _PDX_CLEAR
+// otherwise.
+//
+// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+#define _PDX_NEXT_FUNC(next_element, next_func) \
+ _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
+
+// Macros for the unary version of PDX_FOR_EACH.
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_1(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_2(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_1
+// otherwise.
+#define _PDX_HANDLE_EMPTY_ARGS(macro, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_1) \
+ (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH(macro, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS(macro, __VA_ARGS__))
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_1(macro, head, next, ...) \
+ , macro(head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_2(macro, head, next, ...) \
+ , macro(head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the start of a list.
+#define _PDX_APPLY_LIST_0(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_LIST_0
+// otherwise.
+#define _PDX_HANDLE_EMPTY_LIST(macro, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_LIST_0) \
+ (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH_LIST(macro, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST(macro, __VA_ARGS__))
+
+// Macros for the binary version of PDX_FOR_EACH.
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_1(macro, arg, head, next, ...) \
+ macro(arg, head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_2)(macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_2(macro, arg, head, next, ...) \
+ macro(arg, head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_1)(macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_ARGS that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_1) \
+ (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY(macro, arg, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, __VA_ARGS__))
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_1(macro, arg, head, next, ...) \
+ , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_2)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_2(macro, arg, head, next, ...) \
+ , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the start of a list. Duplicated for macro
+// recursive expansion.
+#define _PDX_APPLY_BINARY_LIST_0(macro, arg, head, next, ...) \
+ macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_LIST that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_LIST_0) \
+ (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY_LIST(macro, arg, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, __VA_ARGS__))
+
+#endif // ANDROID_PDX_RPC_MACROS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/message_buffer.h b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
new file mode 100644
index 0000000..ba4e86e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+#define ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+
+#include <pdx/rpc/thread_local_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type for thread-local buffers, providing suitable defaults for most
+// situations. Independent thread-local buffers may be created by using
+// different types for Slot -- ThreadLocalSlot, ThreadLocalTypedSlot and
+// ThreadLocalIndexedSlot provide utilities for building these types.
+template <typename Slot, std::size_t Capacity = 4096, typename T = std::uint8_t,
+ typename Allocator = DefaultInitializationAllocator<T>>
+using MessageBuffer = ThreadLocalBuffer<T, Allocator, Capacity, Slot>;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/payload.h b/libs/vr/libpdx/private/pdx/rpc/payload.h
new file mode 100644
index 0000000..a48a64c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/payload.h
@@ -0,0 +1,157 @@
+#ifndef ANDROID_PDX_RPC_PAYLOAD_H_
+#define ANDROID_PDX_RPC_PAYLOAD_H_
+
+#include <iterator>
+
+#include <pdx/client.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Implements the payload interface, required by Serialize/Deserialize, on top
+// of a thread-local MessageBuffer.
+template <typename Slot>
+class MessagePayload {
+ public:
+ using BufferType = typename MessageBuffer<Slot>::BufferType;
+ using ValueType = typename MessageBuffer<Slot>::ValueType;
+
+ // Constructs a MessagePayload with an empty TLS buffer.
+ MessagePayload()
+ : buffer_(MessageBuffer<Slot>::GetEmptyBuffer()),
+ cursor_(buffer_.begin()),
+ const_cursor_(buffer_.cbegin()) {}
+
+ // Returns a reference to the cursor iterator to be used during serialization
+ // into the underlying MessageBuffer.
+ typename BufferType::iterator& Cursor() { return cursor_; }
+
+ // Returns a reference to the const cursor iterator at the beginning of the
+ // underlying MessageBuffer.
+ typename BufferType::const_iterator& ConstCursor() { return const_cursor_; }
+
+ // Returns a const iterator marking the end of the underlying MessageBuffer.
+ typename BufferType::const_iterator ConstEnd() { return buffer_.cend(); }
+
+ // Resizes the underlying MessageBuffer and sets the cursor to the beginning.
+ void Resize(std::size_t size) {
+ buffer_.resize(size);
+ cursor_ = buffer_.begin();
+ const_cursor_ = buffer_.cbegin();
+ }
+
+ // Resets the read cursor so that data can be read from the buffer again.
+ void Rewind() { const_cursor_ = buffer_.cbegin(); }
+
+ // Adds |size| bytes to the size of the underlying MessageBuffer and positions
+ // the cursor at the beginning of the extended region.
+ void Extend(std::size_t size) {
+ const std::size_t offset = buffer_.size();
+ buffer_.resize(offset + size);
+ cursor_ = buffer_.begin() + offset;
+ const_cursor_ = buffer_.cbegin() + offset;
+ }
+
+ // Clears the underlying MessageBuffer and sets the cursor to the beginning.
+ void Clear() {
+ buffer_.clear();
+ cursor_ = buffer_.begin();
+ const_cursor_ = buffer_.cbegin();
+ }
+
+ ValueType* Data() { return buffer_.data(); }
+ const ValueType* Data() const { return buffer_.data(); }
+ std::size_t Size() const { return buffer_.size(); }
+ std::size_t Capacity() const { return buffer_.capacity(); }
+
+ private:
+ BufferType& buffer_;
+ typename BufferType::iterator cursor_;
+ typename BufferType::const_iterator const_cursor_;
+
+ MessagePayload(const MessagePayload<Slot>&) = delete;
+ void operator=(const MessagePayload<Slot>&) = delete;
+};
+
+// Implements the payload interface for service-side RPCs. Handles translating
+// between remote and local handle spaces automatically.
+template <typename Slot>
+class ServicePayload : public MessagePayload<Slot>,
+ public MessageWriter,
+ public MessageReader {
+ public:
+ ServicePayload(Message& message) : message_(message) {}
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ this->Extend(size);
+ return &*this->Cursor();
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return &message_; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*this->ConstCursor(), &*this->ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(this->ConstCursor(),
+ PointerDistance(new_start, &*this->ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return &message_; }
+
+ private:
+ Message& message_;
+};
+
+// Implements the payload interface for client-side RPCs. Handles gathering file
+// handles to be sent over IPC automatically.
+template <typename Slot>
+class ClientPayload : public MessagePayload<Slot>,
+ public MessageWriter,
+ public MessageReader {
+ public:
+ using ContainerType =
+ MessageBuffer<ThreadLocalTypeSlot<ClientPayload<Slot>>, 1024u, int>;
+ using BufferType = typename ContainerType::BufferType;
+
+ ClientPayload(Transaction& transaction) : transaction_{transaction} {}
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ this->Extend(size);
+ return &*this->Cursor();
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override {
+ return &transaction_;
+ }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*this->ConstCursor(), &*this->ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(this->ConstCursor(),
+ PointerDistance(new_start, &*this->ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override {
+ return &transaction_;
+ }
+
+ private:
+ Transaction& transaction_;
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_PAYLOAD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
new file mode 100644
index 0000000..d496719
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+#define ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for pointers to any serializable type. This class is used by
+// serialization/deserialization to handle pointers to objects that are to be
+// serialized or deserialized.
+template <typename T>
+class PointerWrapper {
+ public:
+ using BaseType = T;
+
+ PointerWrapper(T* pointer) : pointer_(pointer) {}
+ PointerWrapper(const PointerWrapper&) = default;
+ PointerWrapper(PointerWrapper&&) = default;
+ PointerWrapper& operator=(const PointerWrapper&) = default;
+ PointerWrapper& operator=(PointerWrapper&&) = default;
+
+ T& Dereference() { return *pointer_; }
+ const T& Dereference() const { return *pointer_; }
+
+ private:
+ T* pointer_;
+};
+
+template <typename T>
+PointerWrapper<T> WrapPointer(T* pointer) {
+ return PointerWrapper<T>(pointer);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_POINTER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method.h b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
new file mode 100644
index 0000000..49bee40
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
@@ -0,0 +1,550 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_H_
+
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/client.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+#ifdef __clang__
+// Stand-in type to avoid Clang compiler bug. Clang currently has a bug where
+// performing parameter pack expansion for arguments with empty packs causes a
+// compiler crash. Provide a substitute void type and specializations/overloads
+// of CheckArgumentTypes and DispatchRemoteMethod to work around this problem.
+struct Void {};
+
+// Evaluates to true if the method type is <any>(Void), false otherwise.
+template <typename RemoteMethodType>
+using IsVoidMethod = typename std::integral_constant<
+ bool,
+ RemoteMethodType::Traits::Arity == 1 &&
+ std::is_same<typename RemoteMethodType::Traits::template Arg<0>,
+ Void>::value>;
+
+// Utility to determine if a method is of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfVoidMethod =
+ typename std::enable_if<IsVoidMethod<RemoteMethodType>::value>::type;
+
+// Utility to determine if a method is not of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod =
+ typename std::enable_if<!IsVoidMethod<RemoteMethodType>::value>::type;
+
+#else
+// GCC works fine with void argument types, always enable the regular
+// implementation of DispatchRemoteMethod.
+using Void = void;
+template <typename RemoteMethodType>
+using EnableIfVoidMethod = void;
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod = void;
+#endif
+
+// Helper type trait to specialize InvokeRemoteMethods for return types that
+// can be obtained directly from Transaction::Send<T>() without deserializing
+// reply payload.
+template <typename T>
+struct IsDirectReturn : std::false_type {};
+
+template <>
+struct IsDirectReturn<void> : std::true_type {};
+
+template <>
+struct IsDirectReturn<int> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalHandle> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalChannelHandle> : std::true_type {};
+
+template <typename Return, typename Type = void>
+using EnableIfDirectReturn =
+ typename std::enable_if<IsDirectReturn<Return>::value, Type>::type;
+
+template <typename Return, typename Type = void>
+using EnableIfNotDirectReturn =
+ typename std::enable_if<!IsDirectReturn<Return>::value, Type>::type;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename T>
+class UnpackArguments;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename Return, typename... Args>
+class UnpackArguments<Class, Return(Args...)> {
+ public:
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+ using MethodType = Return (Class::*)(Message&, Args...);
+
+ UnpackArguments(Class& instance, MethodType method, Message& message,
+ ArgsTupleType& parameters)
+ : instance_(instance),
+ method_(method),
+ message_(message),
+ parameters_(parameters) {}
+
+ // Invokes method_ on intance_ with the packed arguments from parameters_.
+ Return Invoke() {
+ constexpr auto Arity = sizeof...(Args);
+ return static_cast<Return>(InvokeHelper(MakeIndexSequence<Arity>{}));
+ }
+
+ private:
+ Class& instance_;
+ MethodType method_;
+ Message& message_;
+ ArgsTupleType& parameters_;
+
+ template <std::size_t... Index>
+ Return InvokeHelper(IndexSequence<Index...>) {
+ return static_cast<Return>((instance_.*method_)(
+ message_,
+ std::get<Index>(std::forward<ArgsTupleType>(parameters_))...));
+ }
+
+ UnpackArguments(const UnpackArguments&) = delete;
+ void operator=(const UnpackArguments&) = delete;
+};
+
+// Returns an error code from a remote method to the client. May be called
+// either during dispatch of the remote method handler or at a later time if the
+// message is moved for delayed response.
+inline void RemoteMethodError(Message& message, int error_code) {
+ const int ret = message.ReplyError(error_code);
+ ALOGE_IF(ret < 0, "RemoteMethodError: Failed to reply to message: %s",
+ strerror(-ret));
+}
+
+// Returns a value from a remote method to the client. The usual method to
+// return a value during dispatch of a remote method is to simply use a return
+// statement in the handler. If the message is moved however, these methods may
+// be used to return a value at a later time, outside of initial dispatch.
+
+// Overload for direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+ Message& message, const Return& return_value) {
+ const int ret = message.Reply(return_value);
+ ALOGE_IF(ret < 0, "RemoteMethodReturn: Failed to reply to message: %s",
+ strerror(-ret));
+}
+
+// Overload for non-direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfNotDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+ Message& message, const Return& return_value) {
+ using Signature = typename RemoteMethodType::template RewriteReturn<Return>;
+ rpc::ServicePayload<ReplyBuffer> payload(message);
+ MakeArgumentEncoder<Signature>(&payload).EncodeReturn(return_value);
+
+ int ret;
+ auto size = message.Write(payload.Data(), payload.Size());
+ if (size < static_cast<decltype(size)>(payload.Size()))
+ ret = message.ReplyError(EIO);
+ else
+ ret = message.Reply(0);
+ ALOGE_IF(ret < 0, "RemoteMethodReturn: Failed to reply to message: %s",
+ strerror(-ret));
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for void return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+ typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ void (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for int return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+ typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ int (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for FileHandle return types.
+template <typename RemoteMethodType, FileHandleMode Mode, typename Class,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ FileHandle<Mode> (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature =
+ typename RemoteMethodType::template RewriteSignature<FileHandle<Mode>,
+ Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for ChannelHandle return types.
+template <typename RemoteMethodType, ChannelHandleMode Mode, typename Class,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(
+ Class& instance, ChannelHandle<Mode> (Class::*method)(Message&, Args...),
+ Message& message, std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface signature check. Overload for generic return types.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ Return (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature =
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ auto size = message.Read(payload.Data(), payload.Size());
+ if (size < 0) {
+ RemoteMethodError(message, -size);
+ return;
+ }
+
+ payload.Resize(size);
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+#ifdef __clang__
+// Overloads to handle Void argument type without exploding clang.
+
+// Overload for void return type.
+template <typename RemoteMethodType, typename Class,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&),
+ Message& message) {
+ (instance.*method)(message);
+ // Return to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Overload for int return type.
+template <typename RemoteMethodType, typename Class,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, int (Class::*method)(Message&),
+ Message& message) {
+ const int return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for FileHandle return type.
+template <typename RemoteMethodType, typename Class, FileHandleMode Mode,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ FileHandle<Mode> (Class::*method)(Message&),
+ Message& message) {
+ FileHandle<Mode> return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for ChannelHandle return types.
+template <typename RemoteMethodType, typename Class, ChannelHandleMode Mode,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ ChannelHandle<Mode> (Class::*method)(Message&),
+ Message& message) {
+ ChannelHandle<Mode> return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for generic return type.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&),
+ Message& message) {
+ auto return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+#endif
+
+} // namespace rpc
+
+// Definitions for template methods declared in pdx/client.h.
+
+template <int Opcode, typename T>
+struct CheckArgumentTypes;
+
+template <int Opcode, typename Return, typename... Args>
+struct CheckArgumentTypes<Opcode, Return(Args...)> {
+ template <typename R>
+ static typename rpc::EnableIfDirectReturn<R, Status<R>> Invoke(Client& client,
+ Args... args) {
+ Transaction trans{client};
+ rpc::ClientPayload<rpc::SendBuffer> payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&payload).EncodeArguments(
+ std::forward<Args>(args)...);
+ return trans.Send<R>(Opcode, payload.Data(), payload.Size(), nullptr, 0);
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfNotDirectReturn<R, Status<R>> Invoke(
+ Client& client, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+ reply_payload.Resize(reply_payload.Capacity());
+
+ Status<R> result;
+ auto status =
+ trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+ reply_payload.Data(), reply_payload.Size());
+ if (!status) {
+ result.SetError(status.error());
+ } else {
+ R return_value;
+ rpc::ErrorType error =
+ rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+ .DecodeReturn(&return_value);
+
+ switch (error.error_code()) {
+ case rpc::ErrorCode::NO_ERROR:
+ result.SetValue(std::move(return_value));
+ break;
+
+ // This error is returned when ArrayWrapper/StringWrapper is too
+ // small to receive the payload.
+ case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ result.SetError(ENOBUFS);
+ break;
+
+ default:
+ result.SetError(EIO);
+ break;
+ }
+ }
+ return result;
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfDirectReturn<R, Status<void>> InvokeInPlace(
+ Client& client, R* return_value, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ Status<void> result;
+ auto status = trans.Send<R>(Opcode, send_payload.Data(),
+ send_payload.Size(), nullptr, 0);
+ if (status) {
+ *return_value = status.take();
+ result.SetValue();
+ } else {
+ result.SetError(status.error());
+ }
+ return result;
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfNotDirectReturn<R, Status<void>> InvokeInPlace(
+ Client& client, R* return_value, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+ reply_payload.Resize(reply_payload.Capacity());
+
+ auto result =
+ trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+ reply_payload.Data(), reply_payload.Size());
+ if (result) {
+ rpc::ErrorType error =
+ rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+ .DecodeReturn(return_value);
+
+ switch (error.error_code()) {
+ case rpc::ErrorCode::NO_ERROR:
+ result.SetValue();
+ break;
+
+ // This error is returned when ArrayWrapper/StringWrapper is too
+ // small to receive the payload.
+ case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ result.SetError(ENOBUFS);
+ break;
+
+ default:
+ result.SetError(EIO);
+ break;
+ }
+ }
+ return result;
+ }
+};
+
+// Invokes the remote method with opcode and signature described by
+// |RemoteMethodType|.
+template <typename RemoteMethodType, typename... Args>
+Status<typename RemoteMethodType::Return> Client::InvokeRemoteMethod(
+ Args&&... args) {
+ return CheckArgumentTypes<
+ RemoteMethodType::Opcode,
+ typename RemoteMethodType::template RewriteArgs<Args...>>::
+ template Invoke<typename RemoteMethodType::Return>(
+ *this, std::forward<Args>(args)...);
+}
+
+template <typename RemoteMethodType, typename Return, typename... Args>
+Status<void> Client::InvokeRemoteMethodInPlace(Return* return_value,
+ Args&&... args) {
+ return CheckArgumentTypes<
+ RemoteMethodType::Opcode,
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>>::
+ template InvokeInPlace(*this, return_value, std::forward<Args>(args)...);
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_REMOTE_METHOD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
new file mode 100644
index 0000000..de9a3cc
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
@@ -0,0 +1,67 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/enumeration.h>
+#include <pdx/rpc/function_traits.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class binding a remote method opcode to its function signature.
+// Describes the interface between RPC clients and services for a single method.
+template <int Opcode_, typename Signature_>
+struct RemoteMethodType {
+ typedef FunctionTraits<Signature_> Traits;
+
+ enum : int { Opcode = Opcode_ };
+
+ typedef typename Traits::Signature Signature;
+ typedef typename Traits::Return Return;
+ typedef typename Traits::Args Args;
+
+ template <typename... Params>
+ using RewriteArgs = typename Traits::template RewriteArgs<Params...>;
+
+ template <typename ReturnType, typename... Params>
+ using RewriteSignature =
+ typename Traits::template RewriteSignature<ReturnType, Params...>;
+
+ template <typename ReturnType>
+ using RewriteReturn = typename Traits::template RewriteReturn<ReturnType>;
+};
+
+// Utility class representing a set of related RemoteMethodTypes. Describes the
+// interface between RPC clients and services as a set of methods.
+template <typename... MethodTypes>
+struct RemoteAPI {
+ typedef std::tuple<MethodTypes...> Methods;
+ enum : std::size_t { Length = sizeof...(MethodTypes) };
+
+ template <std::size_t Index>
+ using Method = typename std::tuple_element<Index, Methods>::type;
+
+ template <typename MethodType>
+ static constexpr std::size_t MethodIndex() {
+ return ElementForType<MethodType, MethodTypes...>::Index;
+ }
+};
+
+// Macro to simplify defining remote method signatures. Remote method signatures
+// are specified by defining a RemoteMethodType for each remote method.
+#define PDX_REMOTE_METHOD(name, opcode, ... /*signature*/) \
+ using name = ::android::pdx::rpc::RemoteMethodType<opcode, __VA_ARGS__>
+
+// Macro to simplify defining a set of remote method signatures.
+#define PDX_REMOTE_API(name, ... /*methods*/) \
+ using name = ::android::pdx::rpc::RemoteAPI<__VA_ARGS__>
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/sequence.h b/libs/vr/libpdx/private/pdx/rpc/sequence.h
new file mode 100644
index 0000000..5fd898a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/sequence.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_RPC_SEQUENCE_H_
+#define ANDROID_PDX_RPC_SEQUENCE_H_
+
+#include <cstdint>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides a C++11 implementation of C++14 index_sequence and
+// make_index_sequence for compatibility with common compilers. This
+// implementation may be conditionally replaced with compiler-provided versions
+// when C++14 support is available.
+
+// Utility to capture a sequence of unsigned indices.
+template <std::size_t... I>
+struct IndexSequence {
+ using type = IndexSequence;
+ using value_type = std::size_t;
+ static constexpr std::size_t size() { return sizeof...(I); }
+};
+
+namespace detail {
+
+// Helper class to merge and renumber sequence parts in log N instantiations.
+template <typename S1, typename S2>
+struct MergeSequencesAndRenumber;
+
+template <std::size_t... I1, std::size_t... I2>
+struct MergeSequencesAndRenumber<IndexSequence<I1...>, IndexSequence<I2...>>
+ : IndexSequence<I1..., (sizeof...(I1) + I2)...> {};
+
+} // namespace detail
+
+// Utility to build an IndexSequence with N indices.
+template <std::size_t N>
+struct MakeIndexSequence : detail::MergeSequencesAndRenumber<
+ typename MakeIndexSequence<N / 2>::type,
+ typename MakeIndexSequence<N - N / 2>::type> {};
+
+// Identity sequences.
+template <>
+struct MakeIndexSequence<0> : IndexSequence<> {};
+template <>
+struct MakeIndexSequence<1> : IndexSequence<0> {};
+
+// Utility to build an IndexSequence with indices for each element of a
+// parameter pack.
+template <typename... T>
+using IndexSequenceFor = MakeIndexSequence<sizeof...(T)>;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SEQUENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serializable.h b/libs/vr/libpdx/private/pdx/rpc/serializable.h
new file mode 100644
index 0000000..04a4352
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serializable.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZABLE_H_
+#define ANDROID_PDX_RPC_SERIALIZABLE_H_
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+
+#include "macros.h"
+#include "serialization.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This file provides utilities to define serializable types for communication
+// between clients and services. Supporting efficient, typed communication
+// protocols is the primary goal, NOT providing a general-purpose solution for
+// all your C++ serialization needs. Features that are not aligned to the goals
+// are not supported, such as static/const member serialization and serializable
+// types with virtual methods (requiring a virtual destructor).
+
+// Captures the type and value of a pointer to member. Pointer to members are
+// essentially compile-time constant offsets that can be stored in the type
+// system without adding to the size of the structures they describe. This
+// library uses this property to implement a limited form of reflection for
+// serialization/deserialization functions.
+template <typename T, T>
+struct MemberPointer;
+
+template <typename Type, typename Class, Type Class::*Pointer>
+struct MemberPointer<Type Class::*, Pointer> {
+ // Type of the member pointer this type represents.
+ using PointerType = Type Class::*;
+
+ // Resolves a pointer to member with the given instance, yielding a
+ // reference to the member in that instance.
+ static Type& Resolve(Class& instance) { return (instance.*Pointer); }
+ static const Type& Resolve(const Class& instance) {
+ return (instance.*Pointer);
+ }
+};
+
+// Describes a set of members to be serialized/deserialized by this library. The
+// parameter pack MemberPointers takes a list of MemberPointer types that
+// describe each member to participate in serialization/deserialization.
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType {
+ using Type = T;
+
+ // The number of member pointers described by this type.
+ enum : std::size_t { MemberCount = sizeof...(MemberPointers) };
+
+ // The member pointers described by this type.
+ using Members = std::tuple<MemberPointers...>;
+
+ // Accessor for individual member pointer types.
+ template <std::size_t Index>
+ using At = typename std::tuple_element<Index, Members>::type;
+};
+
+// Classes must do the following to correctly define a serializable type:
+// 1. Define a type called "SerializableMembers" as a template instantiation
+// of SerializableMembersType, describing the members of the class to
+// participate in serialization (presumably all of them). Use the macro
+// PDX_SERIALIZABLE_MEMBERS(...) below to aid the correct type
+// definition. This type should be private to prevent leaking member
+// access information.
+// 2. Make SerializableTraits and HasSerilizableMembers types a friend of
+// the class. The macro PDX_SERIALIZABLE_MEMEBRS(...) takes care of
+// this automatically.
+// 3. Define a public default constructor, if necessary. Deserialization
+// requires instances to be default-constructible.
+//
+// Example usage:
+// class MySerializableType : public AnotherBaseType {
+// public:
+// MySerializableType();
+// ...
+// private:
+// int a;
+// string b;
+// PDX_SERIALIZABLE_MEMBERS(MySerializableType, a, b);
+// };
+//
+// Note that const and static member serialization is not supported.
+
+template <typename T>
+class SerializableTraits {
+ public:
+ // Gets the serialized size of type T.
+ static std::size_t GetSerializedSize(const T& value) {
+ return GetEncodingSize(EncodeArrayType(SerializableMembers::MemberCount)) +
+ GetMembersSize<SerializableMembers>(value);
+ }
+
+ // Serializes type T.
+ static void SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArrayEncoding(EncodeArrayType(SerializableMembers::MemberCount),
+ SerializableMembers::MemberCount, buffer);
+ SerializeMembers<SerializableMembers>(value, writer, buffer);
+ }
+
+ // Deserializes type T.
+ static ErrorType DeserializeObject(T* value, MessageReader* reader,
+ const void*& start, const void* end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != SerializableMembers::MemberCount) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else {
+ return DeserializeMembers<SerializableMembers>(value, reader, start, end);
+ }
+ }
+
+ private:
+ using SerializableMembers = typename T::SerializableMembers;
+};
+
+// Utility macro to define a MemberPointer type for a member name.
+#define PDX_MEMBER_POINTER(type, member) \
+ ::android::pdx::rpc::MemberPointer<decltype(&type::member), &type::member>
+
+// Defines a list of MemberPointer types given a list of member names.
+#define PDX_MEMBERS(type, ... /*members*/) \
+ PDX_FOR_EACH_BINARY_LIST(PDX_MEMBER_POINTER, type, __VA_ARGS__)
+
+// Defines the serializable members of a type given a list of member names and
+// befriends SerializableTraits and HasSerializableMembers for the class. This
+// macro handles requirements #1 and #2 above.
+#define PDX_SERIALIZABLE_MEMBERS(type, ... /*members*/) \
+ template <typename T> \
+ friend class ::android::pdx::rpc::SerializableTraits; \
+ template <typename, typename> \
+ friend struct ::android::pdx::rpc::HasSerializableMembers; \
+ using SerializableMembers = ::android::pdx::rpc::SerializableMembersType< \
+ type, PDX_MEMBERS(type, __VA_ARGS__)>
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SERIALIZABLE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serialization.h b/libs/vr/libpdx/private/pdx/rpc/serialization.h
new file mode 100644
index 0000000..fccd028
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serialization.h
@@ -0,0 +1,1996 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZATION_H_
+#define ANDROID_PDX_RPC_SERIALIZATION_H_
+
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <map>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/trace.h>
+#include <pdx/utility.h>
+
+#include "array_wrapper.h"
+#include "default_initialization_allocator.h"
+#include "encoding.h"
+#include "pointer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Automatic serialization/deserialization library based on MessagePack
+// (http://msgpack.org). This library provides top level Serialize() and
+// Deserialize() functions to encode/decode a variety of data types.
+//
+// The following data types are supported:
+// * Standard signed integer types: int8_t, int16_t, int32_t, and int64_t.
+// * Regular signed integer types equivalent to the standard types:
+// signed char, short, int, long, and long long.
+// * Standard unsigned integer types: uint8_t, uint16_t, uint32_t, and
+// uint64_t.
+// * Regular unsigned integer types equivalent to the standard types:
+// unsigned char, unsigned short, unsigned int, unsigned long,
+// and unsigned long long.
+// * char without signed/unsigned qualifiers.
+// * bool.
+// * std::vector with value type of any supported type, including nesting.
+// * std::string.
+// * std::tuple with elements of any supported type, including nesting.
+// * std::pair with elements of any supported type, including nesting.
+// * std::map with keys and values of any supported type, including nesting.
+// * std::unordered_map with keys and values of any supported type, including
+// nesting.
+// * std::array with values of any supported type, including nesting.
+// * ArrayWrapper of any supported basic type.
+// * BufferWrapper of any POD type.
+// * StringWrapper of any supported char type.
+// * User types with correctly defined SerializableMembers member type.
+//
+// Planned support for:
+// * std::basic_string with all supported char types.
+
+// Counting template for managing template recursion.
+template <std::size_t N>
+struct Index {};
+
+// Forward declaration of traits type to access types with a SerializedMembers
+// member type.
+template <typename T>
+class SerializableTraits;
+
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType;
+
+// Utility to deduce the template type from a derived type.
+template <template <typename...> class TT, typename... Ts>
+std::true_type DeduceTemplateType(const TT<Ts...>*);
+template <template <typename...> class TT>
+std::false_type DeduceTemplateType(...);
+
+// Utility determining whether template type TT<...> is a base of type T.
+template <template <typename...> class TT, typename T>
+using IsTemplateBaseOf = decltype(DeduceTemplateType<TT>(std::declval<T*>()));
+
+// Utility type for SFINAE in HasHasSerializableMembers.
+template <typename... Ts>
+using TrySerializableMembersType = void;
+
+// Determines whether type T has a member type named SerializableMembers of
+// template type SerializableMembersType.
+template <typename, typename = void>
+struct HasSerializableMembers : std::false_type {};
+template <typename T>
+struct HasSerializableMembers<
+ T, TrySerializableMembersType<typename T::SerializableMembers>>
+ : std::integral_constant<
+ bool, IsTemplateBaseOf<SerializableMembersType,
+ typename T::SerializableMembers>::value> {};
+
+// Utility to simplify overload enable expressions for types with correctly
+// defined SerializableMembers.
+template <typename T>
+using EnableIfHasSerializableMembers =
+ typename std::enable_if<HasSerializableMembers<T>::value>::type;
+
+// Utility to simplify overload enable expressions for enum types.
+template <typename T, typename ReturnType = void>
+using EnableIfEnum =
+ typename std::enable_if<std::is_enum<T>::value, ReturnType>::type;
+
+///////////////////////////////////////////////////////////////////////////////
+// Error Reporting //
+///////////////////////////////////////////////////////////////////////////////
+
+// Error codes returned by the deserialization code.
+enum class ErrorCode {
+ NO_ERROR = 0,
+ UNEXPECTED_ENCODING,
+ UNEXPECTED_TYPE_SIZE,
+ INSUFFICIENT_BUFFER,
+ INSUFFICIENT_DESTINATION_SIZE,
+ GET_FILE_DESCRIPTOR_FAILED,
+ GET_CHANNEL_HANDLE_FAILED,
+ INVALID_VARIANT_ELEMENT,
+};
+
+// Type for errors returned by the deserialization code.
+class ErrorType {
+ public:
+ ErrorType() : error_code_(ErrorCode::NO_ERROR) {}
+
+ // ErrorType constructor for generic error codes. Explicitly not explicit,
+ // implicit conversion from ErrorCode to ErrorType is desirable behavior.
+ // NOLINTNEXTLINE(runtime/explicit)
+ ErrorType(ErrorCode error_code) : error_code_(error_code) {}
+
+ // ErrorType constructor for encoding type errors.
+ ErrorType(ErrorCode error_code, EncodingClass encoding_class,
+ EncodingType encoding_type)
+ : error_code_(error_code) {
+ unexpected_encoding_.encoding_class = encoding_class;
+ unexpected_encoding_.encoding_type = encoding_type;
+ }
+
+ // Evaluates to true if the ErrorType represents an error.
+ explicit operator bool() const { return error_code_ != ErrorCode::NO_ERROR; }
+
+ operator ErrorCode() const { return error_code_; }
+ ErrorCode error_code() const { return error_code_; }
+
+ // Accessors for extra info about unexpected encoding errors.
+ EncodingClass encoding_class() const {
+ return unexpected_encoding_.encoding_class;
+ }
+ EncodingType encoding_type() const {
+ return unexpected_encoding_.encoding_type;
+ }
+
+ operator std::string() const {
+ std::ostringstream stream;
+
+ switch (error_code_) {
+ case ErrorCode::NO_ERROR:
+ return "NO_ERROR";
+ case ErrorCode::UNEXPECTED_ENCODING:
+ stream << "UNEXPECTED_ENCODING: " << static_cast<int>(encoding_class())
+ << ", " << static_cast<int>(encoding_type());
+ return stream.str();
+ case ErrorCode::UNEXPECTED_TYPE_SIZE:
+ return "UNEXPECTED_TYPE_SIZE";
+ case ErrorCode::INSUFFICIENT_BUFFER:
+ return "INSUFFICIENT_BUFFER";
+ case ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ return "INSUFFICIENT_DESTINATION_SIZE";
+ default:
+ return "[Unknown Error]";
+ }
+ }
+
+ private:
+ ErrorCode error_code_;
+
+ // Union of extra information for different error code types.
+ union {
+ // UNEXPECTED_ENCODING.
+ struct {
+ EncodingClass encoding_class;
+ EncodingType encoding_type;
+ } unexpected_encoding_;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Size //
+///////////////////////////////////////////////////////////////////////////////
+
+inline constexpr std::size_t GetSerializedSize(const bool& b) {
+ return GetEncodingSize(EncodeType(b));
+}
+
+// Overloads of GetSerializedSize() for standard integer types.
+inline constexpr std::size_t GetSerializedSize(const char& c) {
+ return GetEncodingSize(EncodeType(c));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint8_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int8_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint16_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int16_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint32_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int32_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint64_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int64_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+
+inline constexpr std::size_t GetSerializedSize(const float& f) {
+ return GetEncodingSize(EncodeType(f));
+}
+inline constexpr std::size_t GetSerializedSize(const double& d) {
+ return GetEncodingSize(EncodeType(d));
+}
+
+// Overload for enum types.
+template <typename T>
+inline EnableIfEnum<T, std::size_t> GetSerializedSize(T v) {
+ return GetSerializedSize(static_cast<std::underlying_type_t<T>>(v));
+}
+
+// Forward declaration for nested definitions.
+inline std::size_t GetSerializedSize(const EmptyVariant&);
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>&);
+template <typename T, typename Enabled>
+inline constexpr std::size_t GetSerializedSize(const T&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>&);
+inline constexpr std::size_t GetSerializedSize(const std::string&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>&);
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>&);
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const ChannelHandle<Mode>&);
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::map<Key, T, Compare, Allocator>& m);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&);
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>&);
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v);
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p);
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple);
+
+// Overload for empty variant type.
+inline std::size_t GetSerializedSize(const EmptyVariant& empty) {
+ return GetEncodingSize(EncodeType(empty));
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>& variant) {
+ return GetEncodingSize(EncodeType(variant)) +
+ GetSerializedSize(variant.index()) +
+ variant.Visit(
+ [](const auto& value) { return GetSerializedSize(value); });
+}
+
+// Overload for structs/classes with SerializableMembers defined.
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline constexpr std::size_t GetSerializedSize(const T& value) {
+ return SerializableTraits<T>::GetSerializedSize(value);
+}
+
+// Overload for PointerWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>& p) {
+ return GetSerializedSize(p.Dereference());
+}
+
+// Overload for std::string.
+inline constexpr std::size_t GetSerializedSize(const std::string& s) {
+ return GetEncodingSize(EncodeType(s)) +
+ s.length() * sizeof(std::string::value_type);
+}
+
+// Overload for StringWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>& s) {
+ return GetEncodingSize(EncodeType(s)) +
+ s.length() * sizeof(typename StringWrapper<T>::value_type);
+}
+
+// Overload for BufferWrapper types.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>& b) {
+ return GetEncodingSize(EncodeType(b)) +
+ b.size() * sizeof(typename BufferWrapper<T>::value_type);
+}
+
+// Overload for FileHandle. FileHandle is encoded as a FIXEXT2, with a type code
+// of "FileHandle" and a signed 16-bit offset into the pushed fd array. Empty
+// FileHandles are encoded with an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>& fd) {
+ return GetEncodingSize(EncodeType(fd)) + sizeof(std::int16_t);
+}
+
+// Overload for ChannelHandle. ChannelHandle is encoded as a FIXEXT4, with a
+// type code of "ChannelHandle" and a signed 32-bit offset into the pushed
+// channel array. Empty ChannelHandles are encoded with an array index of -1.
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(
+ const ChannelHandle<Mode>& channel_handle) {
+ return GetEncodingSize(EncodeType(channel_handle)) + sizeof(std::int32_t);
+}
+
+// Overload for standard vector types.
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for standard map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::map<Key, T, Compare, Allocator>& v) {
+ return std::accumulate(
+ v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const std::pair<Key, T>& object) {
+ return sum + GetSerializedSize(object.first) +
+ GetSerializedSize(object.second);
+ });
+}
+
+// Overload for standard unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
+ return std::accumulate(
+ v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const std::pair<Key, T>& object) {
+ return sum + GetSerializedSize(object.first) +
+ GetSerializedSize(object.second);
+ });
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for std::pair.
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p) {
+ return GetEncodingSize(EncodeType(p)) + GetSerializedSize(p.first) +
+ GetSerializedSize(p.second);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline std::size_t GetTupleSize(const std::tuple<T...>&, Index<0>) {
+ return 0;
+}
+
+// Gets the size of each element in a tuple recursively.
+template <typename... T, std::size_t index>
+inline std::size_t GetTupleSize(const std::tuple<T...>& tuple, Index<index>) {
+ return GetTupleSize(tuple, Index<index - 1>()) +
+ GetSerializedSize(std::get<index - 1>(tuple));
+}
+
+// Overload for tuple types. Gets the size of the tuple, recursing
+// through the elements.
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple) {
+ return GetEncodingSize(EncodeType(tuple)) +
+ GetTupleSize(tuple, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member of a Serializable
+// type is reached.
+template <typename Members, typename T>
+inline std::size_t GetMemberSize(const T&, Index<0>) {
+ return 0;
+}
+
+// Gets the size of each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline std::size_t GetMemberSize(const T& object, Index<index>) {
+ return GetMemberSize<Members>(object, Index<index - 1>()) +
+ GetSerializedSize(Members::template At<index - 1>::Resolve(object));
+}
+
+// Gets the size of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline std::size_t GetMembersSize(const T& object) {
+ return GetMemberSize<Members>(object, Index<Members::MemberCount>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Serialization //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// SerializeRaw() converts a primitive array or type into a raw byte string.
+// These functions are named differently from SerializeObject() expressly to
+// avoid catch-all specialization of that template, which can be difficult to
+// detect otherwise.
+//
+
+inline void WriteRawData(void*& dest, const void* src, size_t size) {
+ memcpy(dest, src, size);
+ dest = static_cast<uint8_t*>(dest) + size;
+}
+
+// Serializes a primitive array into a raw byte string.
+template <typename T,
+ typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline void SerializeRaw(const T& value, void*& buffer) {
+ WriteRawData(buffer, &value, sizeof(value));
+}
+
+inline void SerializeEncoding(EncodingType encoding, void*& buffer) {
+ SerializeRaw(encoding, buffer);
+}
+
+inline void SerializeType(const bool& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code, extended type code, and size for
+// extension types.
+inline void SerializeExtEncoding(EncodingType encoding,
+ EncodingExtType ext_type, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_EXT8) {
+ std::uint8_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_EXT16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_EXT32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixextEncoding(encoding) */ {
+ // Encoding byte contains the fixext length, nothing else to do.
+ }
+ SerializeRaw(ext_type, buffer);
+}
+
+// Serializes the type code for file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeType(const FileHandle<Mode>& value, void*& buffer) {
+ SerializeExtEncoding(EncodeType(value), ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 2,
+ buffer);
+}
+
+// Serializes the type code for channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeType(const ChannelHandle<Mode>& handle, void*& buffer) {
+ SerializeExtEncoding(EncodeType(handle), ENCODING_EXT_TYPE_CHANNEL_HANDLE, 4,
+ buffer);
+}
+
+// Serializes type code for variant types.
+template <typename... Types>
+inline void SerializeType(const Variant<Types...>& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code for string types.
+template <typename StringType>
+inline void SerializeStringType(const StringType& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_STR8) {
+ std::uint8_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_STR16) {
+ std::uint16_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_STR32) {
+ std::uint32_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixstrEncoding(encoding) */ {
+ // Encoding byte contains the fixstr length, nothing else to do.
+ }
+}
+
+// Serializes the type code for std::string and StringWrapper. These types are
+// interchangeable and must serialize to the same format.
+inline void SerializeType(const std::string& value, void*& buffer) {
+ SerializeStringType(value, buffer);
+}
+template <typename T>
+inline void SerializeType(const StringWrapper<T>& value, void*& buffer) {
+ SerializeStringType(value, buffer);
+}
+
+// Serializes the type code for bin types.
+inline void SerializeBinEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_BIN8) {
+ std::uint8_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_BIN16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_BIN32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else {
+ // Invalid encoding for BIN type.
+ }
+}
+
+// Serializes the type code for BufferWrapper types.
+template <typename T>
+inline void SerializeType(const BufferWrapper<T>& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeBinEncoding(
+ encoding, value.size() * sizeof(typename BufferWrapper<T>::value_type),
+ buffer);
+}
+
+// Serializes the array encoding type and length.
+inline void SerializeArrayEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_ARRAY16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_ARRAY32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixarrayEncoding(encoding) */ {
+ // Encoding byte contains the fixarray length, nothing else to do.
+ }
+}
+
+// Serializes the map encoding type and length.
+inline void SerializeMapEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_MAP16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_MAP32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixmapEncoding(encoding) */ {
+ // Encoding byte contains the fixmap length, nothing else to do.
+ }
+}
+
+// Serializes the type code for array types.
+template <typename ArrayType>
+inline void SerializeArrayType(const ArrayType& value, std::size_t size,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeArrayEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for map types.
+template <typename MapType>
+inline void SerializeMapType(const MapType& value, std::size_t size,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeMapEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for std::vector and ArrayWrapper. These types are
+// interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeType(const std::vector<T, Allocator>& value,
+ void*& buffer) {
+ SerializeArrayType(value, value.size(), buffer);
+}
+template <typename T>
+inline void SerializeType(const ArrayWrapper<T>& value, void*& buffer) {
+ SerializeArrayType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::array. This type serializes to the same
+// format as std::vector and ArrayWrapper and is interchangeable in certain
+// situations.
+template <typename T, std::size_t Size>
+inline void SerializeType(const std::array<T, Size>& value, void*& buffer) {
+ SerializeArrayType(value, Size, buffer);
+}
+
+// Serializes the type code for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeType(const std::map<Key, T, Compare, Allocator>& value,
+ void*& buffer) {
+ SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeType(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value,
+ void*& buffer) {
+ SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::pair types.
+template <typename T, typename U>
+inline void SerializeType(const std::pair<T, U>& value, void*& buffer) {
+ SerializeArrayType(value, 2, buffer);
+}
+
+// Serializes the type code for std::tuple types.
+template <typename... T>
+inline void SerializeType(const std::tuple<T...>& value, void*& buffer) {
+ SerializeArrayType(value, sizeof...(T), buffer);
+}
+
+// Specialization of SerializeObject for boolean type.
+inline void SerializeObject(const bool& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ SerializeType(value, buffer);
+ // Encoding contains the boolean value, nothing else to do.
+}
+
+// Overloads of SerializeObject for float and double types.
+inline void SerializeObject(const float& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ SerializeRaw(value, buffer);
+}
+
+inline void SerializeObject(const double& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ SerializeRaw(value, buffer);
+}
+
+// Overloads of SerializeObject() for standard integer types.
+inline void SerializeObject(const char& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int8_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint8_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int16_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint16_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int32_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ const int16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_INT32) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint32_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ const uint16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT32) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int64_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ const int16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_INT32) {
+ const int32_t word = value;
+ SerializeRaw(word, buffer);
+ } else if (encoding == ENCODING_TYPE_INT64) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint64_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ const uint16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT32) {
+ const uint32_t word = value;
+ SerializeRaw(word, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT64) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+// Serialize enum types.
+template <typename T>
+inline EnableIfEnum<T> SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializeObject(static_cast<std::underlying_type_t<T>>(value), writer,
+ buffer);
+}
+
+// Forward declaration for nested definitions.
+inline void SerializeObject(const EmptyVariant&, MessageWriter*, void*&);
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>&, MessageWriter*, void*&);
+template <typename T, typename Enabled>
+inline void SerializeObject(const T&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>&, MessageWriter*, void*&);
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>&, MessageWriter*, void*&);
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>&, MessageWriter*, void*&);
+inline void SerializeObject(const std::string&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>&, MessageWriter*, void*&);
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeObject(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&, MessageWriter*, void*&);
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>&, MessageWriter*, void*&);
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>&, MessageWriter*, void*&);
+
+// Overload for empty variant type.
+inline void SerializeObject(const EmptyVariant& empty,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const EncodingType encoding = EncodeType(empty);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>& variant,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(variant, buffer);
+ SerializeObject(variant.index(), writer, buffer);
+ return variant.Visit([writer, &buffer](const auto& value) {
+ return SerializeObject(value, writer, buffer);
+ });
+}
+
+// Overload for serializable structure/class types.
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline void SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializableTraits<T>::SerializeObject(value, writer, buffer);
+}
+
+// Serializes the payload of a PointerWrapper.
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>& pointer,
+ MessageWriter* writer, void*& buffer) {
+ SerializeObject(pointer.Dereference(), writer, buffer);
+}
+
+// Serializes the payload of file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>& fd, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(fd, buffer);
+ const FileReference value =
+ writer->GetOutputResourceMapper()->PushFileHandle(fd);
+ SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>& handle,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(handle, buffer);
+ const ChannelReference value =
+ writer->GetOutputResourceMapper()->PushChannelHandle(handle);
+ SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of BufferWrapper types.
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>& b,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const auto value_type_size =
+ sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+ SerializeType(b, buffer);
+ WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>& b,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+ SerializeType(b, buffer);
+ WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+
+// Serializes the payload of string types.
+template <typename StringType>
+inline void SerializeString(const StringType& s, void*& buffer) {
+ const auto value_type_size = sizeof(typename StringType::value_type);
+ SerializeType(s, buffer);
+ WriteRawData(buffer, s.data(), s.length() * value_type_size);
+}
+
+// Overload of SerializeObject() for std::string and StringWrapper. These types
+// are interchangeable and must serialize to the same format.
+inline void SerializeObject(const std::string& s, MessageWriter* /*writer*/,
+ void*& buffer) {
+ SerializeString(s, buffer);
+}
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>& s,
+ MessageWriter* /*writer*/, void*& buffer) {
+ SerializeString(s, buffer);
+}
+
+// Serializes the payload of array types.
+template <typename ArrayType>
+inline void SerializeArray(const ArrayType& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(v, buffer);
+ for (const auto& element : v)
+ SerializeObject(element, writer, buffer);
+}
+
+// Serializes the payload for map types.
+template <typename MapType>
+inline void SerializeMap(const MapType& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(v, buffer);
+ for (const auto& element : v) {
+ SerializeObject(element.first, writer, buffer);
+ SerializeObject(element.second, writer, buffer);
+ }
+}
+
+// Overload of SerializeObject() for std::vector and ArrayWrapper types. These
+// types are interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::array types. These types serialize to
+// the same format at std::vector and ArrayWrapper and are interchangeable in
+// certain situations.
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeObject(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std:pair types.
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>& pair, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(pair, buffer);
+ SerializeObject(pair.first, writer, buffer);
+ SerializeObject(pair.second, writer, buffer);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline void SerializeTuple(const std::tuple<T...>&, MessageWriter*, void*&,
+ Index<0>) {}
+
+// Serializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline void SerializeTuple(const std::tuple<T...>& tuple, MessageWriter* writer,
+ void*& buffer, Index<index>) {
+ SerializeTuple(tuple, writer, buffer, Index<index - 1>());
+ SerializeObject(std::get<index - 1>(tuple), writer, buffer);
+}
+
+// Overload of SerializeObject() for tuple types.
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>& tuple,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(tuple, buffer);
+ SerializeTuple(tuple, writer, buffer, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member pointer is reached.
+template <typename Members, typename T>
+inline void SerializeMember(const T&, MessageWriter*, void*&, Index<0>) {}
+
+// Serializes each member pointer recursively.
+template <typename Members, typename T, std::size_t index>
+inline void SerializeMember(const T& object, MessageWriter* writer,
+ void*& buffer, Index<index>) {
+ SerializeMember<Members>(object, writer, buffer, Index<index - 1>());
+ SerializeObject(Members::template At<index - 1>::Resolve(object), writer,
+ buffer);
+}
+
+// Serializes the members of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline void SerializeMembers(const T& object, MessageWriter* writer,
+ void*& buffer) {
+ SerializeMember<Members>(object, writer, buffer,
+ Index<Members::MemberCount>());
+}
+
+// Top level serialization function that replaces the buffer's contents.
+template <typename T>
+inline void Serialize(const T& object, MessageWriter* writer) {
+ PDX_TRACE_NAME("Serialize");
+ const std::size_t size = GetSerializedSize(object);
+
+ // Reserve the space needed for the object(s).
+ void* buffer = writer->GetNextWriteBufferSection(size);
+ SerializeObject(object, writer, buffer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Deserialization //
+///////////////////////////////////////////////////////////////////////////////
+
+inline ErrorType ReadRawDataFromNextSection(void* dest, MessageReader* reader,
+ const void*& start,
+ const void*& end, size_t size) {
+ while (AdvancePointer(start, size) > end) {
+ auto remaining_size = PointerDistance(end, start);
+ if (remaining_size > 0) {
+ memcpy(dest, start, remaining_size);
+ dest = AdvancePointer(dest, remaining_size);
+ size -= remaining_size;
+ }
+ reader->ConsumeReadBufferSectionData(AdvancePointer(start, remaining_size));
+ std::tie(start, end) = reader->GetNextReadBufferSection();
+ if (start == end)
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ }
+ memcpy(dest, start, size);
+ start = AdvancePointer(start, size);
+ return ErrorCode::NO_ERROR;
+}
+
+inline ErrorType ReadRawData(void* dest, MessageReader* /*reader*/,
+ const void*& start, const void*& end,
+ size_t size) {
+ if (PDX_UNLIKELY(AdvancePointer(start, size) > end)) {
+ // TODO(avakulenko): Enabling reading from next sections of input buffer
+ // (using ReadRawDataFromNextSection) screws up clang compiler optimizations
+ // (probably inefficient inlining) making the whole deserialization
+ // code path about twice as slow. Investigate and enable more generic
+ // deserialization code, but right now we don't really need/support this
+ // scenario, so I keep this commented out for the time being...
+
+ // return ReadRawDataFromNextSection(dest, reader, start, end, size);
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ }
+ memcpy(dest, start, size);
+ start = AdvancePointer(start, size);
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes a primitive object from raw bytes.
+template <typename T,
+ typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline ErrorType DeserializeRaw(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return ReadRawData(value, reader, start, end, sizeof(T));
+}
+
+// Utility to deserialize POD types when the serialized type is different
+// (smaller) than the target real type. This happens when values are serialized
+// into more compact encodings.
+template <typename SerializedType, typename RealType>
+ErrorType DeserializeValue(RealType* real_value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ SerializedType serialized_value;
+ if (const auto error =
+ DeserializeRaw(&serialized_value, reader, start, end)) {
+ return error;
+ } else {
+ *real_value = serialized_value;
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+inline ErrorType DeserializeEncoding(EncodingType* encoding,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeRaw(encoding, reader, start, end);
+}
+
+// Overload to deserialize bool type.
+inline ErrorType DeserializeObject(bool* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsBoolEncoding(encoding)) {
+ *value = (encoding == ENCODING_TYPE_TRUE);
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BOOL,
+ encoding);
+ }
+}
+
+// Specializations to deserialize float and double types.
+inline ErrorType DeserializeObject(float* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFloat32Encoding(encoding)) {
+ return DeserializeValue<float>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(double* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFloat32Encoding(encoding)) {
+ return DeserializeValue<float>(value, reader, start, end);
+ } else if (IsFloat64Encoding(encoding)) {
+ return DeserializeValue<double>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+ encoding);
+ }
+}
+
+// Specializations to deserialize standard integer types.
+inline ErrorType DeserializeObject(char* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = static_cast<char>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<char>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int8_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint8_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int16_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint16_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int32_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else if (IsInt32Encoding(encoding)) {
+ return DeserializeValue<std::int32_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint32_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else if (IsUInt32Encoding(encoding)) {
+ return DeserializeValue<std::uint32_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int64_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else if (IsInt32Encoding(encoding)) {
+ return DeserializeValue<std::int32_t>(value, reader, start, end);
+ } else if (IsInt64Encoding(encoding)) {
+ return DeserializeValue<std::int64_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint64_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else if (IsUInt32Encoding(encoding)) {
+ return DeserializeValue<std::uint32_t>(value, reader, start, end);
+ } else if (IsUInt64Encoding(encoding)) {
+ return DeserializeValue<std::uint64_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+template <typename T>
+inline EnableIfEnum<T, ErrorType> DeserializeObject(T* value,
+ MessageReader* reader,
+ const void*& start,
+ const void*& end) {
+ std::underlying_type_t<T> enum_value;
+ ErrorType error = DeserializeObject(&enum_value, reader, start, end);
+ if (!error)
+ *value = static_cast<T>(enum_value);
+ return error;
+}
+
+// Forward declarations for nested definitions.
+template <typename T, typename Enabled>
+inline ErrorType DeserializeObject(T*, MessageReader*, const void*&,
+ const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(LocalHandle*, MessageReader*, const void*&,
+ const void*&);
+inline ErrorType DeserializeObject(LocalChannelHandle*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(BufferWrapper<std::vector<T, Allocator>>*,
+ MessageReader*, const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(std::string*, MessageReader*, const void*&,
+ const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+ const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>*, MessageReader*,
+ const void*&, const void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>*,
+ MessageReader*, const void*&, const void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline ErrorType DeserializeObject(
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+ const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(EmptyVariant*,
+ MessageReader*, const void*&,
+ const void*&);
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>*,
+ MessageReader*, const void*&,
+ const void*&);
+
+// Deserializes a Serializable type.
+template <typename T, typename Enable = EnableIfHasSerializableMembers<T>>
+inline ErrorType DeserializeObject(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return SerializableTraits<T>::DeserializeObject(value, reader, start, end);
+}
+
+// Deserializes a PointerWrapper.
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>* pointer,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeObject(&pointer->Dereference(), reader, start, end);
+}
+
+// Deserializes the type code and size for extension types.
+inline ErrorType DeserializeExtType(EncodingType* encoding,
+ EncodingExtType* type, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixextEncoding(*encoding)) {
+ *size = GetFixextSize(*encoding);
+ } else if (*encoding == ENCODING_TYPE_EXT8) {
+ if (const auto error =
+ DeserializeValue<std::uint8_t>(size, reader, start, end))
+ return error;
+ } else if (*encoding == ENCODING_TYPE_EXT16) {
+ if (const auto error =
+ DeserializeValue<std::uint16_t>(size, reader, start, end))
+ return error;
+ } else if (*encoding == ENCODING_TYPE_EXT32) {
+ if (const auto error =
+ DeserializeValue<std::uint32_t>(size, reader, start, end))
+ return error;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ *encoding);
+ }
+
+ // The extension type code follows the encoding and size.
+ return DeserializeRaw(type, reader, start, end);
+}
+
+// Deserializes a file handle and performs handle space translation, if
+// required.
+inline ErrorType DeserializeObject(LocalHandle* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ EncodingExtType type;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+ return error;
+ } else if (size != 2) {
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+ encoding);
+ } else if (type == ENCODING_EXT_TYPE_FILE_DESCRIPTOR) {
+ // Read the encoded file descriptor value.
+ FileReference ref;
+ if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+ return error;
+ }
+
+ return reader->GetInputResourceMapper()->GetFileHandle(ref, value)
+ ? ErrorCode::NO_ERROR
+ : ErrorCode::GET_FILE_DESCRIPTOR_FAILED;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(LocalChannelHandle* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ EncodingExtType type;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+ return error;
+ } else if (size != 4) {
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+ encoding);
+ } else if (type == ENCODING_EXT_TYPE_CHANNEL_HANDLE) {
+ // Read the encoded channel handle value.
+ ChannelReference ref;
+ if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+ return error;
+ }
+ return reader->GetInputResourceMapper()->GetChannelHandle(ref, value)
+ ? ErrorCode::NO_ERROR
+ : ErrorCode::GET_CHANNEL_HANDLE_FAILED;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ encoding);
+ }
+}
+
+// Deserializes the type code and size for bin types.
+inline ErrorType DeserializeBinType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (*encoding == ENCODING_TYPE_BIN8) {
+ return DeserializeValue<std::uint8_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_BIN16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_BIN32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BINARY,
+ *encoding);
+ }
+}
+
+// Overload of DeserializeObject() for BufferWrapper types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(
+ BufferWrapper<std::vector<T, Allocator>>* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ const auto value_type_size =
+ sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeBinType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the BufferWrapper to the size of the payload.
+ value->resize(size / value_type_size);
+
+ if (size > value->size() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeBinType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the BufferWrapper to the size of the payload.
+ value->resize(size / value_type_size);
+
+ if (size > value->size() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+
+// Deserializes the type code and size for string types.
+inline ErrorType DeserializeStringType(EncodingType* encoding,
+ std::size_t* size, MessageReader* reader,
+ const void*& start, const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixstrEncoding(*encoding)) {
+ *size = GetFixstrSize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_STR8) {
+ return DeserializeValue<std::uint8_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_STR16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_STR32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_STRING,
+ *encoding);
+ }
+}
+
+// Overload of DeserializeObject() for std::string types.
+inline ErrorType DeserializeObject(std::string* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeStringType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size == 0U) {
+ value->clear();
+ return ErrorCode::NO_ERROR;
+ } else {
+ value->resize(size);
+ return ReadRawData(&(*value)[0], reader, start, end, size);
+ }
+}
+
+// Overload of DeserializeObject() for StringWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ const auto value_type_size = sizeof(typename StringWrapper<T>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeStringType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the StringWrapper to the size of the payload
+ // string.
+ value->resize(size / value_type_size);
+
+ if (size > value->length() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+
+// Deserializes the type code and size of array types.
+inline ErrorType DeserializeArrayType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixarrayEncoding(*encoding)) {
+ *size = GetFixarraySize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_ARRAY16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_ARRAY32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_ARRAY,
+ *encoding);
+ }
+}
+
+// Deserializes the type code and size of map types.
+inline ErrorType DeserializeMapType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixmapEncoding(*encoding)) {
+ *size = GetFixmapSize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_MAP16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_MAP32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+ *encoding);
+ }
+}
+
+// Overload for std::vector types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end))
+ return error;
+
+ std::vector<T, Allocator> result(size);
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&result[i], reader, start, end))
+ return error;
+ }
+
+ *value = std::move(result);
+ return ErrorCode::NO_ERROR;
+
+// TODO(eieio): Consider the benefits and trade offs of this alternative.
+#if 0
+ value->resize(size);
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+ return ErrorCode::NO_ERROR;
+#endif
+}
+
+// Deserializes an EmptyVariant value.
+inline ErrorType DeserializeObject(EmptyVariant* /*empty*/,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (encoding != ENCODING_TYPE_NIL) {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+ encoding);
+ } else {
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+// Deserializes a Variant type.
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>* variant,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeMapType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ if (size != 1)
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_MAP,
+ encoding);
+
+ std::int32_t type;
+ if (const auto error = DeserializeObject(&type, reader, start, end)) {
+ return error;
+ } else if (type < Variant<Types...>::kEmptyIndex ||
+ type >= static_cast<std::int32_t>(sizeof...(Types))) {
+ return ErrorCode::INVALID_VARIANT_ELEMENT;
+ } else {
+ variant->Become(type);
+ return variant->Visit([reader, &start, &end](auto&& value) {
+ return DeserializeObject(&value, reader, start, end);
+ });
+ }
+}
+
+// Deserializes map types.
+template <typename MapType>
+inline ErrorType DeserializeMap(MapType* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeMapType(&encoding, &size, reader, start, end))
+ return error;
+
+ MapType result;
+ for (std::size_t i = 0; i < size; i++) {
+ std::pair<typename MapType::key_type, typename MapType::mapped_type>
+ element;
+ if (const auto error =
+ DeserializeObject(&element.first, reader, start, end))
+ return error;
+ if (const auto error =
+ DeserializeObject(&element.second, reader, start, end))
+ return error;
+ result.emplace(std::move(element));
+ }
+
+ *value = std::move(result);
+ return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline ErrorType DeserializeObject(
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>* value,
+ MessageReader* reader, const void*& start, const void*& end) {
+ return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ // Try to resize the wrapper.
+ value->resize(size);
+
+ // Make sure there is enough space in the ArrayWrapper for the
+ // payload.
+ if (size > value->capacity())
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+
+ return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ if (size != Size)
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes std::pair types.
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != 2) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else if (const auto error =
+ DeserializeObject(&value->first, reader, start, end)) {
+ return error;
+ } else if (const auto error =
+ DeserializeObject(&value->second, reader, start, end)) {
+ return error;
+ } else {
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline ErrorType DeserializeTuple(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*, Index<0>) {
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline ErrorType DeserializeTuple(std::tuple<T...>* tuple,
+ MessageReader* reader, const void*& start,
+ const void*& end, Index<index>) {
+ if (const auto error =
+ DeserializeTuple(tuple, reader, start, end, Index<index - 1>()))
+ return error;
+ else
+ return DeserializeObject(&std::get<index - 1>(*tuple), reader, start, end);
+}
+
+// Overload for standard tuple types.
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != sizeof...(T)) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else {
+ return DeserializeTuple(value, reader, start, end, Index<sizeof...(T)>());
+ }
+}
+
+// Stops template recursion when the last member of a Serializable type is
+// reached.
+template <typename Members, typename T>
+inline ErrorType DeserializeMember(T*, MessageReader*, const void*&,
+ const void*, Index<0>) {
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline ErrorType DeserializeMember(T* value, MessageReader* reader,
+ const void*& start, const void*& end,
+ Index<index>) {
+ if (const auto error = DeserializeMember<Members>(value, reader, start, end,
+ Index<index - 1>()))
+ return error;
+ else
+ return DeserializeObject(&Members::template At<index - 1>::Resolve(*value),
+ reader, start, end);
+}
+
+// Deserializes the members of a Serializable type using the given
+// SerializableMembersType type.
+template <typename Members, typename T>
+inline ErrorType DeserializeMembers(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return DeserializeMember<Members>(value, reader, start, end,
+ Index<Members::MemberCount>());
+}
+
+// Top level deserialization function.
+template <typename T>
+inline ErrorType Deserialize(T* value, MessageReader* reader) {
+ PDX_TRACE_NAME("Deserialize");
+ MessageReader::BufferSection section = reader->GetNextReadBufferSection();
+ if (section.first == section.second)
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ ErrorType error =
+ DeserializeObject(value, reader, section.first, section.second);
+ reader->ConsumeReadBufferSectionData(section.first);
+ return error;
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SERIALIZATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
new file mode 100644
index 0000000..19fc4c1
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
@@ -0,0 +1,129 @@
+#ifndef ANDROID_PDX_RPC_STRING_WRAPPER_H_
+#define ANDROID_PDX_RPC_STRING_WRAPPER_H_
+
+#include <cstddef>
+#include <cstring>
+#include <string>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C string buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::basic_string, and may be substituted for std::basic_string
+// during serialization and deserialization. This substitution makes handling of
+// C strings more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::basic_string arguments or return values.
+template <typename CharT = std::string::value_type,
+ typename Traits = std::char_traits<CharT>>
+class StringWrapper {
+ public:
+ // Define types in the style of STL strings to support STL operators.
+ typedef Traits traits_type;
+ typedef typename Traits::char_type value_type;
+ typedef std::size_t size_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+
+ StringWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ StringWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ StringWrapper(pointer buffer, size_type size)
+ : StringWrapper(buffer, size, size) {}
+
+ explicit StringWrapper(pointer buffer)
+ : StringWrapper(buffer, std::strlen(buffer)) {}
+
+ StringWrapper(const StringWrapper& other) { *this = other; }
+
+ StringWrapper(StringWrapper&& other) { *this = std::move(other); }
+
+ StringWrapper& operator=(const StringWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ StringWrapper& operator=(StringWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type length() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+// Utility functions that infer the underlying type of the string, simplifying
+// the wrapper interface.
+
+// TODO(eieio): Wrapping std::basic_string is here for completeness, but is it
+// useful?
+template <typename T, typename... Any>
+StringWrapper<const T> WrapString(const std::basic_string<T, Any...>& s) {
+ return StringWrapper<const T>(s.c_str(), s.length());
+}
+
+template <typename T, typename SizeType = std::size_t>
+StringWrapper<T> WrapString(T* s, SizeType size) {
+ return StringWrapper<T>(s, size);
+}
+
+template <typename T>
+StringWrapper<T> WrapString(T* s) {
+ return StringWrapper<T>(s);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_STRING_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
new file mode 100644
index 0000000..e5ef2aa
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
@@ -0,0 +1,134 @@
+#ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+#define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/trace.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to distinguish between different thread local entries or
+// "slots" in the thread local variable table. Each slot is uniquely identified
+// by (T,Index) and is independent of any other slot.
+template <typename T, std::size_t Index>
+struct ThreadLocalSlot;
+
+// Utility class to specify thread local slots using only a type.
+template <typename T>
+struct ThreadLocalTypeSlot;
+
+// Utility class to specify thread local slots using only an index.
+template <std::size_t Index>
+struct ThreadLocalIndexSlot;
+
+// Initial capacity of thread local buffer, unless otherwise specified.
+constexpr std::size_t InitialBufferCapacity = 4096;
+
+// Thread local slots for buffers used by this library to send, receive, and
+// reply to messages.
+using SendBuffer = ThreadLocalIndexSlot<0>;
+using ReceiveBuffer = ThreadLocalIndexSlot<1>;
+using ReplyBuffer = ThreadLocalIndexSlot<2>;
+
+// Provides a simple interface to thread local buffers for large IPC messages.
+// Slot provides multiple thread local slots for a given T, Allocator, Capacity
+// combination.
+template <typename T, typename Allocator = DefaultInitializationAllocator<T>,
+ std::size_t Capacity = InitialBufferCapacity,
+ typename Slot = ThreadLocalSlot<void, 0>>
+class ThreadLocalBuffer {
+ public:
+ using BufferType = std::vector<T, Allocator>;
+ using ValueType = T;
+
+ // Reserves |capacity| number of elements of capacity in the underlying
+ // buffer. Call this during startup to avoid allocation during use.
+ static void Reserve(std::size_t capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::Reserve");
+ InitializeBuffer(capacity);
+ buffer_->reserve(capacity);
+ }
+
+ // Resizes the buffer to |size| elements.
+ static void Resize(std::size_t size) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::Resize");
+ InitializeBuffer(size);
+ buffer_->resize(size);
+ }
+
+ // Gets a reference to the underlying buffer after reserving |capacity|
+ // elements. The current size of the buffer is left intact. The returned
+ // reference is valid until FreeBuffer() is called.
+ static BufferType& GetBuffer(std::size_t capacity = Capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer");
+ Reserve(capacity);
+ return *buffer_;
+ }
+
+ // Gets a reference to the underlying buffer after reserving |Capacity|
+ // elements. The current size of the buffer is set to zero. The returned
+ // reference is valid until FreeBuffer() is called.
+ static BufferType& GetEmptyBuffer() {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer");
+ Reserve(Capacity);
+ buffer_->clear();
+ return *buffer_;
+ }
+
+ // Gets a reference to the underlying buffer after resizing it to |size|
+ // elements. The returned reference is valid until FreeBuffer() is called.
+ static BufferType& GetSizedBuffer(std::size_t size = Capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer");
+ Resize(size);
+ return *buffer_;
+ }
+
+ // Frees the underlying buffer. The buffer will be reallocated if any of the
+ // methods above are called.
+ static void FreeBuffer() {
+ if (buffer_) {
+ GetBufferGuard().reset(buffer_ = nullptr);
+ }
+ }
+
+ private:
+ friend class ThreadLocalBufferTest;
+
+ static void InitializeBuffer(std::size_t capacity) {
+ if (!buffer_) {
+ GetBufferGuard().reset(buffer_ = new BufferType(capacity));
+ }
+ }
+
+ // Work around performance issues with thread-local dynamic initialization
+ // semantics by using a normal pointer in parallel with a std::unique_ptr. The
+ // std::unique_ptr is never dereferenced, only assigned, to avoid the high
+ // cost of dynamic initialization checks, while still providing automatic
+ // cleanup. The normal pointer provides fast access to the buffer object.
+ // Never dereference buffer_guard or performance could be severely impacted
+ // by slow implementations of TLS dynamic initialization.
+ static thread_local BufferType* buffer_;
+
+ static std::unique_ptr<BufferType>& GetBufferGuard() {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard");
+ static thread_local std::unique_ptr<BufferType> buffer_guard;
+ return buffer_guard;
+ }
+};
+
+// Instantiation of the static ThreadLocalBuffer::buffer_ member.
+template <typename T, typename Allocator, std::size_t Capacity, typename Slot>
+thread_local
+ typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType*
+ ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/type_operators.h b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
new file mode 100644
index 0000000..811bd87
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
@@ -0,0 +1,195 @@
+#ifndef ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+#define ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+
+#include <array>
+#include <map>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/copy_cv_reference.h>
+#include <pdx/rpc/pointer_wrapper.h>
+#include <pdx/rpc/string_wrapper.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Simplifies type expressions.
+template <typename T>
+using Decay = typename std::decay<T>::type;
+
+// Compares the underlying type of A and B.
+template <typename A, typename B>
+using IsEquivalent = typename std::is_same<Decay<A>, Decay<B>>::type;
+
+// Logical AND over template parameter pack.
+template <typename... T>
+struct And : std::false_type {};
+template <typename A, typename B>
+struct And<A, B> : std::integral_constant<bool, A::value && B::value> {};
+template <typename A, typename B, typename... Rest>
+struct And<A, B, Rest...> : And<A, And<B, Rest...>> {};
+
+// Determines whether A is convertible to B (serializes to the same format)
+// using these rules:
+// 1. std:vector<T, Any...> is convertible to ArrayWrapper<T>.
+// 2. ArrayWrapper<T> is convertible to std:vector<T, Any...>.
+// 3. std::basic_string<T, Any...> is convertible to StringWrapper<T>.
+// 4. StringWrapper<T> is convertible to std::basic_string<T, Any...>.
+// 5. BufferWrapper<T*> is convertible to BufferWrapper<std::vector<T,
+// Any...>>.
+// 6. BufferWrapper<std::vector<T, ...>> is convertible to BufferWrapper<T*>.
+// 7. The value type T of A and B must match.
+
+// Compares A and B for convertibility. This base type determines convertibility
+// by equivalence of the underlying types of A and B. Specializations of this
+// type handle the rules for which complex types are convertible.
+template <typename A, typename B>
+struct IsConvertible : IsEquivalent<A, B> {};
+
+// Compares TT<A, ...> and TT<B, ...>; these are convertible if A and B are
+// convertible.
+template <template <typename, typename...> class TT, typename A, typename B,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<TT<A, AnyA...>, TT<B, AnyB...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares TT<KeyA, ValueA, ...> and TT<KeyB, ValueB, ...>; these are
+// convertible if KeyA and KeyB are
+// convertible and ValueA and ValueB are convertible.
+template <template <typename, typename, typename...> class TT, typename KeyA,
+ typename ValueA, typename KeyB, typename ValueB, typename... AnyA,
+ typename... AnyB>
+struct IsConvertible<TT<KeyA, ValueA, AnyA...>, TT<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares two std::pairs to see if the corresponding elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::pair<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares std::pair with a two-element std::tuple to see if the corresponding
+// elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::tuple<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::tuple<A, B>, std::pair<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares two std::tuples to see if the corresponding elements are
+// convertible.
+template <typename... A, typename... B>
+struct IsConvertible<std::tuple<A...>, std::tuple<B...>>
+ : And<IsConvertible<Decay<A>, Decay<B>>...> {};
+
+// Compares std::vector, std::array, and ArrayWrapper; these are convertible if
+// the value types are convertible.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::vector<A, Any...>, ArrayWrapper<B>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<ArrayWrapper<A>, std::vector<B, Any...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::vector<A, Any...>, std::array<B, Size>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, std::vector<B, Any...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<ArrayWrapper<A>, std::array<B, Size>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, ArrayWrapper<B>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares std::map and std::unordered_map; these are convertible if the keys
+// are convertible and the values are convertible.
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<std::map<KeyA, ValueA, AnyA...>,
+ std::unordered_map<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<std::unordered_map<KeyA, ValueA, AnyA...>,
+ std::map<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares BufferWrapper<A*> and BufferWrapper<std::vector<B>>; these are
+// convertible if A and B are equivalent. Allocator types are not relevant to
+// convertibility.
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<A*>,
+ BufferWrapper<std::vector<B, Allocator>>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<std::vector<A, Allocator>>,
+ BufferWrapper<B*>> : IsEquivalent<A, B> {};
+template <typename A, typename B, typename AllocatorA, typename AllocatorB>
+struct IsConvertible<BufferWrapper<std::vector<A, AllocatorA>>,
+ BufferWrapper<std::vector<B, AllocatorB>>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B>
+struct IsConvertible<BufferWrapper<A*>, BufferWrapper<B*>>
+ : IsEquivalent<A, B> {};
+
+// Compares std::basic_string<A, ...> and StringWrapper<B>; these are
+// convertible if A and B are equivalent.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::basic_string<A, Any...>, StringWrapper<B>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<StringWrapper<A>, std::basic_string<B, Any...>>
+ : IsEquivalent<A, B> {};
+
+// Compares PointerWrapper<A> and B; these are convertible if A and B are
+// convertible.
+template <typename A, typename B>
+struct IsConvertible<PointerWrapper<A>, B> : IsConvertible<Decay<A>, Decay<B>> {
+};
+template <typename A, typename B>
+struct IsConvertible<A, PointerWrapper<B>> : IsConvertible<Decay<A>, Decay<B>> {
+};
+
+// LocalHandle is convertible to RemoteHandle on the service side. This means
+// that a RemoteHandle may be supplied by a service when the protocol calls for
+// a LocalHandle return value. The other way around is not safe and can leak
+// file descriptors. The ServicePayload class enforces this policy by only
+// supporting RemoteHandle for pushed handles.
+template <>
+struct IsConvertible<LocalHandle, RemoteHandle> : std::true_type {};
+template <>
+struct IsConvertible<LocalHandle, BorrowedHandle> : std::true_type {};
+
+template <>
+struct IsConvertible<LocalChannelHandle, RemoteChannelHandle> : std::true_type {
+};
+template <>
+struct IsConvertible<LocalChannelHandle, BorrowedChannelHandle>
+ : std::true_type {};
+
+// Conditionally "rewrites" type A as type B, including cv-reference qualifiers,
+// iff A is convertible to B.
+template <typename A, typename B>
+using ConditionalRewrite =
+ typename std::conditional<IsConvertible<Decay<A>, Decay<B>>::value,
+ CopyCVReferenceType<A, B>, A>::type;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_TYPE_OPERATORS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
new file mode 100644
index 0000000..09789e5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -0,0 +1,693 @@
+#ifndef ANDROID_PDX_RPC_VARIANT_H_
+#define ANDROID_PDX_RPC_VARIANT_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Type tag denoting an empty variant.
+struct EmptyVariant {};
+
+namespace detail {
+
+// Type for matching tagged overloads.
+template <typename T>
+struct TypeTag {};
+
+// Determines the type of the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeForIndex = std::tuple_element_t<I, std::tuple<Types...>>;
+
+// Determines the type tag for the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>;
+
+// Enable if T(Args...) is well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfConstructible =
+ typename std::enable_if<std::is_constructible<T, Args...>::value, R>::type;
+// Enable if T(Args...) is not well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfNotConstructible =
+ typename std::enable_if<!std::is_constructible<T, Args...>::value, R>::type;
+
+// Determines whether T is an element of Types...;
+template <typename... Types>
+struct HasType : std::false_type {};
+template <typename T, typename U>
+struct HasType<T, U> : std::is_same<T, U> {};
+template <typename T, typename First, typename... Rest>
+struct HasType<T, First, Rest...>
+ : std::integral_constant<
+ bool, std::is_same<T, First>::value || HasType<T, Rest...>::value> {};
+
+template <typename T, typename... Types>
+using HasTypeIgnoreRef =
+ HasType<typename std::remove_reference<T>::type, Types...>;
+
+// Defines set operations on a set of Types...
+template <typename... Types>
+struct Set {
+ // Default specialization catches the empty set, which is always a subset.
+ template <typename...>
+ struct IsSubset : std::true_type {};
+ template <typename T>
+ struct IsSubset<T> : HasType<T, Types...> {};
+ template <typename First, typename... Rest>
+ struct IsSubset<First, Rest...>
+ : std::integral_constant<
+ bool, IsSubset<First>::value && IsSubset<Rest...>::value> {};
+};
+
+// Determines the number of elements of Types... that are constructible from
+// From.
+template <typename... Types>
+struct ConstructibleCount;
+template <typename From, typename To>
+struct ConstructibleCount<From, To>
+ : std::integral_constant<std::size_t,
+ std::is_constructible<To, From>::value> {};
+template <typename From, typename First, typename... Rest>
+struct ConstructibleCount<From, First, Rest...>
+ : std::integral_constant<std::size_t,
+ std::is_constructible<First, From>::value +
+ ConstructibleCount<From, Rest...>::value> {};
+
+// Enable if T is an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfElement =
+ typename std::enable_if<HasTypeIgnoreRef<T, Types...>::value, R>::type;
+// Enable if T is not an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfNotElement =
+ typename std::enable_if<!HasTypeIgnoreRef<T, Types...>::value, R>::type;
+
+// Enable if T is convertible to an element of Types... T is considered
+// convertible IIF a single element of Types... is assignable from T and T is
+// not a direct element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfConvertible =
+ typename std::enable_if<!HasTypeIgnoreRef<T, Types...>::value &&
+ ConstructibleCount<T, Types...>::value == 1,
+ R>::type;
+
+// Enable if T is assignable to an element of Types... T is considered
+// assignable IFF a single element of Types... is constructible from T or T is a
+// direct element of Types.... Note that T is REQUIRED to be an element of
+// Types... when multiple elements are constructible from T to prevent ambiguity
+// in conversion.
+template <typename R, typename T, typename... Types>
+using EnableIfAssignable =
+ typename std::enable_if<HasTypeIgnoreRef<T, Types...>::value ||
+ ConstructibleCount<T, Types...>::value == 1,
+ R>::type;
+
+// Selects a type for SFINAE constructor selection.
+template <bool CondA, typename SelectA, typename SelectB>
+using Select = std::conditional_t<CondA, SelectA, SelectB>;
+
+// Recursive union type.
+template <typename... Types>
+union Union;
+
+// Specialization handling a singular type, terminating template recursion.
+template <typename Type>
+union Union<Type> {
+ Union() {}
+ ~Union() {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<Type>, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T, typename = EnableIfAssignable<void, T, Type>>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+
+ Type& get(TypeTag<Type>) { return first_; }
+ const Type& get(TypeTag<Type>) const { return first_; }
+ EmptyVariant get(TypeTag<EmptyVariant>) const { return {}; }
+ constexpr std::int32_t index(TypeTag<Type>) const { return 0; }
+
+ template <typename... Args>
+ std::int32_t Construct(TypeTag<Type>, Args&&... args) {
+ new (&first_) Type(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename... Args>
+ EnableIfConstructible<std::int32_t, Type, Args...> Construct(Args&&... args) {
+ new (&first_) Type(std::forward<Args>(args)...);
+ return 0;
+ }
+
+ void Destruct(std::int32_t target_index) {
+ if (target_index == index(TypeTag<Type>{})) {
+ (&get(TypeTag<Type>{}))->~Type();
+ }
+ }
+
+ template <typename T>
+ bool Assign(TypeTag<Type>, std::int32_t target_index, T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T>
+ EnableIfConstructible<bool, Type, T> Assign(std::int32_t target_index,
+ T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T>
+ EnableIfNotConstructible<bool, Type, T> Assign(std::int32_t /*target_index*/,
+ T&& /*value*/) {
+ return false;
+ }
+
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+ if (target_index == index(TypeTag<Type>{}))
+ return std::forward<Op>(op)(get(TypeTag<Type>{}));
+ else
+ return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+ }
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+ if (target_index == index(TypeTag<Type>{}))
+ return std::forward<Op>(op)(get(TypeTag<Type>{}));
+ else
+ return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+ }
+
+ template <typename... Args>
+ bool Become(std::int32_t target_index, Args&&... args) {
+ if (target_index == index(TypeTag<Type>{})) {
+ Construct(TypeTag<Type>{}, std::forward<Args>(args)...);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ Type first_;
+};
+
+// Specialization that recursively unions types from the paramater pack.
+template <typename First, typename... Rest>
+union Union<First, Rest...> {
+ Union() {}
+ ~Union() {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<First>, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T, typename U>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<T>, U&& value)
+ : rest_(index + 1, index_out, TypeTag<T>{}, std::forward<U>(value)) {}
+
+ struct FirstType {};
+ struct RestType {};
+ template <typename T>
+ using SelectConstructor =
+ Select<ConstructibleCount<T, First>::value == 1, FirstType, RestType>;
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value)
+ : Union(index, index_out, std::forward<T>(value),
+ SelectConstructor<T>{}) {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType)
+ : rest_(index + 1, index_out, std::forward<T>(value)) {}
+
+ First& get(TypeTag<First>) { return first_; }
+ const First& get(TypeTag<First>) const { return first_; }
+ constexpr std::int32_t index(TypeTag<First>) const { return 0; }
+
+ template <typename T>
+ T& get(TypeTag<T>) {
+ return rest_.template get(TypeTag<T>{});
+ }
+ template <typename T>
+ const T& get(TypeTag<T>) const {
+ return rest_.template get(TypeTag<T>{});
+ }
+ template <typename T>
+ constexpr std::int32_t index(TypeTag<T>) const {
+ return 1 + rest_.template index(TypeTag<T>{});
+ }
+
+ template <typename... Args>
+ std::int32_t Construct(TypeTag<First>, Args&&... args) {
+ new (&first_) First(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename T, typename... Args>
+ std::int32_t Construct(TypeTag<T>, Args&&... args) {
+ return 1 +
+ rest_.template Construct(TypeTag<T>{}, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ EnableIfConstructible<std::int32_t, First, Args...> Construct(
+ Args&&... args) {
+ new (&first_) First(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename... Args>
+ EnableIfNotConstructible<std::int32_t, First, Args...> Construct(
+ Args&&... args) {
+ return 1 + rest_.template Construct(std::forward<Args>(args)...);
+ }
+
+ void Destruct(std::int32_t target_index) {
+ if (target_index == index(TypeTag<First>{})) {
+ (get(TypeTag<First>{})).~First();
+ } else {
+ rest_.Destruct(target_index - 1);
+ }
+ }
+
+ template <typename T>
+ bool Assign(TypeTag<First>, std::int32_t target_index, T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T, typename U>
+ bool Assign(TypeTag<T>, std::int32_t target_index, U&& value) {
+ return rest_.Assign(TypeTag<T>{}, target_index - 1, std::forward<U>(value));
+ }
+ template <typename T>
+ EnableIfConstructible<bool, First, T> Assign(std::int32_t target_index,
+ T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return rest_.Assign(target_index - 1, std::forward<T>(value));
+ }
+ }
+ template <typename T>
+ EnableIfNotConstructible<bool, First, T> Assign(std::int32_t target_index,
+ T&& value) {
+ return rest_.Assign(target_index - 1, std::forward<T>(value));
+ }
+
+ // Recursively traverses the union and calls Op on the active value when the
+ // active type is found. If the union is empty Op is called on EmptyVariant.
+ // TODO(eieio): This could be refactored into an array or jump table. It's
+ // unclear whether this would be more efficient for practical variant arity.
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+ if (target_index == index(TypeTag<First>{}))
+ return std::forward<Op>(op)(get(TypeTag<First>{}));
+ else
+ return rest_.Visit(target_index - 1, std::forward<Op>(op));
+ }
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+ if (target_index == index(TypeTag<First>{}))
+ return std::forward<Op>(op)(get(TypeTag<First>{}));
+ else
+ return rest_.Visit(target_index - 1, std::forward<Op>(op));
+ }
+
+ template <typename... Args>
+ bool Become(std::int32_t target_index, Args&&... args) {
+ if (target_index == index(TypeTag<First>{})) {
+ Construct(TypeTag<First>{}, std::forward<Args>(args)...);
+ return true;
+ } else {
+ return rest_.Become(target_index - 1, std::forward<Args>(args)...);
+ }
+ }
+
+ private:
+ First first_;
+ Union<Rest...> rest_;
+};
+
+} // namespace detail
+
+template <typename... Types>
+class Variant {
+ private:
+ // Convenience types.
+ template <typename T>
+ using TypeTag = detail::TypeTag<T>;
+ template <typename T>
+ using TypeTagIgnoreRef = TypeTag<typename std::remove_reference<T>::type>;
+ template <std::size_t I>
+ using TypeForIndex = detail::TypeForIndex<I, Types...>;
+ template <std::size_t I>
+ using TypeTagForIndex = detail::TypeTagForIndex<I, Types...>;
+ template <typename T>
+ using HasType = detail::HasType<T, Types...>;
+ template <typename T>
+ using HasTypeIgnoreRef = detail::HasTypeIgnoreRef<T, Types...>;
+ template <typename R, typename T>
+ using EnableIfElement = detail::EnableIfElement<R, T, Types...>;
+ template <typename R, typename T>
+ using EnableIfConvertible = detail::EnableIfConvertible<R, T, Types...>;
+ template <typename R, typename T>
+ using EnableIfAssignable = detail::EnableIfAssignable<R, T, Types...>;
+
+ struct Direct {};
+ struct Convert {};
+ template <typename T>
+ using SelectConstructor =
+ detail::Select<HasTypeIgnoreRef<T>::value, Direct, Convert>;
+
+ // Constructs by type tag when T is an direct element of Types...
+ template <typename T>
+ explicit Variant(T&& value, Direct)
+ : value_(0, &index_, TypeTagIgnoreRef<T>{}, std::forward<T>(value)) {}
+ // Conversion constructor when T is not a direct element of Types...
+ template <typename T>
+ explicit Variant(T&& value, Convert)
+ : value_(0, &index_, std::forward<T>(value)) {}
+
+ public:
+ // Variants are default construcible, regardless of whether the elements are
+ // default constructible. Default consruction yields an empty Variant.
+ Variant() {}
+ explicit Variant(EmptyVariant) {}
+ ~Variant() { Destruct(); }
+
+ // Copy and move construction from Variant types. Each element of OtherTypes
+ // must be convertible to an element of Types.
+ template <typename... OtherTypes>
+ explicit Variant(const Variant<OtherTypes...>& other) {
+ other.Visit([this](const auto& value) { Construct(value); });
+ }
+ template <typename... OtherTypes>
+ explicit Variant(Variant<OtherTypes...>&& other) {
+ other.Visit([this](auto&& value) { Construct(std::move(value)); });
+ }
+
+ // Construction from non-Variant types.
+ template <typename T, typename = EnableIfAssignable<void, T>>
+ explicit Variant(T&& value)
+ : Variant(std::forward<T>(value), SelectConstructor<T>{}) {}
+
+ // Performs assignment from type T belonging to Types. This overload takes
+ // priority to prevent implicit conversion in cases where T is implicitly
+ // convertible to multiple elements of Types.
+ template <typename T>
+ EnableIfElement<Variant&, T> operator=(T&& value) {
+ Assign(TypeTagIgnoreRef<T>{}, std::forward<T>(value));
+ return *this;
+ }
+
+ // Performs assignment from type T not belonging to Types. This overload
+ // matches in cases where conversion is the only viable option.
+ template <typename T>
+ EnableIfConvertible<Variant&, T> operator=(T&& value) {
+ Assign(std::forward<T>(value));
+ return *this;
+ }
+
+ // Handles assignment from the empty type. This overload supports assignment
+ // in visitors using generic lambdas.
+ Variant& operator=(EmptyVariant) {
+ Assign(EmptyVariant{});
+ return *this;
+ }
+
+ // Assignment from Variant types. Each element of OtherTypes must be
+ // convertible to an element of Types. Forwards through non-Variant assignment
+ // operators to apply conversion checks.
+ template <typename... OtherTypes>
+ Variant& operator=(const Variant<OtherTypes...>& other) {
+ other.Visit([this](const auto& value) { *this = value; });
+ return *this;
+ }
+ template <typename... OtherTypes>
+ Variant& operator=(Variant<OtherTypes...>&& other) {
+ other.Visit([this](auto&& value) { *this = std::move(value); });
+ return *this;
+ }
+
+ // Becomes the target type, constructing a new element from the given
+ // arguments if necessary. No action is taken if the active element is already
+ // the target type. Otherwise the active element is destroyed and replaced by
+ // constructing an element of the new type using |Args|. An invalid target
+ // type index results in an empty Variant.
+ template <typename... Args>
+ void Become(std::int32_t target_index, Args&&... args) {
+ if (target_index != index()) {
+ Destruct();
+ index_ = value_.Become(target_index, std::forward<Args>(args)...)
+ ? target_index
+ : kEmptyIndex;
+ }
+ }
+
+ // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked
+ // on EmptyVariant.
+ template <typename Op>
+ decltype(auto) Visit(Op&& op) {
+ return value_.Visit(index_, std::forward<Op>(op));
+ }
+ template <typename Op>
+ decltype(auto) Visit(Op&& op) const {
+ return value_.Visit(index_, std::forward<Op>(op));
+ }
+
+ // Index returned when the Variant is empty.
+ enum : std::int32_t { kEmptyIndex = -1 };
+
+ // Returns the index of the given type.
+ template <typename T>
+ constexpr std::int32_t index_of() const {
+ static_assert(HasType<T>::value, "T is not an element type of Variant.");
+ return value_.template index(TypeTag<T>{});
+ }
+
+ // Returns the index of the active type. If the Variant is empty -1 is
+ // returned.
+ std::int32_t index() const { return index_; }
+
+ // Returns true if the given type is active, false otherwise.
+ template <typename T>
+ bool is() const {
+ static_assert(HasType<T>::value, "T is not an element type of Variant.");
+ return index() == index_of<T>();
+ }
+
+ // Returns true if the Variant is empty, false otherwise.
+ bool empty() const { return index() == kEmptyIndex; }
+
+ // Element accessors. Returns a pointer to the active value if the given
+ // type/index is active, otherwise nullptr is returned.
+ template <typename T>
+ T* get() {
+ if (is<T>())
+ return &value_.template get(TypeTag<T>{});
+ else
+ return nullptr;
+ }
+ template <typename T>
+ const T* get() const {
+ if (is<T>())
+ return &value_.template get(TypeTag<T>{});
+ else
+ return nullptr;
+ }
+ template <std::size_t I>
+ TypeForIndex<I>* get() {
+ if (is<TypeForIndex<I>>())
+ return &value_.template get(TypeTagForIndex<I>{});
+ else
+ return nullptr;
+ }
+ template <std::size_t I>
+ const TypeForIndex<I>* get() const {
+ if (is<TypeForIndex<I>>())
+ return &value_.template get(TypeTagForIndex<I>{});
+ else
+ return nullptr;
+ }
+
+ private:
+ std::int32_t index_ = kEmptyIndex;
+ detail::Union<Types...> value_;
+
+ // Constructs an element from the given arguments and sets the Variant to the
+ // resulting type.
+ template <typename... Args>
+ void Construct(Args&&... args) {
+ index_ = value_.template Construct(std::forward<Args>(args)...);
+ }
+ void Construct(EmptyVariant) {}
+
+ // Destroys the active element of the Variant.
+ void Destruct() { value_.Destruct(index_); }
+
+ // Assigns the Variant when non-empty and the current type matches the target
+ // type, otherwise destroys the current value and constructs a element of the
+ // new type. Tagged assignment is used when T is an element of the Variant to
+ // prevent implicit conversion in cases where T is implicitly convertible to
+ // multiple element types.
+ template <typename T, typename U>
+ void Assign(TypeTag<T>, U&& value) {
+ if (!value_.template Assign(TypeTag<T>{}, index_, std::forward<U>(value))) {
+ Destruct();
+ Construct(TypeTag<T>{}, std::forward<U>(value));
+ }
+ }
+ template <typename T>
+ void Assign(T&& value) {
+ if (!value_.template Assign(index_, std::forward<T>(value))) {
+ Destruct();
+ Construct(std::forward<T>(value));
+ }
+ }
+ // Handles assignment from an empty Variant.
+ void Assign(EmptyVariant) { Destruct(); }
+};
+
+// Utility type to extract/convert values from a variant. This class simplifies
+// conditional logic to get/move/swap/action values from a variant when one or
+// more elements are compatible with the destination type.
+//
+// Example:
+// Variant<int, bool, std::string> v(10);
+// bool bool_value;
+// if (IfAnyOf<int, bool>::Get(v, &bool_value)) {
+// DoSomething(bool_value);
+// } else {
+// HandleInvalidType();
+// }
+// IfAnyOf<int>::Call(v, [](const auto& value) { DoSomething(value); });
+//
+template <typename... ValidTypes>
+struct IfAnyOf {
+ // Calls Op on the underlying value of the variant and returns true when the
+ // variant is a valid type, otherwise does nothing and returns false.
+ template <typename Op, typename... Types>
+ static bool Call(Variant<Types...>* variant, Op&& op) {
+ static_assert(
+ detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+ "ValidTypes may only contain element types from the Variant.");
+ return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+ }
+ template <typename Op, typename... Types>
+ static bool Call(const Variant<Types...>* variant, Op&& op) {
+ static_assert(
+ detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+ "ValidTypes may only contain element types from the Variant.");
+ return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+ }
+
+ // Gets/converts the underlying value of the variant to type T and returns
+ // true when the variant is a valid type, otherwise does nothing and returns
+ // false.
+ template <typename T, typename... Types>
+ static bool Get(const Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](const auto& value) { *value_out = value; });
+ }
+
+ // Moves the underlying value of the variant and returns true when the variant
+ // is a valid type, otherwise does nothing and returns false.
+ template <typename T, typename... Types>
+ static bool Take(Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](auto&& value) { *value_out = std::move(value); });
+ }
+
+ // Swaps the underlying value of the variant with |*value_out| and returns
+ // true when the variant is a valid type, otherwise does nothing and returns
+ // false.
+ template <typename T, typename... Types>
+ static bool Swap(Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](auto&& value) { std::swap(*value_out, value); });
+ }
+
+ private:
+ template <typename Op>
+ struct CallOp {
+ Op&& op;
+ template <typename U>
+ detail::EnableIfNotElement<bool, U, ValidTypes...> operator()(U&&) {
+ return false;
+ }
+ template <typename U>
+ detail::EnableIfElement<bool, U, ValidTypes...> operator()(const U& value) {
+ std::forward<Op>(op)(value);
+ return true;
+ }
+ template <typename U>
+ detail::EnableIfElement<bool, U, ValidTypes...> operator()(U&& value) {
+ std::forward<Op>(op)(std::forward<U>(value));
+ return true;
+ }
+ };
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+// Overloads of std::get<T> and std::get<I> for android::pdx::rpc::Variant.
+namespace std {
+
+template <typename T, typename... Types>
+inline T& get(::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<T>();
+}
+template <typename T, typename... Types>
+inline T&& get(::android::pdx::rpc::Variant<Types...>&& v) {
+ return std::move(*v.template get<T>());
+}
+template <typename T, typename... Types>
+inline const T& get(const ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<T>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+ ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<I>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>&& get(
+ ::android::pdx::rpc::Variant<Types...>&& v) {
+ return std::move(*v.template get<I>());
+}
+template <std::size_t I, typename... Types>
+inline const ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+ const ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<I>();
+}
+
+} // namespace std
+
+#endif // ANDROID_PDX_RPC_VARIANT_H_
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
new file mode 100644
index 0000000..029e6bf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -0,0 +1,703 @@
+#ifndef ANDROID_PDX_SERVICE_H_
+#define ANDROID_PDX_SERVICE_H_
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "pdx/channel_handle.h"
+#include "pdx/file_handle.h"
+#include "pdx/message_reader.h"
+#include "pdx/message_writer.h"
+#include "pdx/service_endpoint.h"
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+namespace opcodes {
+
+/*
+ * Reserved message opcodes used by libpdx. The reserved opcodes start at the
+ * max positive signed integer for the system and go down.
+ * In contrast, service opcodes start at zero and go up. This scheme leaves
+ * most of the positive integer space for services, a tiny fraction of the
+ * positive integer space for the framework, and the entire negative integer
+ * space for the kernel.
+ */
+#define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n)) // 0x7fff..ffff - n
+
+enum {
+ // System message sent when a new client channel is open.
+ CHANNEL_OPEN = -1,
+ // System message sent when a channel is closed.
+ CHANNEL_CLOSE = -2,
+ // Request the service to reload system properties.
+ PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0),
+ // Request the service to dump state and return it in a text buffer.
+ PDX_OPCODE(DUMP_STATE, 1),
+};
+
+} // namespace opcodes
+
+/*
+ * Base class of service-side per-channel context classes.
+ */
+class Channel : public std::enable_shared_from_this<Channel> {
+ public:
+ Channel() {}
+ virtual ~Channel() {}
+
+ /*
+ * Utility to get a shared_ptr reference from the channel context pointer.
+ */
+ static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+};
+
+/*
+ * Message class represents an RPC message, and implicitly the blocked sender
+ * waiting for a response. Every message should get a reply, at some point
+ * (unless the endpoint is closed), to prevent clients from blocking
+ * indefinitely. In order to enforce this and prevent leaking message ids,
+ * Message automatically replies with an error to the client on destruction,
+ * unless one of two things happens:
+ *
+ * 1. The service calls one of the reply methods before the Message is
+ * destroyed.
+ * 2. The responsibility for the message is moved to another instance of
+ * Message, using either move construction or move assignment.
+ *
+ * The second case is useful for services that need to delay responding to a
+ * sender until a later time. In this situation the service can move the
+ * Message to another instance in a suitable data structure for later use. The
+ * moved-to Message then takes on the same behavior and responsibilities
+ * described above.
+ */
+class Message : public OutputResourceMapper, public InputResourceMapper {
+ public:
+ Message();
+ Message(const MessageInfo& info);
+ ~Message();
+
+ /*
+ * Message objects support move construction and assignment.
+ */
+ Message(Message&& other);
+ Message& operator=(Message&& other);
+
+ /*
+ * Read/write payload, in either single buffer or iovec form.
+ */
+ ssize_t ReadVector(const iovec* vector, size_t vector_length);
+ ssize_t Read(void* buffer, size_t length);
+ ssize_t WriteVector(const iovec* vector, size_t vector_length);
+ ssize_t Write(const void* buffer, size_t length);
+
+ template <size_t N>
+ inline ssize_t ReadVector(const iovec (&vector)[N]) {
+ return ReadVector(vector, N);
+ }
+
+ template <size_t N>
+ inline ssize_t WriteVector(const iovec (&vector)[N]) {
+ return WriteVector(vector, N);
+ }
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override;
+ FileReference PushFileHandle(const BorrowedHandle& handle) override;
+ FileReference PushFileHandle(const RemoteHandle& handle) override;
+ ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ /*
+ * Various ways to reply to a message.
+ */
+ int Reply(int return_code);
+ int ReplyError(unsigned error);
+ int ReplyFileDescriptor(unsigned int fd);
+ int Reply(const LocalHandle& handle);
+ int Reply(const BorrowedHandle& handle);
+ int Reply(const RemoteHandle& handle);
+ int Reply(const LocalChannelHandle& handle);
+ int Reply(const BorrowedChannelHandle& handle);
+ int Reply(const RemoteChannelHandle& handle);
+
+ template <typename T>
+ inline int Reply(const Status<T>& status) {
+ return status ? Reply(status.get()) : ReplyError(status.error());
+ }
+
+ /*
+ * Update the channel event bits with the given clear and set masks.
+ */
+ int ModifyChannelEvents(int clear_mask, int set_mask);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the client. See
+ * Service::PushChannel() for a detail description of this method's operation.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ int flags, const std::shared_ptr<Channel>& channel, int* channel_id);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the client. See
+ * Service::PushChannel() for a detail description of this method's operation.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ Service* service, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id);
+
+ /*
+ * Check whether the |ref| is a reference to channel to this service.
+ * If the channel reference in question is valid, the Channel object is
+ * returned in |channel| when non-nullptr.
+ *
+ * Return values:
+ * channel_id - id of the channel if the |ref| is a valid reference to
+ * this service's channel.
+ * Errors:
+ * EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+ * another service.
+ * EBADF - the file descriptor is invalid.
+ * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+ * memory addresses.
+ * EINVAL - the value of |ref| is invalid or the message id for this
+ * message is no longer valid.
+ */
+ Status<int> CheckChannel(ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that checks whether the channel reference is for
+ * a channel to the service |service|.
+ */
+ Status<int> CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that automatically converts to shared pointers
+ * to types derived from Channel.
+ */
+ template <class C>
+ Status<int> CheckChannel(ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ template <class C>
+ Status<int> CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(service, ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ /*
+ * MessageInfo accessors.
+ */
+ pid_t GetProcessId() const;
+ pid_t GetThreadId() const;
+ uid_t GetEffectiveUserId() const;
+ gid_t GetEffectiveGroupId() const;
+ int GetChannelId() const;
+ int GetMessageId() const;
+ int GetOp() const;
+ int GetFlags() const;
+ size_t GetSendLength() const;
+ size_t GetReceiveLength() const;
+ size_t GetFileDescriptorCount() const;
+
+ /*
+ * Impulses are asynchronous and cannot be replied to. All impulses have this
+ * invalid message id.
+ */
+ enum { IMPULSE_MESSAGE_ID = -1 };
+
+ /*
+ * Returns true if this Message describes an asynchronous "impulse" message.
+ */
+ bool IsImpulse() const { return GetMessageId() == IMPULSE_MESSAGE_ID; }
+
+ /*
+ * Returns a pointer to the impulse payload. Impulses are a maximum of 32
+ * bytes in size and the start of the impulse payload is guaranteed to be
+ * 8-byte aligned. Use GetSendLength() to determine the payload size.
+ */
+ const std::uint8_t* ImpulseBegin() const;
+
+ /*
+ * Returns one byte past the end of the impulse payload, as conventional for
+ * STL iterators.
+ */
+ const std::uint8_t* ImpulseEnd() const;
+
+ /*
+ * Get/set the Channel object for the channel associated
+ * with this message. It is up to the caller to synchronize
+ * these in multi-threaded services.
+ */
+ std::shared_ptr<Channel> GetChannel() const;
+ void SetChannel(const std::shared_ptr<Channel>& channnel);
+
+ /*
+ * Get the Channel object for the channel associated with this message,
+ * automatically converted to the desired subclass of Channel.
+ */
+ template <class C>
+ std::shared_ptr<C> GetChannel() const {
+ return std::static_pointer_cast<C>(GetChannel());
+ }
+
+ /*
+ * Gets the service this message was received on. Returns nullptr if the
+ * service was destroyed.
+ */
+ std::shared_ptr<Service> GetService() const;
+
+ /*
+ * Raw access to the MessageInfo for this message.
+ */
+ const MessageInfo& GetInfo() const;
+
+ bool replied() const { return replied_; }
+ bool IsChannelExpired() const { return channel_.expired(); }
+ bool IsServiceExpired() const { return service_.expired(); }
+
+ /*
+ * Returns true if the message is non-empty; that is the message can be
+ * replied to using this instance.
+ */
+ explicit operator bool() const { return !replied_; }
+
+ const void* GetState() const { return state_; }
+ void* GetState() { return state_; }
+
+ private:
+ friend class Service;
+
+ Message(const Message&) = delete;
+ void operator=(const Message&) = delete;
+ void Destroy();
+
+ std::weak_ptr<Service> service_;
+ std::weak_ptr<Channel> channel_;
+ MessageInfo info_;
+ void* state_{nullptr};
+ bool replied_;
+};
+
+// Base class for RPC services.
+class Service : public std::enable_shared_from_this<Service> {
+ public:
+ Service(const std::string& name, std::unique_ptr<Endpoint> endpoint);
+ virtual ~Service();
+
+ /*
+ * Utility to get a shared_ptr reference from the service context pointer.
+ */
+ static std::shared_ptr<Service> GetFromMessageInfo(const MessageInfo& info);
+
+ /*
+ * Returns whether initialization was successful. Subclasses that override
+ * this must call this base method and AND the results with their own. This
+ * method is not intended to do any initialization work itself, only to
+ * signal success or failure.
+ */
+ virtual bool IsInitialized() const;
+
+ /*
+ * Called by defaultHandleMessage in response to a CHANNEL_OPEN message.
+ * This gives subclasses of Service a convenient hook to create per-channel
+ * context in the form of a Channel subclass.
+ *
+ * The Channel instance returned by this is used to set the channel context
+ * pointer for the channel that was just opened.
+ */
+ virtual std::shared_ptr<Channel> OnChannelOpen(Message& message);
+
+ /*
+ * Called by defaultHandleMessage in response to a CHANNEL_CLOSE message.
+ * This give subclasses of Service a convenient hook to clean up per-channel
+ * context.
+ */
+ virtual void OnChannelClose(Message& message,
+ const std::shared_ptr<Channel>& channel);
+
+ /*
+ * Set the channel context for the given channel. This keeps a reference to
+ * the Channel object until the channel is closed or another call replaces
+ * the current value.
+ */
+ int SetChannel(int channel_id, const std::shared_ptr<Channel>& channel);
+
+ /*
+ * Get the channel context for the given channel id. This method should be
+ * used sparingly because of the performance characteristics of the underlying
+ * map; it is intended for limited, non-critical path access from outside of
+ * message dispatch. In most cases lookup by id should be unnecessary in a
+ * properly designed service; Message::GetChannel() should be used instead
+ * whenever an operation is in the context of a message.
+ *
+ * Again, if you lookup a channel context object for a service by id in a
+ * message handling path for the same service, you're probably doing something
+ * wrong.
+ */
+ std::shared_ptr<Channel> GetChannel(int channel_id) const;
+
+ /*
+ * Get a snapshot of the active channels for this service. This is the
+ * preferred way to access the set of channels because it avoids potential
+ * deadlocks and race conditions that may occur when operating on the channel
+ * map directly. However, it is more expensive than direct iteration because
+ * of dynamic memory allocation and shared pointer reference costs.
+ *
+ * Automatically converts returned items to shared pointers of the type
+ * std::shared_ptr<C>, where C is the subclass of Channel used by the service.
+ */
+ template <class C>
+ std::vector<std::shared_ptr<C>> GetChannels() const {
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+ std::vector<std::shared_ptr<C>> items;
+ items.reserve(channels_.size());
+
+ for (const auto& pair : channels_) {
+ items.push_back(std::static_pointer_cast<C>(pair.second));
+ }
+
+ return items;
+ }
+
+ /*
+ * Close a channel, signaling the client file object and freeing the channel
+ * id. Once closed, the client side of the channel always returns the error
+ * ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ *
+ * The internal reference to the Channel instance associated with the channel
+ * is removed, which may result in the Channel object being freed.
+ *
+ * OnChannelClosed is not called in response to this method call.
+ */
+ int CloseChannel(int channel_id);
+
+ /*
+ * Update the event bits for the given channel (given by id), using the
+ * given clear and set masks.
+ *
+ * This is useful for asynchronously signaling events that clients may be
+ * waiting for using select/poll/epoll.
+ */
+ int ModifyChannelEvents(int channel_id, int clear_mask, int set_mask);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the process
+ * sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ * O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ * sending process may change these later using fcntl()). The internal Channel
+ * instance associated with this channel is set to |channel|, which may be
+ * nullptr. The new channel id allocated for this channel is returned in
+ * |channel_id|, which may also be nullptr if not needed.
+ *
+ * On success, returns the remote channel handle for the new channel in the
+ * sending process' handle space. This MUST be returned to the sender via
+ * Message::Reply(), Message::Write(), or Message::WriteVector().
+ *
+ * On error, returns an errno code describing the cause of the error.
+ *
+ * Service::OnChannelCreate() is not called in response to the creation of the
+ * new channel.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ Message* message, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id);
+
+ /*
+ * Check whether the |ref| is a reference to a channel to this service.
+ * If the channel reference in question is valid, the Channel object is
+ * returned in |channel| when non-nullptr.
+ *
+ * Return values:
+ * channel_id - id of the channel if the channel reference.
+ * Errors:
+ * EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+ * another service.
+ * EBADF - the file descriptor is invalid.
+ * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+ * memory addresses.
+ * EINVAL - the value of |ref| is invalid or the message id for this
+ * message is no longer valid.
+ */
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that automatically converts to shared pointers
+ * of types derived from Channel.
+ */
+ template <class C>
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(message, ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ /*
+ * Handle a message. Subclasses override this to receive messages and decide
+ * how to dispatch them.
+ *
+ * The default implementation simply calls defaultHandleMessage().
+ * Subclasses should call the same for any unrecognized message opcodes.
+ */
+ virtual int HandleMessage(Message& message);
+
+ /*
+ * Handle an asynchronous message. Subclasses override this to receive
+ * asynchronous "impulse" messages. Impulses have a limited-size payload that
+ * is transferred upfront with the message description.
+ */
+ virtual void HandleImpulse(Message& impulse);
+
+ /*
+ * The default message handler. It is important that all messages
+ * (eventually) get a reply. This method should be called by subclasses for
+ * any unrecognized opcodes or otherwise unhandled messages to prevent
+ * erroneous requests from blocking indefinitely.
+ *
+ * Provides default handling of CHANNEL_OPEN and CHANNEL_CLOSE, calling
+ * OnChannelOpen() and OnChannelClose(), respectively.
+ *
+ * For all other message opcodes, this method replies with -ENOTSUP.
+ */
+ int DefaultHandleMessage(Message& message);
+
+ /*
+ * Called when system properties have changed. Subclasses should implement
+ * this method if they need to handle when system properties change.
+ */
+ virtual void OnSysPropChange();
+
+ /*
+ * Get the endpoint for the service.
+ */
+ Endpoint* endpoint() const { return endpoint_.get(); }
+
+ /*
+ * Cancels the endpoint, unblocking any receiver threads waiting in
+ * ReceiveAndDispatch().
+ */
+ int Cancel();
+
+ /*
+ * Iterator type for Channel map iterators.
+ */
+ using ChannelIterator =
+ std::unordered_map<int, std::shared_ptr<Channel>>::iterator;
+
+ /*
+ * Iterates over the Channel map and performs the action given by |action| on
+ * each channel map item (const ChannelIterator::value_type).
+ * |channels_mutex_| is not held; it is the responsibility of the caller to
+ * ensure serialization between threads that modify or iterate over the
+ * Channel map.
+ */
+ template <class A>
+ void ForEachChannelUnlocked(A action) const {
+ std::for_each(channels_.begin(), channels_.end(), action);
+ }
+
+ /*
+ * Iterates over the Channel map and performs the action given by |action| on
+ * each channel map item (const ChannelIterator::value_type).
+ * |channels_mutex_| is held to serialize access to the map; care must be
+ * taken to avoid recursively acquiring the mutex, for example, by calling
+ * Service::{GetChannel,SetChannel,CloseChannel,PushChannel}() or
+ * Message::SetChannel() in the action.
+ */
+ template <class A>
+ void ForEachChannel(A action) const {
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+ ForEachChannelUnlocked(action);
+ }
+
+ /*
+ * Subclasses of Service may override this method to provide a text string
+ * describing the state of the service. This method is called by
+ * HandleSystemMessage in response to the standard
+ * DUMP_STATE message. The string returned to the dump state client is
+ * truncated to |max_length| and reflects the maximum size the client can
+ * handle.
+ */
+ virtual std::string DumpState(size_t max_length);
+
+ /*
+ * Receives a message on this Service instance's endpoint and dispatches it.
+ * If the endpoint is in blocking mode this call blocks until a message is
+ * received, a signal is delivered to this thread, or the service is canceled.
+ * If the endpoint is in non-blocking mode and a message is not pending this
+ * call returns immediately with -ETIMEDOUT.
+ */
+ int ReceiveAndDispatch();
+
+ private:
+ friend class Message;
+
+ bool HandleSystemMessage(Message& message);
+
+ Service(const Service&);
+ void operator=(const Service&) = delete;
+
+ const std::string name_;
+ std::unique_ptr<Endpoint> endpoint_;
+
+ /*
+ * Maintains references to active channels.
+ */
+ mutable std::mutex channels_mutex_;
+ std::unordered_map<int, std::shared_ptr<Channel>> channels_;
+};
+
+/*
+ * Utility base class for services. This template handles allocation and
+ * initialization checks, reducing boiler plate code.
+ */
+template <typename TYPE>
+class ServiceBase : public Service {
+ public:
+ /*
+ * Static service allocation method that check for initialization errors.
+ * If errors are encountered these automatically clean up and return
+ * nullptr.
+ */
+ template <typename... Args>
+ static inline std::shared_ptr<TYPE> Create(Args&&... args) {
+ std::shared_ptr<TYPE> service(new TYPE(std::forward<Args>(args)...));
+ if (service->IsInitialized())
+ return service;
+ else
+ return nullptr;
+ }
+
+ protected:
+ /*
+ * Shorthand for subclasses to refer to this base, particularly
+ * to call the base class constructor.
+ */
+ typedef ServiceBase<TYPE> BASE;
+
+ ServiceBase(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+ : Service(name, std::move(endpoint)) {}
+};
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+#define PDX_ERROR_PREFIX "[" __FILE__ ":" STRINGIFY(__LINE__) "]"
+
+/*
+ * Macros for replying to messages. Error handling can be tedious;
+ * these macros make things a little cleaner.
+ */
+#define CHECK_ERROR(cond, error, fmt, ...) \
+ do { \
+ if ((cond)) { \
+ ALOGE(fmt, ##__VA_ARGS__); \
+ goto error; \
+ } \
+ } while (0)
+
+#define REPLY_ERROR(message, error, error_label) \
+ do { \
+ int __ret = message.ReplyError(error); \
+ CHECK_ERROR(__ret < 0, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ strerror(-__ret)); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_ERROR_RETURN(message, error, ...) \
+ do { \
+ int __ret = message.ReplyError(error); \
+ ALOGE_IF(__ret < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ strerror(-__ret)); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define REPLY_MESSAGE(message, message_return_code, error_label) \
+ do { \
+ int __ret = message.Reply(message_return_code); \
+ CHECK_ERROR(__ret < 0, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ strerror(-__ret)); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_SUCCESS(message, message_return_code, error_label) \
+ REPLY_MESSAGE(message, message_return_code, error_label)
+
+#define REPLY_MESSAGE_RETURN(message, message_return_code, ...) \
+ do { \
+ int __ret = message.Reply(message_return_code); \
+ ALOGE_IF(__ret < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ strerror(-__ret)); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define REPLY_SUCCESS_RETURN(message, message_return_code, ...) \
+ REPLY_MESSAGE_RETURN(message, message_return_code, __VA_ARGS__)
+
+#define REPLY_FD(message, push_fd, error_label) \
+ do { \
+ int __ret = message.ReplyFileDescriptor(push_fd); \
+ CHECK_ERROR(__ret < 0, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ strerror(-__ret)); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_FD_RETURN(message, push_fd, ...) \
+ do { \
+ int __ret = message.ReplyFileDescriptor(push_fd); \
+ ALOGE_IF(__ret < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ strerror(-__ret)); \
+ return __VA_ARGS__; \
+ } while (0)
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_SERVICE_H_
diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h
new file mode 100644
index 0000000..c5e342a
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h
@@ -0,0 +1,79 @@
+#ifndef ANDROID_PDX_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_SERVICE_DISPATCHER_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+/*
+ * ServiceDispatcher manages a list of Service instances and handles message
+ * reception and dispatch to the services. This makes repetitive dispatch tasks
+ * easier to implement.
+ */
+class ServiceDispatcher {
+ public:
+ virtual ~ServiceDispatcher() = default;
+
+ /*
+ * Adds a service to the list of services handled by this dispatcher. This
+ * will fail if any threads are blocked waiting for messages in this
+ * dispatcher.
+ *
+ * Returns 0 on success; -EEXIST if the service was already added.
+ */
+ virtual int AddService(const std::shared_ptr<Service>& service) = 0;
+
+ /*
+ * Removes a service from this dispatcher. This will fail if any threads are
+ * blocked waiting for messages in this dispatcher.
+ *
+ * Returns 0 on success; -ENOENT if the service was not previously added;
+ * -EBUSY if there are threads in the dispatcher.
+ */
+ virtual int RemoveService(const std::shared_ptr<Service>& service) = 0;
+
+ /*
+ * Receive and dispatch one set of messages. Multiple threads may enter this
+ * method to create an implicit thread pool, as described for
+ * enterDispatchLoop() below, however this method exits after one dispatch
+ * cycle, requiring an external loop. This is useful when other work needs
+ * to be done in the service dispatch loop.
+ */
+ virtual int ReceiveAndDispatch() = 0;
+
+ /*
+ * Same as above with timeout in milliseconds. A negative value means
+ * infinite timeout, while a value of 0 means return immediately if no
+ * messages are available to receive.
+ */
+ virtual int ReceiveAndDispatch(int timeout) = 0;
+
+ /*
+ * Receive and dispatch messages until canceled. When more than one thread
+ * enters this method it creates an implicit thread pool to dispatch messages.
+ * Explicit thread pools may be created by using a single dispatch thread that
+ * hands Message instances (via move assignment) over to a queue of threads
+ * (or perhaps one of several) to handle.
+ */
+ virtual int EnterDispatchLoop() = 0;
+
+ /*
+ * Sets the canceled state of the dispatcher. When canceled is true, any
+ * threads blocked waiting for messages will return. This method waits until
+ * all dispatch threads have exited the dispatcher.
+ */
+ virtual void SetCanceled(bool cancel) = 0;
+
+ /*
+ * Gets the canceled state of the dispatcher.
+ */
+ virtual bool IsCanceled() const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h
new file mode 100644
index 0000000..613be7c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_endpoint.h
@@ -0,0 +1,149 @@
+#ifndef ANDROID_PDX_ENDPOINT_H_
+#define ANDROID_PDX_ENDPOINT_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class Service;
+class Channel;
+class Message;
+
+struct MessageInfo {
+ int pid{0};
+ int tid{0};
+ int cid{0};
+ int mid{0};
+ int euid{0};
+ int egid{0};
+ int32_t op{0};
+ uint32_t flags{0};
+ Service* service{nullptr};
+ Channel* channel{nullptr};
+ size_t send_len{0};
+ size_t recv_len{0};
+ size_t fd_count{0};
+ uint64_t impulse[4] = {};
+};
+
+// Wrapper around transport endpoint. Abstracts the underlying transport APIs in
+// a way, that the underlying IPC can be substituted for another technology
+// without changing the Service, Client and Message classes of this library.
+class Endpoint {
+ public:
+ virtual ~Endpoint() = default;
+
+ // Returns a tag that uniquely identifies a specific underlying IPC transport.
+ virtual uint32_t GetIpcTag() const = 0;
+
+ // Associates a Service instance with an endpoint by setting the service
+ // context pointer to the address of the Service. Only one Service may be
+ // associated with a given endpoint.
+ virtual int SetService(Service* service) = 0;
+
+ // Set the channel context for the given channel.
+ virtual int SetChannel(int channel_id, Channel* channel) = 0;
+
+ // Close a channel, signaling the client file object and freeing the channel
+ // id. Once closed, the client side of the channel always returns the error
+ // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ virtual int CloseChannel(int channel_id) = 0;
+
+ // Update the event bits for the given channel (given by id), using the
+ // given clear and set masks.
+ virtual int ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) = 0;
+
+ // Create a new channel and push it as a file descriptor to the process
+ // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ // sending process may change these later using fcntl()). The internal Channel
+ // instance associated with this channel is set to |channel|, which may be
+ // nullptr. The new channel id allocated for this channel is returned in
+ // |channel_id|, which may also be nullptr if not needed.
+ virtual Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel,
+ int* channel_id) = 0;
+
+ // Check whether the |ref| is a reference to a channel to the service
+ // represented by the |endpoint|. If the channel reference in question is
+ // valid, the Channel object is returned in |channel| when non-nullptr and
+ // the channel ID is returned through the Status object.
+ virtual Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) = 0;
+
+ // The default message handler. It is important that all messages
+ // (eventually) get a reply. This method should be called for any unrecognized
+ // opcodes or otherwise unhandled messages to prevent erroneous requests from
+ // blocking indefinitely.
+ virtual int DefaultHandleMessage(const MessageInfo& info) = 0;
+
+ // Receives a message on the given endpoint file descriptor.
+ virtual int MessageReceive(Message* message) = 0;
+
+ // Replies to the message with a return code.
+ virtual int MessageReply(Message* message, int return_code) = 0;
+
+ // Replies to the message with a file descriptor.
+ virtual int MessageReplyFd(Message* message, unsigned int push_fd) = 0;
+
+ // Replies to the message with a local channel handle.
+ virtual int MessageReplyChannelHandle(Message* message,
+ const LocalChannelHandle& handle) = 0;
+
+ // Replies to the message with a borrowed local channel handle.
+ virtual int MessageReplyChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) = 0;
+
+ // Replies to the message with a remote channel handle.
+ virtual int MessageReplyChannelHandle(Message* message,
+ const RemoteChannelHandle& handle) = 0;
+
+ // Reads message data into an array of memory buffers.
+ virtual ssize_t ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) = 0;
+
+ // Sends reply data for message.
+ virtual ssize_t WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) = 0;
+
+ // Records a file descriptor into the message buffer and returns the remapped
+ // reference to be sent to the remote process.
+ virtual FileReference PushFileHandle(Message* message,
+ const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(Message* message,
+ const BorrowedHandle& handle) = 0;
+ virtual FileReference PushFileHandle(Message* message,
+ const RemoteHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ Message* message, const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) = 0;
+
+ // Obtains a file descriptor/channel handle from a message for the given
+ // reference.
+ virtual LocalHandle GetFileHandle(Message* message,
+ FileReference ref) const = 0;
+ virtual LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const = 0;
+
+ // Transport-specific message state management.
+ virtual void* AllocateMessageState() = 0;
+ virtual void FreeMessageState(void* state) = 0;
+
+ // Cancels the endpoint, unblocking any receiver threads waiting for a
+ // message.
+ virtual int Cancel() = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/status.h b/libs/vr/libpdx/private/pdx/status.h
new file mode 100644
index 0000000..ca2832c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/status.h
@@ -0,0 +1,168 @@
+#ifndef ANDROID_PDX_STATUS_H_
+#define ANDROID_PDX_STATUS_H_
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+namespace android {
+namespace pdx {
+
+// This is a helper class for constructing Status<T> with an error code.
+struct ErrorStatus {
+ public:
+ ErrorStatus(int error) : error_{error} {}
+ int error() const { return error_; }
+
+ static std::string ErrorToString(int error_code);
+
+ private:
+ int error_;
+};
+
+// Status<T> is a container class that can be used to return a value of type T
+// or error code to the caller.
+template <typename T>
+class Status {
+ public:
+ // Default constructor so an empty Status object can be created.
+ Status() : error_{-1} {}
+
+ // Value copy/move constructors. These are intentionally not marked as
+ // explicit to allow direct value returns from functions without having
+ // to explicitly wrap them into Status<T>().
+ Status(const T& value) : value_{value} {} // NOLINT(runtime/explicit)
+ Status(T&& value) : value_{std::move(value)} {} // NOLINT(runtime/explicit)
+
+ // Constructor for storing an error code inside the Status object.
+ Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit)
+ : error_{error_status.error()} {}
+
+ // Copy/move constructors. Move constructor leaves |other| object in empty
+ // state.
+ Status(const Status& other) = default;
+ Status(Status&& other)
+ : value_{std::move(other.value_)}, error_{other.error_} {
+ other.error_ = -1;
+ }
+
+ // Assignment operators.
+ Status& operator=(const Status& other) = default;
+ Status& operator=(Status&& other) {
+ error_ = other.error_;
+ value_ = std::move(other.value_);
+ other.error_ = -1;
+ T empty;
+ std::swap(other.value_, empty);
+ return *this;
+ }
+
+ // Change the value/error code of the status object directly.
+ void SetValue(T value) {
+ error_ = 0;
+ value_ = std::move(value);
+ }
+ void SetError(int error) {
+ error_ = error;
+ T empty;
+ std::swap(value_, empty);
+ }
+
+ // If |other| is in error state, copy the error code to this object.
+ // Returns true if error was propagated
+ template<typename U>
+ bool PropagateError(const Status<U>& other) {
+ if (!other.ok() && !other.empty()) {
+ SetError(other.error());
+ return true;
+ }
+ return false;
+ }
+
+ // Returns true if the status object contains valid value for type T.
+ // This means, the object is not empty and does not contain an error code.
+ bool ok() const { return error_ == 0; }
+
+ // Checks if the object is empty (doesn't contain a valid value nor an error).
+ bool empty() const { return error_ < 0; }
+
+ // Explicit bool conversion, equivalent to invoking ok().
+ explicit operator bool() const { return ok(); }
+
+ // Accessors for the value stored in Status. Calling when ok() is false leads
+ // to undefined behavior.
+ const T& get() const { return value_; }
+ T&& take() {
+ error_ = -1;
+ return std::move(value_);
+ }
+
+ // Returns the error code stored in the object. These codes are positive
+ // non-zero values.
+ // Can be called only when an error is actually stored (that is, the object
+ // is not empty nor containing a valid value).
+ int error() const { return std::max(error_, 0); }
+
+ // Returns the error message associated with error code stored in the object.
+ // The message is the same as the string returned by strerror(status.error()).
+ // Can be called only when an error is actually stored (that is, the object
+ // is not empty nor containing a valid value).
+ std::string GetErrorMessage() const {
+ std::string message;
+ if (error_ > 0)
+ message = ErrorStatus::ErrorToString(error_);
+ return message;
+ }
+
+ private:
+ T value_{};
+ int error_{0};
+};
+
+// Specialization for status containing no other value but the error code.
+template <>
+class Status<void> {
+ public:
+ Status() = default;
+ Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit)
+ : error_{error_status.error()} {}
+ void SetValue() { error_ = 0; }
+ void SetError(int error) { error_ = error; }
+
+ template<typename U>
+ bool PropagateError(const Status<U>& other) {
+ if (!other.ok() && !other.empty()) {
+ SetError(other.error());
+ return true;
+ }
+ return false;
+ }
+
+ bool ok() const { return error_ == 0; }
+ bool empty() const { return false; }
+ explicit operator bool() const { return ok(); }
+ int error() const { return std::max(error_, 0); }
+ std::string GetErrorMessage() const {
+ std::string message;
+ if (error_ > 0)
+ message = ErrorStatus::ErrorToString(error_);
+ return message;
+ }
+
+ private:
+ int error_{0};
+};
+
+// TODO(avakulenko): Remove these function once all callers of it are gone.
+inline int ReturnStatusOrError(const Status<void>& status) {
+ return status ? 0 : -status.error();
+}
+
+inline int ReturnStatusOrError(const Status<int>& status) {
+ return status ? status.get() : -status.error();
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_STATUS_H_
diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h
new file mode 100644
index 0000000..ebe8491
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/trace.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_PDX_TRACE_H_
+#define ANDROID_PDX_TRACE_H_
+
+// Tracing utilities for libpdx. Tracing in the service framework is enabled
+// under these conditions:
+// 1. ATRACE_TAG is defined, AND
+// 2. ATRACE_TAG does not equal ATRACE_TAG_NEVER, AND
+// 3. PDX_TRACE_ENABLED is defined, AND
+// 4. PDX_TRACE_ENABLED is equal to logical true.
+//
+// If any of these conditions are not met tracing is completely removed from the
+// library and headers.
+
+// If ATRACE_TAG is not defined, default to never.
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_NEVER
+#endif
+
+// Include tracing functions after the trace tag is defined.
+#include <utils/Trace.h>
+
+// If PDX_TRACE_ENABLED is not defined, default to off.
+#ifndef PDX_TRACE_ENABLED
+#define PDX_TRACE_ENABLED 0
+#endif
+
+#if (ATRACE_TAG) != (ATRACE_TAG_NEVER) && (PDX_TRACE_ENABLED)
+#define PDX_TRACE_NAME ATRACE_NAME
+#else
+#define PDX_TRACE_NAME(name) \
+ do { \
+ } while (0)
+#endif
+
+#endif // ANDROID_PDX_TRACE_H_
diff --git a/libs/vr/libpdx/private/pdx/utility.h b/libs/vr/libpdx/private/pdx/utility.h
new file mode 100644
index 0000000..c8c717c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/utility.h
@@ -0,0 +1,367 @@
+#ifndef ANDROID_PDX_UTILITY_H_
+#define ANDROID_PDX_UTILITY_H_
+
+#include <cstdint>
+#include <iterator>
+
+#include <pdx/rpc/sequence.h>
+
+// Utilities for testing object serialization.
+
+namespace android {
+namespace pdx {
+
+class ByteBuffer {
+ public:
+ using iterator = uint8_t*;
+ using const_iterator = const uint8_t*;
+ using size_type = size_t;
+
+ ByteBuffer() = default;
+ ByteBuffer(const ByteBuffer& other) {
+ resize(other.size());
+ if (other.size())
+ memcpy(data_, other.data(), other.size());
+ }
+
+ ByteBuffer& operator=(const ByteBuffer& other) {
+ resize(other.size());
+ if (other.size())
+ memcpy(data_, other.data(), other.size());
+ return *this;
+ }
+
+ ByteBuffer& operator=(ByteBuffer&& other) {
+ std::swap(data_, other.data_);
+ std::swap(size_, other.size_);
+ std::swap(capacity_, other.capacity_);
+ other.clear();
+ return *this;
+ }
+
+ inline const uint8_t* data() const { return data_; }
+ inline uint8_t* data() { return data_; }
+ inline size_t size() const { return size_; }
+ inline size_t capacity() const { return capacity_; }
+
+ iterator begin() { return data_; }
+ const_iterator begin() const { return data_; }
+ iterator end() { return data_ + size_; }
+ const_iterator end() const { return data_ + size_; }
+
+ inline bool operator==(const ByteBuffer& other) const {
+ return size_ == other.size_ &&
+ (size_ == 0 || memcmp(data_, other.data_, size_) == 0);
+ }
+
+ inline bool operator!=(const ByteBuffer& other) const {
+ return !operator==(other);
+ }
+
+ inline void reserve(size_t size) {
+ if (size <= capacity_)
+ return;
+ // Find next power of 2 (assuming the size is 32 bits for now).
+ size--;
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size |= size >> 16;
+ size++;
+ void* new_data = data_ ? realloc(data_, size) : malloc(size);
+ // TODO(avakulenko): Check for allocation failures.
+ data_ = static_cast<uint8_t*>(new_data);
+ capacity_ = size;
+ }
+
+ inline void resize(size_t size) {
+ reserve(size);
+ size_ = size;
+ }
+
+ inline uint8_t* grow_by(size_t size_delta) {
+ size_t old_size = size_;
+ resize(old_size + size_delta);
+ return data_ + old_size;
+ }
+
+ inline void clear() { size_ = 0; }
+
+ private:
+ uint8_t* data_{nullptr};
+ size_t size_{0};
+ size_t capacity_{0};
+};
+
+// Utility functions to increment/decrement void pointers to data buffers.
+template <typename OFFSET_T>
+inline const void* AdvancePointer(const void* ptr, OFFSET_T offset) {
+ return static_cast<const uint8_t*>(ptr) + offset;
+}
+
+template <typename OFFSET_T>
+inline void* AdvancePointer(void* ptr, OFFSET_T offset) {
+ return static_cast<uint8_t*>(ptr) + offset;
+}
+
+inline ptrdiff_t PointerDistance(const void* end, const void* begin) {
+ return static_cast<const uint8_t*>(end) - static_cast<const uint8_t*>(begin);
+}
+
+// Utility to build sequences of types.
+template <typename, typename>
+struct AppendTypeSequence;
+
+template <typename T, typename... S, template <typename...> class TT>
+struct AppendTypeSequence<T, TT<S...>> {
+ using type = TT<S..., T>;
+};
+
+// Utility to generate repeated types.
+template <typename T, std::size_t N, template <typename...> class TT>
+struct RepeatedType {
+ using type = typename AppendTypeSequence<
+ T, typename RepeatedType<T, N - 1, TT>::type>::type;
+};
+
+template <typename T, template <typename...> class TT>
+struct RepeatedType<T, 0, TT> {
+ using type = TT<>;
+};
+
+template <typename V, typename S>
+inline V ReturnValueHelper(V value, S /*ignore*/) {
+ return value;
+}
+
+template <typename R, typename V, size_t... S>
+inline R GetNTupleHelper(V value, rpc::IndexSequence<S...>) {
+ return std::make_tuple(ReturnValueHelper(value, S)...);
+}
+
+// Returns an N-tuple of type std::tuple<T,...T> containing |value| in each
+// element.
+template <size_t N, typename T,
+ typename R = typename RepeatedType<T, N, std::tuple>::type>
+inline R GetNTuple(T value) {
+ return GetNTupleHelper<R>(value, rpc::MakeIndexSequence<N>{});
+}
+
+class NoOpOutputResourceMapper : public OutputResourceMapper {
+ public:
+ FileReference PushFileHandle(const LocalHandle& handle) override {
+ return handle.Get();
+ }
+
+ FileReference PushFileHandle(const BorrowedHandle& handle) override {
+ return handle.Get();
+ }
+
+ FileReference PushFileHandle(const RemoteHandle& handle) override {
+ return handle.Get();
+ }
+
+ ChannelReference PushChannelHandle(
+ const LocalChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override {
+ return handle.value();
+ }
+};
+
+class NoOpInputResourceMapper : public InputResourceMapper {
+ public:
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override {
+ *handle = LocalHandle{ref};
+ return true;
+ }
+
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override {
+ *handle = LocalChannelHandle{nullptr, ref};
+ return true;
+ }
+};
+
+class NoOpResourceMapper : public NoOpOutputResourceMapper,
+ public NoOpInputResourceMapper {};
+
+// Simple implementation of the payload interface, required by
+// Serialize/Deserialize. This is intended for test cases, where compatibility
+// with std::vector is helpful.
+class Payload : public MessageWriter,
+ public MessageReader,
+ public OutputResourceMapper {
+ public:
+ using BaseType = ByteBuffer;
+ using iterator = typename BaseType::iterator;
+ using const_iterator = typename BaseType::const_iterator;
+ using size_type = typename BaseType::size_type;
+
+ Payload() = default;
+ explicit Payload(size_type count, uint8_t value = 0) { Append(count, value); }
+ Payload(const Payload& other) : buffer_(other.buffer_) {}
+ Payload(const std::initializer_list<uint8_t>& initializer) {
+ buffer_.resize(initializer.size());
+ std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+ }
+
+ Payload& operator=(const Payload& other) {
+ buffer_ = other.buffer_;
+ read_pos_ = 0;
+ return *this;
+ }
+ Payload& operator=(const std::initializer_list<uint8_t>& initializer) {
+ buffer_.resize(initializer.size());
+ std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+ read_pos_ = 0;
+ return *this;
+ }
+
+ // Compares Payload with Payload.
+ bool operator==(const Payload& other) const {
+ return buffer_ == other.buffer_;
+ }
+ bool operator!=(const Payload& other) const {
+ return buffer_ != other.buffer_;
+ }
+
+ // Compares Payload with std::vector.
+ template <typename Type, typename AllocatorType>
+ typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+ operator==(const std::vector<Type, AllocatorType>& other) const {
+ return buffer_.size() == other.size() &&
+ memcmp(buffer_.data(), other.data(), other.size()) == 0;
+ }
+ template <typename Type, typename AllocatorType>
+ typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+ operator!=(const std::vector<Type, AllocatorType>& other) const {
+ return !operator!=(other);
+ }
+
+ iterator begin() { return buffer_.begin(); }
+ const_iterator begin() const { return buffer_.begin(); }
+ iterator end() { return buffer_.end(); }
+ const_iterator end() const { return buffer_.end(); }
+
+ void Append(size_type count, uint8_t value) {
+ auto* data = buffer_.grow_by(count);
+ std::fill(data, data + count, value);
+ }
+
+ void Clear() {
+ buffer_.clear();
+ file_handles_.clear();
+ read_pos_ = 0;
+ }
+
+ void Rewind() { read_pos_ = 0; }
+
+ uint8_t* Data() { return buffer_.data(); }
+ const uint8_t* Data() const { return buffer_.data(); }
+ size_type Size() const { return buffer_.size(); }
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ return buffer_.grow_by(size);
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+ }
+
+ FileReference PushFileHandle(const BorrowedHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+ }
+
+ FileReference PushFileHandle(const RemoteHandle& handle) override {
+ return handle.Get();
+ }
+
+ ChannelReference PushChannelHandle(
+ const LocalChannelHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.value());
+ return ref;
+ } else {
+ return handle.value();
+ }
+ }
+
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.value());
+ return ref;
+ } else {
+ return handle.value();
+ }
+ }
+
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {buffer_.data() + read_pos_, &*buffer_.end()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ read_pos_ = PointerDistance(new_start, buffer_.data());
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override {
+ return &input_resource_mapper_;
+ }
+
+ const int* FdArray() const { return file_handles_.data(); }
+ std::size_t FdCount() const { return file_handles_.size(); }
+
+ private:
+ NoOpInputResourceMapper input_resource_mapper_;
+ ByteBuffer buffer_;
+ std::vector<int> file_handles_;
+ size_t read_pos_{0};
+};
+
+} // namespace pdx
+} // namespace android
+
+// Helper macros for branch prediction hinting.
+#ifdef __GNUC__
+#define PDX_LIKELY(x) __builtin_expect(!!(x), true)
+#define PDX_UNLIKELY(x) __builtin_expect(!!(x), false)
+#else
+#define PDX_LIKELY(x) (x)
+#define PDX_UNLIKELY(x) (x)
+#endif
+
+#endif // ANDROID_PDX_UTILITY_H_
diff --git a/libs/vr/libpdx/serialization_tests.cpp b/libs/vr/libpdx/serialization_tests.cpp
new file mode 100644
index 0000000..5ad1047
--- /dev/null
+++ b/libs/vr/libpdx/serialization_tests.cpp
@@ -0,0 +1,2505 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+// Tests the serialization/deserialization of all supported types, verifying all
+// reasonable boundary conditions for types with multiple encodings.
+//
+// NOTE: Sometimes this file uses the construct "var = decltype(var)({...})"
+// instead of the equivalent "var = {...}" to construct vectors. This is to
+// prevent clang-format from producing annoyingly vertical code from long
+// initializers.
+
+// TODO(eieio): Automatically generate some of these tests?
+
+namespace {
+
+// Test data for serialization/deserialization of floats.
+const float kZeroFloat = 0.0f;
+const float kOneFloat = 1.0f;
+const auto kZeroFloatBytes = reinterpret_cast<const std::uint8_t*>(&kZeroFloat);
+const auto kOneFloatBytes = reinterpret_cast<const std::uint8_t*>(&kOneFloat);
+const double kZeroDouble = 0.0;
+const double kOneDouble = 1.0;
+const auto kZeroDoubleBytes =
+ reinterpret_cast<const std::uint8_t*>(&kZeroDouble);
+const auto kOneDoubleBytes = reinterpret_cast<const std::uint8_t*>(&kOneDouble);
+
+struct TestType {
+ enum class Foo { kFoo, kBar, kBaz };
+
+ int a;
+ float b;
+ std::string c;
+ Foo d;
+
+ TestType() {}
+ TestType(int a, float b, const std::string& c, Foo d)
+ : a(a), b(b), c(c), d(d) {}
+
+ // Make gtest expressions simpler by defining equality operator. This is not
+ // needed for serialization.
+ bool operator==(const TestType& other) const {
+ return a == other.a && b == other.b && c == other.c && d == other.d;
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c, d);
+};
+
+template <typename FileHandleType>
+struct TestTemplateType {
+ FileHandleType fd;
+
+ TestTemplateType() {}
+ TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+ bool operator==(const TestTemplateType& other) const {
+ return fd.Get() == other.fd.Get();
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+// Utilities to generate test maps and payloads.
+template <typename MapType>
+MapType MakeMap(std::size_t size) {
+ MapType result;
+ for (std::size_t i = 0; i < size; i++) {
+ result.emplace(i, i);
+ }
+ return result;
+}
+
+template <typename MapType>
+void InsertKeyValue(MessageWriter* writer, std::size_t size) {
+ MapType map;
+ for (std::size_t i = 0; i < size; i++) {
+ map.emplace(i, i);
+ }
+ for (const auto& element : map) {
+ Serialize(element.first, writer);
+ Serialize(element.second, writer);
+ }
+}
+
+} // anonymous namespace
+
+TEST(SerializableTypes, Constructor) {
+ TestType tt(1, 2.0, "three", TestType::Foo::kBar);
+ EXPECT_EQ(1, tt.a);
+ EXPECT_EQ(2.0, tt.b);
+ EXPECT_EQ("three", tt.c);
+ EXPECT_EQ(TestType::Foo::kBar, tt.d);
+}
+
+TEST(SerializationTest, bool) {
+ Payload result;
+ Payload expected;
+ bool value;
+
+ // True.
+ value = true;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_TRUE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // False.
+ value = false;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FALSE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint8_t) {
+ Payload result;
+ Payload expected;
+ uint8_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint16_t) {
+ Payload result;
+ Payload expected;
+ uint16_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint32_t) {
+ Payload result;
+ Payload expected;
+ uint32_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT32.
+ value = (1 << 16);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT32.
+ value = 0xffffffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint64_t) {
+ Payload result;
+ Payload expected;
+ uint64_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT32.
+ value = (1 << 16);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT32.
+ value = 0xffffffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT64.
+ value = (1ULL << 32);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT64, 0, 0, 0, 0, 1, 0, 0, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT64.
+ value = 0xffffffffffffffffULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int8_t) {
+ Payload result;
+ Payload expected;
+ int8_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int16_t) {
+ Payload result;
+ Payload expected;
+ int16_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int32_t) {
+ Payload result;
+ Payload expected;
+ int32_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT32.
+ value = -2147483648;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT32.
+ value = 2147483647;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int64_t) {
+ Payload result;
+ Payload expected;
+ int64_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT32.
+ value = -2147483648;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT32.
+ value = 2147483647;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT64.
+ value = -9223372036854775808ULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT64.
+ value = 9223372036854775807ULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, float) {
+ Payload result;
+ Payload expected;
+ float value;
+
+ value = 0.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ value = 1.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, double) {
+ Payload result;
+ Payload expected;
+ double value;
+
+ value = 0.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+ kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+ kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ value = 1.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+ kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4],
+ kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, Enum) {
+ Payload result;
+ Payload expected;
+
+ enum Foo { kFoo, kBar, kBaz };
+ Foo value = kBar;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, EnumClass) {
+ Payload result;
+ Payload expected;
+
+ enum class Foo { kFoo, kBar, kBaz };
+ Foo value = Foo::kBaz;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, LocalHandle) {
+ Payload result;
+ Payload expected;
+ LocalHandle fd1;
+ LocalHandle fd2;
+
+ fd1 = LocalHandle(100);
+ Serialize(fd1, &result);
+ expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(1u, result.FdCount());
+ EXPECT_EQ(100, result.FdArray()[0]);
+ result.Clear();
+
+ fd2 = LocalHandle(200);
+ Serialize(std::forward_as_tuple(fd1, fd2), &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(2u, result.FdCount());
+ EXPECT_EQ(100, result.FdArray()[0]);
+ EXPECT_EQ(200, result.FdArray()[1]);
+ result.Clear();
+
+ fd1.Release(); // Don't try to close fd 100.
+ fd2.Release(); // Don't try to close fd 200.
+
+ fd1 = LocalHandle(-2);
+ Serialize(fd1, &result);
+ expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+ 0xff};
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(0u, result.FdCount());
+ result.Clear();
+}
+
+TEST(SerializationTest, string) {
+ Payload result;
+ Payload expected;
+ std::string value;
+
+ // Min FIXSTR.
+ value = "";
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXSTR_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXSTR.
+ value = std::string((1 << 5) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXSTR_MAX};
+ expected.Append((1 << 5) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR8.
+ value = std::string((1 << 5), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 5)};
+ expected.Append((1 << 5), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR8.
+ value = std::string((1 << 8) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+ expected.Append((1 << 8) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR16.
+ value = std::string((1 << 8), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+ expected.Append((1 << 8), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR16.
+ value = std::string((1 << 16) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR32.
+ value = std::string((1 << 16), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, StringWrapper) {
+ Payload result;
+ Payload expected;
+ std::string value;
+
+ // Min FIXSTR.
+ value = "";
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_FIXSTR_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXSTR.
+ value = std::string((1 << 5) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_FIXSTR_MAX};
+ expected.Append((1 << 5) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR8.
+ value = std::string((1 << 5), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 5)};
+ expected.Append((1 << 5), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR8.
+ value = std::string((1 << 8) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+ expected.Append((1 << 8) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR16.
+ value = std::string((1 << 8), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+ expected.Append((1 << 8), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR16.
+ value = std::string((1 << 16) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR32.
+ value = std::string((1 << 16), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, vector) {
+ Payload result;
+ Payload expected;
+ std::vector<uint8_t> value;
+
+ // Min FIXARRAY.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ value = decltype(value)((1 << 4) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ value = decltype(value)((1 << 4), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ value = decltype(value)((1 << 16) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ value = decltype(value)((1 << 16), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, map) {
+ Payload result;
+ Payload expected;
+ std::map<std::uint32_t, std::uint32_t> value;
+
+ // Min FIXMAP.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXMAP.
+ value = MakeMap<decltype(value)>((1 << 4) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP16.
+ value = MakeMap<decltype(value)>((1 << 4));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max MAP16.
+ value = MakeMap<decltype(value)>((1 << 16) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP32.
+ value = MakeMap<decltype(value)>((1 << 16));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, unordered_map) {
+ Payload result;
+ Payload expected;
+ std::unordered_map<std::uint32_t, std::uint32_t> value;
+
+ // Min FIXMAP.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXMAP.
+ value = MakeMap<decltype(value)>((1 << 4) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP16.
+ value = MakeMap<decltype(value)>((1 << 4));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max MAP16.
+ value = MakeMap<decltype(value)>((1 << 16) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP32.
+ value = MakeMap<decltype(value)>((1 << 16));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, array) {
+ Payload result;
+ Payload expected;
+
+ // Min FIXARRAY.
+ std::array<std::uint8_t, 0> a0;
+ Serialize(a0, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ std::array<std::uint8_t, (1 << 4) - 1> a1;
+ for (auto& element : a1)
+ element = 'x';
+ Serialize(a1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ std::array<std::uint8_t, (1 << 4)> a2;
+ for (auto& element : a2)
+ element = 'x';
+ Serialize(a2, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ std::array<std::uint8_t, (1 << 16) - 1> a3;
+ for (auto& element : a3)
+ element = 'x';
+ Serialize(a3, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ std::array<std::uint8_t, (1 << 16)> a4;
+ for (auto& element : a4)
+ element = 'x';
+ Serialize(a4, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, ArrayWrapper) {
+ Payload result;
+ Payload expected;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> value;
+ ArrayWrapper<std::uint8_t> wrapper;
+
+ // Min FIXARRAY.
+ value = {};
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ value = decltype(value)((1 << 4) - 1, 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ value = decltype(value)((1 << 4), 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ value = decltype(value)((1 << 16) - 1, 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ value = decltype(value)((1 << 16), 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, pair) {
+ Payload result;
+ Payload expected;
+
+ auto p1 = std::make_pair(1, 2);
+ Serialize(p1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto p2 = std::make_pair('x', std::string("12345"));
+ Serialize(p2, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+ ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3',
+ '4', '5'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, tuple) {
+ Payload result;
+ Payload expected;
+
+ // Min FIXARRAY.
+ auto t1 = std::make_tuple();
+ Serialize(t1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ auto t2 = GetNTuple<15>('x');
+ Serialize(t2, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ auto t3 = GetNTuple<(1 << 4)>('x');
+ Serialize(t3, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+// Template instantiation depth is an issue for these tests. They are commented
+// out to document the expected behavior, even though tuples of this order are
+// not expected in practice.
+#if 0
+ // Max ARRAY16.
+ auto t4 = GetNTuple<(1 << 16)-1>('x');
+ Serialize(t4, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16)-1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ auto t5 = GetNTuple<(1 << 16)>('x');
+ Serialize(t5, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+#endif
+}
+
+// TODO(eieio): More exhaustive testing of type nesting.
+TEST(SerializationTest, NestedTuple) {
+ Payload result;
+ Payload expected;
+
+ auto t1 = std::make_tuple('x', std::make_tuple<int, int>(1, 2));
+ Serialize(t1, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+ ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto t2 = std::make_tuple('x', std::make_tuple<int, int>(1, 2),
+ std::string("0123456789"));
+ Serialize(t2, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 3, 'x',
+ ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2,
+ ENCODING_TYPE_FIXSTR | 10, '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto t3 = std::make_tuple(0.0f, std::uint64_t(10ULL),
+ std::vector<char>{'a', 'b', 'c'});
+ Serialize(t3, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 3, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10,
+ ENCODING_TYPE_FIXARRAY_MIN + 3, 'a', 'b', 'c'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, NestedMap) {
+ Payload result;
+ Payload expected;
+
+ std::map<int, std::pair<std::string, int>> m1 = {{0, {"a", 2}},
+ {1, {"b", 10}}};
+ Serialize(m1, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXMAP_MIN + 2, 0, ENCODING_TYPE_FIXARRAY_MIN + 2,
+ ENCODING_TYPE_FIXSTR_MIN + 1, 'a', 2, 1, ENCODING_TYPE_FIXARRAY_MIN + 2,
+ ENCODING_TYPE_FIXSTR_MIN + 1, 'b', 10});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, Serializable) {
+ Payload result;
+ Payload expected;
+
+ TestType t1{10, 0.0, "12345", TestType::Foo::kBaz};
+ Serialize(t1, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+ '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ TestTemplateType<LocalHandle> tt{LocalHandle(-1)};
+ Serialize(tt, &result);
+ expected =
+ decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+ EXPECT_EQ(expected, result);
+}
+
+TEST(SerializationTest, Variant) {
+ Payload result;
+ Payload expected;
+
+ Variant<int, bool, float> v;
+
+ // Empty variant.
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+ ENCODING_TYPE_NIL};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = 10;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = true;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_TRUE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = false;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_FALSE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = 1.0f;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+ ENCODING_TYPE_FLOAT32,
+ kOneFloatBytes[0],
+ kOneFloatBytes[1],
+ kOneFloatBytes[2],
+ kOneFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // TODO(eieio): Add more serialization tests for Variant.
+}
+
+TEST(DeserializationTest, bool) {
+ Payload buffer;
+ bool result = false;
+ ErrorType error;
+
+ // True.
+ buffer = {ENCODING_TYPE_TRUE};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(1, result); // Gtest generates warning from bool literals.
+
+ // False.
+ buffer = {ENCODING_TYPE_FALSE};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result); // Gtest generates warning from bool literals.
+}
+
+TEST(DeserializationTest, uint8_t) {
+ Payload buffer;
+ std::uint8_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // UINT16 out of range.
+ buffer = {ENCODING_TYPE_UINT16};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT16, error.encoding_type());
+
+ // UINT32 out of range.
+ buffer = {ENCODING_TYPE_UINT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint16_t) {
+ Payload buffer;
+ std::uint16_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // UINT32 out of range.
+ buffer = {ENCODING_TYPE_UINT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint32_t) {
+ Payload buffer;
+ std::uint32_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // Min UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffU, result);
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint64_t) {
+ Payload buffer;
+ std::uint64_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // Min UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffU, result);
+
+ // Min UINT64.
+ buffer = {
+ ENCODING_TYPE_UINT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT64.
+ buffer = {
+ ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffffffffffUL, result);
+}
+
+TEST(DeserializationTest, int8_t) {
+ Payload buffer;
+ std::int8_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // INT16 out of range.
+ buffer = {ENCODING_TYPE_INT16};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT16, error.encoding_type());
+
+ // INT32 out of range.
+ buffer = {ENCODING_TYPE_INT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int16_t) {
+ Payload buffer;
+ std::int16_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // INT32 out of range.
+ buffer = {ENCODING_TYPE_INT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int32_t) {
+ Payload buffer;
+ std::int32_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // Min INT32.
+ buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2147483648, result);
+
+ // Max INT32.
+ buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(2147483647, result);
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int64_t) {
+ Payload buffer;
+ std::int64_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // Min INT32.
+ buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2147483648, result);
+
+ // Max INT32.
+ buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(2147483647, result);
+
+ // Min INT64.
+ buffer = {
+ ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ // Believe it or not, this is actually the correct way to specify the most
+ // negative signed long long.
+ EXPECT_EQ(-9223372036854775807LL - 1, result);
+
+ // Max INT64.
+ buffer = {
+ ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(9223372036854775807LL, result);
+}
+
+TEST(DeserializationTest, float) {
+ Payload buffer;
+ float result;
+ ErrorType error;
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroFloat, result);
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneFloat, result);
+}
+
+TEST(DeserializationTest, double) {
+ Payload buffer;
+ double result;
+ ErrorType error;
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroDouble, result);
+
+ // FLOAT64.
+ buffer = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+ kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+ kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroDouble, result);
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneDouble, result);
+
+ // FLOAT64.
+ buffer = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+ kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4],
+ kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneDouble, result);
+}
+
+TEST(DeserializationTest, Enum) {
+ Payload buffer;
+ enum Foo { kFoo, kBar, kBaz } result;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kBar, result);
+}
+
+TEST(DeserializationTest, EnumClass) {
+ Payload buffer;
+ enum Foo { kFoo, kBar, kBaz } result;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(Foo::kBaz, result);
+}
+
+TEST(DeserializationTest, LocalHandle) {
+ Payload buffer;
+ LocalHandle result1;
+ LocalHandle result2;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+ error = Deserialize(&result1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result1.Get());
+ result1.Release(); // Don't close fd 0.
+
+ std::tuple<LocalHandle&, LocalHandle&> t1(result1, result2);
+ buffer = decltype(buffer)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result1.Get());
+ EXPECT_EQ(1, result2.Get());
+ result1.Release(); // Don't close fd 0.
+ result2.Release(); // Don't close fd 1.
+
+ buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+ 0xff};
+ error = Deserialize(&result1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2, result1.Get());
+}
+
+TEST(DeserializationTest, string) {
+ Payload buffer;
+ std::string result = "";
+ ErrorType error;
+
+ // Min FIXSTR.
+ buffer = {ENCODING_TYPE_FIXSTR_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max FIXSTR.
+ buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ buffer.Append((1 << 5) - 1, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string((1 << 5) - 1, 'x'), result);
+
+ // Min STR8.
+ buffer = {ENCODING_TYPE_STR8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max STR8.
+ buffer = {ENCODING_TYPE_STR8, 0xff};
+ buffer.Append(0xff, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0xff, 'x'), result);
+
+ // Min STR16.
+ buffer = {ENCODING_TYPE_STR16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max STR16.
+ buffer = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ buffer.Append(0xffff, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0xffff, 'x'), result);
+
+ // Min STR32.
+ buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Test STR32 with max STR16 + 1 bytes. It's not practical to test max
+ // STR32.
+ buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0x10000, 'x'), result);
+}
+
+TEST(DeserializationTest, vector) {
+ Payload buffer;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ result;
+ Payload expected;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)((1 << 4) - 1, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append(0xffff, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)(0xffff, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)(0x10000, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, map) {
+ Payload buffer;
+ std::map<std::uint32_t, std::uint32_t> result;
+ std::map<std::uint32_t, std::uint32_t> expected;
+ ErrorType error;
+
+ // Min FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+ // Max FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error) << std::string(error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16));
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, unordered_map) {
+ Payload buffer;
+ std::unordered_map<std::uint32_t, std::uint32_t> result;
+ std::unordered_map<std::uint32_t, std::uint32_t> expected;
+ ErrorType error;
+
+ // Min FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+ // Max FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16));
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, array) {
+ Payload buffer;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ std::array<std::uint8_t, 0> a0;
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_DESTINATION_SIZE, error);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 'x');
+ std::array<std::uint8_t, (1 << 4) - 1> a1, expected1;
+ for (auto& element : expected1)
+ element = 'x';
+ error = Deserialize(&a1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected1, a1);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append((1 << 16) - 1, 'x');
+ std::array<std::uint8_t, (1 << 16) - 1> a3, expected3;
+ for (auto& element : expected3)
+ element = 'x';
+ error = Deserialize(&a3, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected3, a3);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append((1 << 16), 'x');
+ std::array<std::uint8_t, (1 << 16)> a4, expected4;
+ for (auto& element : expected4)
+ element = 'x';
+ error = Deserialize(&a4, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected4, a4);
+}
+
+TEST(DeserializationTest, ArrayWrapper) {
+ Payload buffer;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ result;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ expected;
+ ErrorType error;
+
+ result.reserve(0x10000);
+ ArrayWrapper<std::uint8_t> wrapper(result.data(), result.capacity());
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)((1 << 4) - 1, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append(0xffff, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)(0xffff, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)(0x10000, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, pair) {
+ Payload buffer;
+ ErrorType error;
+
+ std::pair<int, int> p1;
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+ error = Deserialize(&p1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_pair(1, 2), p1);
+}
+
+TEST(DeserializationTest, tuple) {
+ Payload buffer;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ std::tuple<> t1;
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1); // Superfluous.
+
+ // Max FIXARRAY.
+ auto t2 = GetNTuple<15, int>(0);
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&t2, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<15, int>(1)), t2);
+
+ // Min ARRAY16.
+ // Using t1 above.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1);
+
+ // ARRAY16 at Max FIXARRAY + 1
+ auto t3 = GetNTuple<(1 << 4), int>(0);
+ buffer = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ buffer.Append((1 << 4), 1);
+ error = Deserialize(&t3, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t3);
+
+ // Min ARRAY32.
+ // Using t1 from above.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1);
+
+ // ARRAY32 at Max FIXARRAY + 1
+ auto t4 = GetNTuple<(1 << 4), int>(0);
+ buffer = {ENCODING_TYPE_ARRAY32, 0x10, 0x00, 0x00, 0x00};
+ buffer.Append((1 << 4), 1);
+ error = Deserialize(&t4, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t4);
+
+ // Template instantiation depth is an issue for tuples with large numbers of
+ // elements. As these are not expected in practice, the limits of ARRAY16
+ // and ARRAY32 are not tested.
+}
+
+TEST(DeserializationTest, Serializable) {
+ Payload buffer;
+ ErrorType error;
+
+ buffer = decltype(buffer)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+ '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1});
+ TestType t1;
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(TestType(10, 0.f, "12345", TestType::Foo::kBar), t1);
+
+ buffer =
+ decltype(buffer)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+ TestTemplateType<LocalHandle> tt;
+ error = Deserialize(&tt, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(TestTemplateType<LocalHandle>(LocalHandle(-1)), tt);
+}
+
+TEST(DeserializationTest, Variant) {
+ Payload buffer;
+ ErrorType error;
+
+ Variant<int, bool, float> v;
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+ ENCODING_TYPE_NIL};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_TRUE(v.empty());
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(10, std::get<int>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+ ENCODING_TYPE_TRUE};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+ ENCODING_TYPE_FALSE};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(false, std::get<bool>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+ ENCODING_TYPE_FLOAT32,
+ kOneFloatBytes[0],
+ kOneFloatBytes[1],
+ kOneFloatBytes[2],
+ kOneFloatBytes[3]};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+
+ // TODO(eieio): Add more deserialization tests for Variant.
+}
+
+TEST(DeserializationTest, ErrorType) {
+ Payload buffer;
+ ErrorType error;
+
+ std::uint8_t u8;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u8, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint16_t u16;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u16, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint32_t u32;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u32, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint64_t u64;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u64, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int8_t i8;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i8, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int16_t i16;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i16, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int32_t i32;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i32, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int64_t i64;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i64, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::string s;
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+ error = Deserialize(&s, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_STRING, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+ std::vector<std::uint8_t> v;
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_ARRAY, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_STR8};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 0, 1};
+ std::tuple<int> t;
+ error = Deserialize(&t, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 3, 0, 1, 2};
+ std::pair<int, int> p;
+ error = Deserialize(&p, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+}
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
new file mode 100644
index 0000000..0053af8
--- /dev/null
+++ b/libs/vr/libpdx/service.cpp
@@ -0,0 +1,680 @@
+#define LOG_TAG "ServiceFramework"
+#include "pdx/service.h"
+
+#include <cutils/log.h>
+#include <fcntl.h>
+#include <utils/misc.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include <pdx/trace.h>
+#include "errno_guard.h"
+
+#define TRACE 0
+
+namespace android {
+namespace pdx {
+
+std::shared_ptr<Channel> Channel::GetFromMessageInfo(const MessageInfo& info) {
+ return info.channel ? info.channel->shared_from_this()
+ : std::shared_ptr<Channel>();
+}
+
+Message::Message() : replied_(true) {}
+
+Message::Message(const MessageInfo& info)
+ : service_{Service::GetFromMessageInfo(info)},
+ channel_{Channel::GetFromMessageInfo(info)},
+ info_{info},
+ replied_{IsImpulse()} {
+ auto svc = service_.lock();
+ if (svc)
+ state_ = svc->endpoint()->AllocateMessageState();
+}
+
+// C++11 specifies the move semantics for shared_ptr but not weak_ptr. This
+// means we have to manually implement the desired move semantics for Message.
+Message::Message(Message&& other) { *this = std::move(other); }
+
+Message& Message::operator=(Message&& other) {
+ Destroy();
+ auto base = reinterpret_cast<std::uint8_t*>(&info_);
+ std::fill(&base[0], &base[sizeof(info_)], 0);
+ replied_ = true;
+ std::swap(service_, other.service_);
+ std::swap(channel_, other.channel_);
+ std::swap(info_, other.info_);
+ std::swap(state_, other.state_);
+ std::swap(replied_, other.replied_);
+ return *this;
+}
+
+Message::~Message() { Destroy(); }
+
+void Message::Destroy() {
+ auto svc = service_.lock();
+ if (svc) {
+ if (!replied_) {
+ ALOGE(
+ "ERROR: Service \"%s\" failed to reply to message: op=%d pid=%d "
+ "cid=%d\n",
+ svc->name_.c_str(), info_.op, info_.pid, info_.cid);
+ svc->endpoint()->DefaultHandleMessage(info_);
+ }
+ svc->endpoint()->FreeMessageState(state_);
+ }
+ state_ = nullptr;
+ service_.reset();
+ channel_.reset();
+}
+
+const std::uint8_t* Message::ImpulseBegin() const {
+ return reinterpret_cast<const std::uint8_t*>(info_.impulse);
+}
+
+const std::uint8_t* Message::ImpulseEnd() const {
+ return ImpulseBegin() + (IsImpulse() ? GetSendLength() : 0);
+}
+
+ssize_t Message::ReadVector(const struct iovec* vector, size_t vector_length) {
+ PDX_TRACE_NAME("Message::ReadVector");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const ssize_t ret =
+ svc->endpoint()->ReadMessageData(this, vector, vector_length);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ssize_t Message::Read(void* buffer, size_t length) {
+ PDX_TRACE_NAME("Message::Read");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const struct iovec vector = {buffer, length};
+ const ssize_t ret = svc->endpoint()->ReadMessageData(this, &vector, 1);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ssize_t Message::WriteVector(const struct iovec* vector, size_t vector_length) {
+ PDX_TRACE_NAME("Message::WriteVector");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const ssize_t ret =
+ svc->endpoint()->WriteMessageData(this, vector, vector_length);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ssize_t Message::Write(const void* buffer, size_t length) {
+ PDX_TRACE_NAME("Message::Write");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const struct iovec vector = {const_cast<void*>(buffer), length};
+ const ssize_t ret = svc->endpoint()->WriteMessageData(this, &vector, 1);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+FileReference Message::PushFileHandle(const LocalHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+FileReference Message::PushFileHandle(const BorrowedHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+FileReference Message::PushFileHandle(const RemoteHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ChannelReference Message::PushChannelHandle(const LocalChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ChannelReference Message::PushChannelHandle(
+ const BorrowedChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+ChannelReference Message::PushChannelHandle(const RemoteChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+bool Message::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ PDX_TRACE_NAME("Message::GetFileHandle");
+ auto svc = service_.lock();
+ if (!svc)
+ return false;
+
+ if (ref >= 0) {
+ ErrnoGuard errno_guard;
+ *handle = svc->endpoint()->GetFileHandle(this, ref);
+ if (!handle->IsValid())
+ return false;
+ } else {
+ *handle = LocalHandle{ref};
+ }
+ return true;
+}
+
+bool Message::GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) {
+ PDX_TRACE_NAME("Message::GetChannelHandle");
+ auto svc = service_.lock();
+ if (!svc)
+ return false;
+
+ if (ref >= 0) {
+ ErrnoGuard errno_guard;
+ *handle = svc->endpoint()->GetChannelHandle(this, ref);
+ if (!handle->valid())
+ return false;
+ } else {
+ *handle = LocalChannelHandle{nullptr, ref};
+ }
+ return true;
+}
+
+int Message::Reply(int return_code) {
+ PDX_TRACE_NAME("Message::Reply");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReply(this, return_code);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::ReplyFileDescriptor(unsigned int fd) {
+ PDX_TRACE_NAME("Message::ReplyFileDescriptor");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyFd(this, fd);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::ReplyError(unsigned error) {
+ PDX_TRACE_NAME("Message::ReplyError");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReply(this, -error);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const LocalHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ int ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const BorrowedHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ int ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const RemoteHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReply(this, handle.Get());
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const LocalChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const BorrowedChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::Reply(const RemoteChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ ErrnoGuard errno_guard;
+ const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret == 0;
+ return ReturnCodeOrError(ret);
+ } else {
+ return -EINVAL;
+ }
+}
+
+int Message::ModifyChannelEvents(int clear_mask, int set_mask) {
+ PDX_TRACE_NAME("Message::ModifyChannelEvents");
+ if (auto svc = service_.lock()) {
+ ErrnoGuard errno_guard;
+ const int ret =
+ svc->endpoint()->ModifyChannelEvents(info_.cid, clear_mask, set_mask);
+ return ReturnCodeOrError(ret);
+ } else {
+ return -ESHUTDOWN;
+ }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+ int flags, const std::shared_ptr<Channel>& channel, int* channel_id) {
+ PDX_TRACE_NAME("Message::PushChannel");
+ if (auto svc = service_.lock()) {
+ return svc->PushChannel(this, flags, channel, channel_id);
+ } else {
+ return ErrorStatus(ESHUTDOWN);
+ }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+ Service* service, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id) {
+ PDX_TRACE_NAME("Message::PushChannel");
+ return service->PushChannel(this, flags, channel, channel_id);
+}
+
+Status<int> Message::CheckChannel(ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Message::CheckChannel");
+ if (auto svc = service_.lock()) {
+ return svc->CheckChannel(this, ref, channel);
+ } else {
+ return ErrorStatus(ESHUTDOWN);
+ }
+}
+
+Status<int> Message::CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Message::CheckChannel");
+ return service->CheckChannel(this, ref, channel);
+}
+
+pid_t Message::GetProcessId() const { return info_.pid; }
+
+pid_t Message::GetThreadId() const { return info_.tid; }
+
+uid_t Message::GetEffectiveUserId() const { return info_.euid; }
+
+gid_t Message::GetEffectiveGroupId() const { return info_.egid; }
+
+int Message::GetChannelId() const { return info_.cid; }
+
+int Message::GetMessageId() const { return info_.mid; }
+
+int Message::GetOp() const { return info_.op; }
+
+int Message::GetFlags() const { return info_.flags; }
+
+size_t Message::GetSendLength() const { return info_.send_len; }
+
+size_t Message::GetReceiveLength() const { return info_.recv_len; }
+
+size_t Message::GetFileDescriptorCount() const { return info_.fd_count; }
+
+std::shared_ptr<Channel> Message::GetChannel() const { return channel_.lock(); }
+
+void Message::SetChannel(const std::shared_ptr<Channel>& chan) {
+ channel_ = chan;
+
+ if (auto svc = service_.lock())
+ svc->SetChannel(info_.cid, chan);
+}
+
+std::shared_ptr<Service> Message::GetService() const { return service_.lock(); }
+
+const MessageInfo& Message::GetInfo() const { return info_; }
+
+Service::Service(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+ : name_(name), endpoint_{std::move(endpoint)} {
+ if (!endpoint_)
+ return;
+
+ const int ret = endpoint_->SetService(this);
+ ALOGE_IF(ret < 0, "Failed to set service context because: %s",
+ strerror(-ret));
+}
+
+Service::~Service() {
+ if (endpoint_) {
+ const int ret = endpoint_->SetService(nullptr);
+ ALOGE_IF(ret < 0, "Failed to clear service context because: %s",
+ strerror(-ret));
+ }
+}
+
+std::shared_ptr<Service> Service::GetFromMessageInfo(const MessageInfo& info) {
+ return info.service ? info.service->shared_from_this()
+ : std::shared_ptr<Service>();
+}
+
+bool Service::IsInitialized() const { return endpoint_.get() != nullptr; }
+
+std::shared_ptr<Channel> Service::OnChannelOpen(Message& /*message*/) {
+ return nullptr;
+}
+
+void Service::OnChannelClose(Message& /*message*/,
+ const std::shared_ptr<Channel>& /*channel*/) {}
+
+int Service::SetChannel(int channel_id,
+ const std::shared_ptr<Channel>& channel) {
+ PDX_TRACE_NAME("Service::SetChannel");
+ ErrnoGuard errno_guard;
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ const int ret = endpoint_->SetChannel(channel_id, channel.get());
+ if (ret == -1) {
+ ALOGE("%s::SetChannel: Failed to set channel context: %s\n", name_.c_str(),
+ strerror(errno));
+
+ // It's possible someone mucked with things behind our back by calling the C
+ // API directly. Since we know the channel id isn't valid, make sure we
+ // don't have it in the channels map.
+ if (errno == ENOENT)
+ channels_.erase(channel_id);
+
+ return ReturnCodeOrError(ret);
+ }
+
+ if (channel != nullptr)
+ channels_[channel_id] = channel;
+ else
+ channels_.erase(channel_id);
+
+ return ret;
+}
+
+std::shared_ptr<Channel> Service::GetChannel(int channel_id) const {
+ PDX_TRACE_NAME("Service::GetChannel");
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ auto search = channels_.find(channel_id);
+ if (search != channels_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+int Service::CloseChannel(int channel_id) {
+ PDX_TRACE_NAME("Service::CloseChannel");
+ ErrnoGuard errno_guard;
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ const int ret = endpoint_->CloseChannel(channel_id);
+
+ // Always erase the map entry, in case someone mucked with things behind our
+ // back using the C API directly.
+ channels_.erase(channel_id);
+
+ return ReturnCodeOrError(ret);
+}
+
+int Service::ModifyChannelEvents(int channel_id, int clear_mask, int set_mask) {
+ PDX_TRACE_NAME("Service::ModifyChannelEvents");
+ return endpoint_->ModifyChannelEvents(channel_id, clear_mask, set_mask);
+}
+
+Status<RemoteChannelHandle> Service::PushChannel(
+ Message* message, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id) {
+ PDX_TRACE_NAME("Service::PushChannel");
+ ErrnoGuard errno_guard;
+
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ int channel_id_temp = -1;
+ Status<RemoteChannelHandle> ret =
+ endpoint_->PushChannel(message, flags, channel.get(), &channel_id_temp);
+ ALOGE_IF(!ret.ok(), "%s::PushChannel: Failed to push channel: %s",
+ name_.c_str(), strerror(ret.error()));
+
+ if (channel && channel_id_temp != -1)
+ channels_[channel_id_temp] = channel;
+ if (channel_id)
+ *channel_id = channel_id_temp;
+
+ return ret;
+}
+
+Status<int> Service::CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Service::CheckChannel");
+ ErrnoGuard errno_guard;
+
+ // Synchronization to maintain consistency between the kernel's channel
+ // context pointer and the userspace channels_ map. Other threads may attempt
+ // to modify the map at the same time, which could cause the channel context
+ // pointer returned by the kernel to be invalid.
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ Channel* channel_context = nullptr;
+ Status<int> ret = endpoint_->CheckChannel(
+ message, ref, channel ? &channel_context : nullptr);
+ if (ret && channel) {
+ if (channel_context)
+ *channel = channel_context->shared_from_this();
+ else
+ *channel = nullptr;
+ }
+
+ return ret;
+}
+
+std::string Service::DumpState(size_t /*max_length*/) { return ""; }
+
+int Service::HandleMessage(Message& message) {
+ return DefaultHandleMessage(message);
+}
+
+void Service::HandleImpulse(Message& /*impulse*/) {}
+
+bool Service::HandleSystemMessage(Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ switch (info.op) {
+ case opcodes::CHANNEL_OPEN: {
+ ALOGD("%s::OnChannelOpen: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ message.SetChannel(OnChannelOpen(message));
+ message.Reply(0);
+ return true;
+ }
+
+ case opcodes::CHANNEL_CLOSE: {
+ ALOGD("%s::OnChannelClose: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ OnChannelClose(message, Channel::GetFromMessageInfo(info));
+ message.SetChannel(nullptr);
+ message.Reply(0);
+ return true;
+ }
+
+ case opcodes::REPORT_SYSPROP_CHANGE:
+ ALOGD("%s:REPORT_SYSPROP_CHANGE: pid=%d cid=%d\n", name_.c_str(),
+ info.pid, info.cid);
+ OnSysPropChange();
+ android::report_sysprop_change();
+ message.Reply(0);
+ return true;
+
+ case opcodes::DUMP_STATE: {
+ ALOGD("%s:DUMP_STATE: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ auto response = DumpState(message.GetReceiveLength());
+ const size_t response_size = response.size() < message.GetReceiveLength()
+ ? response.size()
+ : message.GetReceiveLength();
+ const ssize_t bytes_written =
+ message.Write(response.data(), response_size);
+ if (bytes_written < static_cast<ssize_t>(response_size))
+ message.ReplyError(EIO);
+ else
+ message.Reply(bytes_written);
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+int Service::DefaultHandleMessage(Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ ALOGD_IF(TRACE, "Service::DefaultHandleMessage: pid=%d cid=%d op=%d\n",
+ info.pid, info.cid, info.op);
+
+ switch (info.op) {
+ case opcodes::CHANNEL_OPEN:
+ case opcodes::CHANNEL_CLOSE:
+ case opcodes::REPORT_SYSPROP_CHANGE:
+ case opcodes::DUMP_STATE:
+ HandleSystemMessage(message);
+ return 0;
+
+ default:
+ return message.ReplyError(ENOTSUP);
+ }
+}
+
+void Service::OnSysPropChange() {}
+
+int Service::ReceiveAndDispatch() {
+ ErrnoGuard errno_guard;
+ Message message;
+ const int ret = endpoint_->MessageReceive(&message);
+ if (ret < 0) {
+ ALOGE("Failed to receive message: %s\n", strerror(errno));
+ return ReturnCodeOrError(ret);
+ }
+
+ std::shared_ptr<Service> service = message.GetService();
+
+ if (!service) {
+ ALOGE("Service::ReceiveAndDispatch: service context is NULL!!!\n");
+ // Don't block the sender indefinitely in this error case.
+ endpoint_->MessageReply(&message, -EINVAL);
+ return -EINVAL;
+ }
+
+ if (message.IsImpulse()) {
+ service->HandleImpulse(message);
+ return 0;
+ } else if (service->HandleSystemMessage(message)) {
+ return 0;
+ } else {
+ return service->HandleMessage(message);
+ }
+}
+
+int Service::Cancel() {
+ ErrnoGuard errno_guard;
+ const int ret = endpoint_->Cancel();
+ return ReturnCodeOrError(ret);
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/service_tests.cpp b/libs/vr/libpdx/service_tests.cpp
new file mode 100644
index 0000000..fc0c8db
--- /dev/null
+++ b/libs/vr/libpdx/service_tests.cpp
@@ -0,0 +1,796 @@
+#include <pdx/service.h>
+
+#include <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <pdx/mock_service_endpoint.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ErrorStatus;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::MockEndpoint;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Service;
+using android::pdx::Status;
+
+using testing::A;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Matcher;
+using testing::Ref;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::SetErrnoAndReturn;
+using testing::WithArg;
+using testing::WithoutArgs;
+using testing::_;
+
+namespace {
+
+// Helper functions to construct fake void pointers for tests.
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+
+// Helper matchers for working with iovec structures in tests.
+// Simple matcher for one element iovec array:
+// EXPECT_CALL(mock, method(IoVecMatcher(ptr, size)));
+MATCHER_P2(IoVecMatcher, ptr, size, "") {
+ return arg->iov_base == ptr && arg->iov_len == size;
+}
+
+// Matcher for an array of iovecs:
+// EXPECT_CALL(mock,
+// method(IoVecMatcher(IoVecArray{{ptr1, size1}, {ptr2, size2}})));
+using IoVecArray = std::vector<iovec>;
+MATCHER_P(IoVecMatcher, iovec_array, "") {
+ for (const iovec& item : iovec_array) {
+ if (arg->iov_base != item.iov_base || arg->iov_len != item.iov_len)
+ return false;
+ arg++;
+ }
+ return true;
+}
+
+using IoVecData = std::vector<std::string>;
+MATCHER_P(IoVecDataMatcher, iovec_data, "") {
+ for (const std::string& item : iovec_data) {
+ std::string data{reinterpret_cast<const char*>(arg->iov_base),
+ arg->iov_len};
+ if (data != item)
+ return false;
+ arg++;
+ }
+ return true;
+}
+
+MATCHER_P(FileHandleMatcher, value, "") { return arg.Get() == value; }
+MATCHER_P(ChannelHandleMatcher, value, "") { return arg.value() == value; }
+
+enum : int {
+ kTestPid = 1,
+ kTestTid,
+ kTestCid,
+ kTestMid,
+ kTestEuid,
+ kTestEgid,
+ kTestOp,
+};
+
+class MockService : public Service {
+ public:
+ using Service::Service;
+ MOCK_METHOD1(OnChannelOpen, std::shared_ptr<Channel>(Message& message));
+ MOCK_METHOD2(OnChannelClose,
+ void(Message& message, const std::shared_ptr<Channel>& channel));
+ MOCK_METHOD1(HandleMessage, int(Message& message));
+ MOCK_METHOD1(HandleImpulse, void(Message& impulse));
+ MOCK_METHOD0(OnSysPropChange, void());
+ MOCK_METHOD1(DumpState, std::string(size_t max_length));
+};
+
+class ServiceTest : public testing::Test {
+ public:
+ ServiceTest() {
+ auto endpoint = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint, SetService(_)).Times(2).WillRepeatedly(Return(0));
+ service_ = std::make_shared<MockService>("MockSvc", std::move(endpoint));
+ }
+
+ MockEndpoint* endpoint() {
+ return static_cast<MockEndpoint*>(service_->endpoint());
+ }
+
+ void SetupMessageInfo(MessageInfo* info, int32_t op, bool impulse = false) {
+ info->pid = kTestPid;
+ info->tid = kTestTid;
+ info->cid = kTestCid;
+ info->mid = impulse ? Message::IMPULSE_MESSAGE_ID : kTestMid;
+ info->euid = kTestEuid;
+ info->egid = kTestEgid;
+ info->op = op;
+ info->flags = 0;
+ info->service = service_.get();
+ info->channel = nullptr;
+ info->send_len = 0;
+ info->recv_len = 0;
+ info->fd_count = 0;
+ memset(info->impulse, 0, sizeof(info->impulse));
+ }
+
+ void SetupMessageInfoAndDefaultExpectations(MessageInfo* info, int32_t op,
+ bool impulse = false) {
+ SetupMessageInfo(info, op, impulse);
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+ EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+ }
+
+ void ExpectDefaultHandleMessage() {
+ EXPECT_CALL(*endpoint(), DefaultHandleMessage(_));
+ }
+
+ std::shared_ptr<MockService> service_;
+ void* kState = IntToPtr(123456);
+};
+
+class ServiceMessageTest : public ServiceTest {
+ public:
+ ServiceMessageTest() {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ message_ = std::make_unique<Message>(info);
+ }
+
+ std::unique_ptr<Message> message_;
+};
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Service class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceTest, IsInitialized) {
+ EXPECT_TRUE(service_->IsInitialized());
+ service_ = std::make_shared<MockService>("MockSvc2", nullptr);
+ EXPECT_FALSE(service_->IsInitialized());
+}
+
+TEST_F(ServiceTest, ConstructMessage) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp);
+ auto test_channel = std::make_shared<Channel>();
+ info.channel = test_channel.get();
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+
+ Message message{info};
+
+ EXPECT_FALSE(message.IsImpulse());
+ EXPECT_EQ(kTestPid, message.GetProcessId());
+ EXPECT_EQ(kTestTid, message.GetThreadId());
+ EXPECT_EQ(kTestCid, message.GetChannelId());
+ EXPECT_EQ(kTestMid, message.GetMessageId());
+ EXPECT_EQ(kTestEuid, message.GetEffectiveUserId());
+ EXPECT_EQ(kTestEgid, message.GetEffectiveGroupId());
+ EXPECT_EQ(kTestOp, message.GetOp());
+ EXPECT_EQ(service_, message.GetService());
+ EXPECT_EQ(test_channel, message.GetChannel());
+ EXPECT_FALSE(message.replied());
+ EXPECT_FALSE(message.IsChannelExpired());
+ EXPECT_FALSE(message.IsServiceExpired());
+ EXPECT_EQ(kState, message.GetState());
+
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+}
+
+TEST_F(ServiceTest, ConstructImpulseMessage) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp, true);
+ auto test_channel = std::make_shared<Channel>();
+ info.channel = test_channel.get();
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+ Message message{info};
+
+ EXPECT_TRUE(message.IsImpulse());
+ EXPECT_EQ(kTestOp, message.GetOp());
+ EXPECT_EQ(service_, message.GetService());
+ EXPECT_EQ(test_channel, message.GetChannel());
+ EXPECT_TRUE(message.replied());
+ EXPECT_FALSE(message.IsChannelExpired());
+ EXPECT_FALSE(message.IsServiceExpired());
+
+ // DefaultHandleMessage should not be called here.
+ EXPECT_CALL(*endpoint(), FreeMessageState(nullptr));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelOpen) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::CHANNEL_OPEN);
+ Message message{info};
+
+ auto channel = std::make_shared<Channel>();
+ EXPECT_CALL(*service_, OnChannelOpen(Ref(message))).WillOnce(Return(channel));
+ EXPECT_CALL(*endpoint(), SetChannel(kTestCid, channel.get()))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelClose) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::CHANNEL_CLOSE);
+ auto channel = std::make_shared<Channel>();
+ info.channel = channel.get();
+ Message message{info};
+
+ EXPECT_CALL(*service_, OnChannelClose(Ref(message), channel));
+ EXPECT_CALL(*endpoint(), SetChannel(kTestCid, nullptr)).WillOnce(Return(0));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnSysPropChange) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(
+ &info, android::pdx::opcodes::REPORT_SYSPROP_CHANGE);
+ Message message{info};
+
+ EXPECT_CALL(*service_, OnSysPropChange());
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpState) {
+ const size_t kRecvBufSize = 1000;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "foo";
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+ .WillOnce(Return(kReply.size()));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, kReply.size()))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateTooLarge) {
+ const size_t kRecvBufSize = 3;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "0123456789";
+ const std::string kActualReply = kReply.substr(0, kRecvBufSize);
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kActualReply}), 1))
+ .WillOnce(Return(kActualReply.size()));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, kActualReply.size()))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateFail) {
+ const size_t kRecvBufSize = 1000;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "foo";
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, -EIO)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageCustom) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ Message message{info};
+
+ EXPECT_CALL(*endpoint(), MessageReply(&message, -ENOTSUP))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, ReplyMessageWithoutService) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp);
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+ Message message{info};
+
+ EXPECT_FALSE(message.IsServiceExpired());
+ service_.reset();
+ EXPECT_TRUE(message.IsServiceExpired());
+
+ EXPECT_EQ(-EINVAL, message.Reply(12));
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchMessage) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ ExpectDefaultHandleMessage();
+
+ auto on_receive = [&info](Message* message) {
+ *message = Message{info};
+ return 0;
+ };
+ EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+ EXPECT_CALL(*service_, HandleMessage(_)).WillOnce(Return(0));
+
+ EXPECT_EQ(0, service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchImpulse) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp, true);
+
+ auto on_receive = [&info](Message* message) {
+ *message = Message{info};
+ return 0;
+ };
+ EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+ EXPECT_CALL(*service_, HandleImpulse(_));
+
+ EXPECT_EQ(0, service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, Cancel) {
+ EXPECT_CALL(*endpoint(), Cancel()).WillOnce(Return(0));
+ EXPECT_EQ(0, service_->Cancel());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Message class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceMessageTest, Reply) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+ .WillOnce(Return(0));
+ EXPECT_FALSE(message_->replied());
+ EXPECT_EQ(0, message_->Reply(12));
+ EXPECT_TRUE(message_->replied());
+
+ EXPECT_EQ(-EINVAL, message_->Reply(12)); // Already replied.
+}
+
+TEST_F(ServiceMessageTest, ReplyFail) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(-EIO, message_->Reply(12));
+
+ ExpectDefaultHandleMessage();
+}
+
+TEST_F(ServiceMessageTest, ReplyError) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -12))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->ReplyError(12));
+}
+
+TEST_F(ServiceMessageTest, ReplyFileDescriptor) {
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), 5))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->ReplyFileDescriptor(5));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandle) {
+ const int kFakeFd = 12345;
+ LocalHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandleError) {
+ LocalHandle handle{-EINVAL};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EINVAL))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandle) {
+ const int kFakeFd = 12345;
+ BorrowedHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandleError) {
+ BorrowedHandle handle{-EACCES};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EACCES))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandle) {
+ RemoteHandle handle{123};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), handle.Get()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandleError) {
+ RemoteHandle handle{-EIO};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EIO))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalChannelHandle) {
+ LocalChannelHandle handle{nullptr, 12345};
+ EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+ message_.get(), A<const LocalChannelHandle&>()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedChannelHandle) {
+ BorrowedChannelHandle handle{12345};
+ EXPECT_CALL(*endpoint(),
+ MessageReplyChannelHandle(message_.get(),
+ A<const BorrowedChannelHandle&>()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteChannelHandle) {
+ RemoteChannelHandle handle{12345};
+ EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+ message_.get(), A<const RemoteChannelHandle&>()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusInt) {
+ Status<int> status{123};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), status.get()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusError) {
+ Status<int> status{ErrorStatus{EIO}};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -status.error()))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, Read) {
+ ExpectDefaultHandleMessage();
+ void* const kDataBuffer = IntToPtr(12345);
+ const size_t kDataSize = 100;
+ EXPECT_CALL(
+ *endpoint(),
+ ReadMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+ .WillOnce(Return(50))
+ .WillOnce(SetErrnoAndReturn(EACCES, -1));
+ EXPECT_EQ(50, message_->Read(kDataBuffer, kDataSize));
+ EXPECT_EQ(-EACCES, message_->Read(kDataBuffer, kDataSize));
+}
+
+TEST_F(ServiceMessageTest, ReadVector) {
+ ExpectDefaultHandleMessage();
+ char buffer1[10];
+ char buffer2[20];
+ iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+ EXPECT_CALL(*endpoint(),
+ ReadMessageData(
+ message_.get(),
+ IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+ .WillOnce(Return(30))
+ .WillOnce(Return(15))
+ .WillOnce(SetErrnoAndReturn(EBADF, -1));
+ EXPECT_EQ(30, message_->ReadVector(vec, 2));
+ EXPECT_EQ(15, message_->ReadVector(vec));
+ EXPECT_EQ(-EBADF, message_->ReadVector(vec));
+}
+
+TEST_F(ServiceMessageTest, Write) {
+ ExpectDefaultHandleMessage();
+ void* const kDataBuffer = IntToPtr(12345);
+ const size_t kDataSize = 100;
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+ .WillOnce(Return(50))
+ .WillOnce(SetErrnoAndReturn(EBADMSG, -1));
+ EXPECT_EQ(50, message_->Write(kDataBuffer, kDataSize));
+ EXPECT_EQ(-EBADMSG, message_->Write(kDataBuffer, kDataSize));
+}
+
+TEST_F(ServiceMessageTest, WriteVector) {
+ ExpectDefaultHandleMessage();
+ char buffer1[10];
+ char buffer2[20];
+ iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+ EXPECT_CALL(*endpoint(),
+ WriteMessageData(
+ message_.get(),
+ IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+ .WillOnce(Return(30))
+ .WillOnce(Return(15))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(30, message_->WriteVector(vec, 2));
+ EXPECT_EQ(15, message_->WriteVector(vec));
+ EXPECT_EQ(-EIO, message_->WriteVector(vec, 2));
+}
+
+TEST_F(ServiceMessageTest, PushLocalFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ LocalHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const LocalHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(12))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(12, message_->PushFileHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushFileHandle(handle));
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ BorrowedHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const BorrowedHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(13))
+ .WillOnce(SetErrnoAndReturn(EACCES, -1));
+ EXPECT_EQ(13, message_->PushFileHandle(handle));
+ EXPECT_EQ(-EACCES, message_->PushFileHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushRemoteFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ RemoteHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const RemoteHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(kFakeFd))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(kFakeFd, message_->PushFileHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushFileHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushLocalChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ LocalChannelHandle handle{nullptr, kValue};
+ EXPECT_CALL(*endpoint(), PushChannelHandle(message_.get(),
+ Matcher<const LocalChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(7))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(7, message_->PushChannelHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ BorrowedChannelHandle handle{kValue};
+ EXPECT_CALL(
+ *endpoint(),
+ PushChannelHandle(message_.get(), Matcher<const BorrowedChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(8))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(8, message_->PushChannelHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, PushRemoteChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ RemoteChannelHandle handle{kValue};
+ EXPECT_CALL(
+ *endpoint(),
+ PushChannelHandle(message_.get(), Matcher<const RemoteChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(kValue))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+ EXPECT_EQ(kValue, message_->PushChannelHandle(handle));
+ EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+TEST_F(ServiceMessageTest, GetFileHandle) {
+ ExpectDefaultHandleMessage();
+ auto make_file_handle = [](FileReference ref) { return LocalHandle{ref}; };
+ EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+ .WillOnce(WithArg<1>(Invoke(make_file_handle)));
+ LocalHandle handle;
+ FileReference kRef = 12345;
+ EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.Get());
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleInvalid) {
+ ExpectDefaultHandleMessage();
+ LocalHandle handle;
+ FileReference kRef = -12;
+ EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleError) {
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+ .WillOnce(WithoutArgs(Invoke([] { return LocalHandle{-EIO}; })));
+ LocalHandle handle;
+ FileReference kRef = 12345;
+ EXPECT_FALSE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(-EIO, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandle) {
+ ExpectDefaultHandleMessage();
+ auto make_channel_handle = [](ChannelReference ref) {
+ return LocalChannelHandle{nullptr, ref};
+ };
+ EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+ .WillOnce(WithArg<1>(Invoke(make_channel_handle)));
+ LocalChannelHandle handle;
+ ChannelReference kRef = 12345;
+ EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleInvalid) {
+ ExpectDefaultHandleMessage();
+ LocalChannelHandle handle;
+ ChannelReference kRef = -12;
+ EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(-12, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleError) {
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+ .WillOnce(WithoutArgs(Invoke([] {
+ return LocalChannelHandle{nullptr, -EIO};
+ })));
+ LocalChannelHandle handle;
+ ChannelReference kRef = 12345;
+ EXPECT_FALSE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(-EIO, handle.value());
+}
+
+TEST_F(ServiceMessageTest, ModifyChannelEvents) {
+ ExpectDefaultHandleMessage();
+ int kClearMask = 1;
+ int kSetMask = 2;
+ EXPECT_CALL(*endpoint(), ModifyChannelEvents(kTestCid, kClearMask, kSetMask))
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, message_->ModifyChannelEvents(kClearMask, kSetMask));
+}
+
+TEST_F(ServiceMessageTest, PushChannelSameService) {
+ ExpectDefaultHandleMessage();
+ int kFlags = 123;
+ int32_t kValue = 12;
+ EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+ Return(ByMove(RemoteChannelHandle{kValue}))));
+ int channel_id = -1;
+ auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kValue, status.get().value());
+ EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, PushChannelFailure) {
+ ExpectDefaultHandleMessage();
+ int kFlags = 123;
+ EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(Return(ByMove(ErrorStatus{EIO})));
+ int channel_id = -1;
+ auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ServiceMessageTest, PushChannelDifferentService) {
+ ExpectDefaultHandleMessage();
+ auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint2, SetService(_)).Times(2).WillRepeatedly(Return(0));
+ auto service2 =
+ std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+ int kFlags = 123;
+ int32_t kValue = 12;
+ EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+ PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+ Return(ByMove(RemoteChannelHandle{kValue}))));
+ int channel_id = -1;
+ auto status =
+ message_->PushChannel(service2.get(), kFlags, nullptr, &channel_id);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kValue, status.get().value());
+ EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelSameService) {
+ ExpectDefaultHandleMessage();
+
+ auto test_channel = std::make_shared<Channel>();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+ .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(kRef, &channel);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kTestCid, status.get());
+ EXPECT_EQ(test_channel, channel);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelFailure) {
+ ExpectDefaultHandleMessage();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+ .WillOnce(Return(ByMove(ErrorStatus{EOPNOTSUPP})));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(kRef, &channel);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EOPNOTSUPP, status.error());
+}
+
+TEST_F(ServiceMessageTest, CheckChannelDifferentService) {
+ ExpectDefaultHandleMessage();
+ auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint2, SetService(_)).Times(2).WillRepeatedly(Return(0));
+ auto service2 =
+ std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+ auto test_channel = std::make_shared<Channel>();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+ CheckChannel(message_.get(), kRef, _))
+ .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(service2.get(), kRef, &channel);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kTestCid, status.get());
+ EXPECT_EQ(test_channel, channel);
+}
diff --git a/libs/vr/libpdx/status.cpp b/libs/vr/libpdx/status.cpp
new file mode 100644
index 0000000..c275daf
--- /dev/null
+++ b/libs/vr/libpdx/status.cpp
@@ -0,0 +1,15 @@
+#include "pdx/status.h"
+
+#include <pdx/rpc/serialization.h>
+#include <string.h>
+
+namespace android {
+namespace pdx {
+
+std::string ErrorStatus::ErrorToString(int error_code) {
+ char message[1024] = {};
+ return strerror_r(error_code, message, sizeof(message));
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/status_tests.cpp b/libs/vr/libpdx/status_tests.cpp
new file mode 100644
index 0000000..d4e697c
--- /dev/null
+++ b/libs/vr/libpdx/status_tests.cpp
@@ -0,0 +1,125 @@
+#include <pdx/status.h>
+
+#include <gtest/gtest.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
+TEST(Status, DefaultInit) {
+ Status<int> status;
+ EXPECT_FALSE(status.ok());
+ EXPECT_TRUE(status.empty());
+ EXPECT_EQ(0, status.get());
+ EXPECT_EQ(0, status.error());
+}
+
+TEST(Status, InitalizeSuccess) {
+ Status<int> status_int{0};
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(0, status_int.get());
+ status_int = Status<int>(3);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(3, status_int.get());
+ status_int = Status<int>(-3);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(-3, status_int.get());
+
+ Status<std::string> status_str{"foo"};
+ EXPECT_FALSE(status_str.empty());
+ EXPECT_TRUE(status_str.ok());
+ EXPECT_EQ("foo", status_str.get());
+}
+
+TEST(Status, InitalizeError) {
+ Status<int> status_int = ErrorStatus(12);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_FALSE(status_int.ok());
+ EXPECT_EQ(0, status_int.get());
+ EXPECT_EQ(12, status_int.error());
+
+ Status<std::string> status_str = ErrorStatus(EIO);
+ EXPECT_FALSE(status_str.empty());
+ EXPECT_FALSE(status_str.ok());
+ EXPECT_EQ(EIO, status_str.error());
+}
+
+TEST(Status, ErrorMessage) {
+ Status<int> status = ErrorStatus(EIO);
+ EXPECT_EQ(status.GetErrorMessage(), strerror(EIO));
+
+ status = ErrorStatus(EINVAL);
+ EXPECT_EQ(status.GetErrorMessage(), strerror(EINVAL));
+}
+
+TEST(Status, Copy) {
+ Status<int> status1;
+ Status<int> status2;
+
+ status1 = Status<int>{12};
+ status2 = ErrorStatus(13);
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_EQ(12, status1.get());
+ EXPECT_EQ(0, status1.error());
+ EXPECT_EQ(0, status2.get());
+ EXPECT_EQ(13, status2.error());
+
+ status1 = status2;
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_FALSE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_EQ(0, status1.get());
+ EXPECT_EQ(13, status1.error());
+ EXPECT_EQ(0, status2.get());
+ EXPECT_EQ(13, status2.error());
+}
+
+TEST(Status, Move) {
+ Status<std::unique_ptr<int>> status1;
+ Status<std::unique_ptr<int>> status2;
+
+ status1 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{11}}};
+ status2 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{12}}};
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_TRUE(status2.ok());
+ EXPECT_EQ(11, *status1.get());
+ EXPECT_EQ(12, *status2.get());
+
+ Status<std::unique_ptr<int>> status3 = std::move(status2);
+ EXPECT_FALSE(status1.empty());
+ EXPECT_TRUE(status2.empty());
+ EXPECT_FALSE(status3.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_TRUE(status3.ok());
+ EXPECT_EQ(11, *status1.get());
+ EXPECT_EQ(nullptr, status2.get());
+ EXPECT_EQ(12, *status3.get());
+
+ std::swap(status1, status3);
+ EXPECT_EQ(12, *status1.get());
+ EXPECT_EQ(11, *status3.get());
+
+ status3 = std::move(status1);
+ EXPECT_TRUE(status1.empty());
+ EXPECT_EQ(12, *status3.get());
+}
+
+TEST(Status, Take) {
+ Status<std::unique_ptr<int>> status{std::unique_ptr<int>{new int{123}}};
+ EXPECT_FALSE(status.empty());
+ EXPECT_NE(nullptr, status.get());
+
+ auto data = status.take();
+ EXPECT_TRUE(status.empty());
+ EXPECT_EQ(nullptr, status.get());
+ EXPECT_EQ(123, *data);
+}
diff --git a/libs/vr/libpdx/thread_local_buffer_tests.cpp b/libs/vr/libpdx/thread_local_buffer_tests.cpp
new file mode 100644
index 0000000..c6a7b0b
--- /dev/null
+++ b/libs/vr/libpdx/thread_local_buffer_tests.cpp
@@ -0,0 +1,116 @@
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/message_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+class ThreadLocalBufferTest {
+ public:
+ // Returns the unique address of the thread-local buffer. Used to test the
+ // correct behavior of the type-based thread local storage slot mapping
+ // mechanism.
+ template <typename Slot>
+ static std::uintptr_t GetSlotAddress() {
+ return reinterpret_cast<std::uintptr_t>(&MessageBuffer<Slot>::buffer_);
+ }
+
+ // Returns the raw value of the thread local buffer. Used to test the behavior
+ // of backing buffer initialization.
+ template <typename Slot>
+ static std::uintptr_t GetSlotValue() {
+ return reinterpret_cast<std::uintptr_t>(MessageBuffer<Slot>::buffer_);
+ }
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct TypeTagA;
+struct TypeTagB;
+
+constexpr std::size_t kSendBufferIndex = 0;
+constexpr std::size_t kReceiveBufferIndex = 1;
+
+using SendSlotA = ThreadLocalSlot<TypeTagA, kSendBufferIndex>;
+using SendSlotB = ThreadLocalSlot<TypeTagB, kSendBufferIndex>;
+using ReceiveSlotA = ThreadLocalSlot<TypeTagA, kReceiveBufferIndex>;
+using ReceiveSlotB = ThreadLocalSlot<TypeTagB, kReceiveBufferIndex>;
+
+} // anonymous namespace
+
+// Tests that index and type-based thread-local slot addressing works by
+// checking that the slot address is the same when the same index/type
+// combination is used and different when different combinations are used.
+TEST(ThreadLocalBufferTest, TypeSlots) {
+ auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+ auto id2 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+ auto id3 = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+ auto id4 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+ EXPECT_NE(id1, id2);
+ EXPECT_NE(id3, id4);
+ EXPECT_NE(id1, id3);
+ EXPECT_NE(id2, id4);
+
+ auto id1_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+ auto id2_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+ auto id3_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+ auto id4_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+ EXPECT_EQ(id1, id1_alias);
+ EXPECT_EQ(id2, id2_alias);
+ EXPECT_EQ(id3, id3_alias);
+ EXPECT_EQ(id4, id4_alias);
+}
+
+// Tests that different threads get different buffers for the same slot address.
+TEST(ThreadLocalBufferTest, ThreadSlots) {
+ auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+ std::uintptr_t id2 = 0U;
+
+ std::thread thread([&id2]() mutable {
+ id2 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+ });
+ thread.join();
+
+ EXPECT_NE(0U, id1);
+ EXPECT_NE(0U, id2);
+ EXPECT_NE(id1, id2);
+}
+
+// Tests that thread-local buffers are allocated at the first buffer request.
+TEST(ThreadLocalBufferTest, InitialValue) {
+ struct TypeTagX;
+ using SendSlotX = ThreadLocalSlot<TypeTagX, kSendBufferIndex>;
+
+ auto value1 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+ auto value2 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+
+ EXPECT_EQ(0U, value1);
+ EXPECT_NE(0U, value2);
+}
+
+// Tests that the underlying buffers are the same for a given index/type pair
+// and different across index/type combinations.
+TEST(ThreadLocalBufferTest, BackingBuffer) {
+ auto& buffer1 = MessageBuffer<SendSlotA>::GetBuffer();
+ auto& buffer2 = MessageBuffer<SendSlotA>::GetBuffer();
+ auto& buffer3 = MessageBuffer<SendSlotB>::GetBuffer();
+ auto& buffer4 = MessageBuffer<SendSlotB>::GetBuffer();
+
+ EXPECT_EQ(buffer1.data(), buffer2.data());
+ EXPECT_EQ(buffer3.data(), buffer4.data());
+ EXPECT_NE(buffer1.data(), buffer3.data());
+ EXPECT_NE(buffer2.data(), buffer4.data());
+}
diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp
new file mode 100644
index 0000000..c30c055
--- /dev/null
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -0,0 +1,1087 @@
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/variant.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct BaseType {
+ BaseType(int value) : value(value) {}
+ int value;
+};
+
+struct DerivedType : BaseType {
+ DerivedType(int value) : BaseType{value} {};
+};
+
+template <typename T>
+class TestType {
+ public:
+ TestType(const T& value) : value_(value) {}
+ TestType(T&& value) : value_(std::move(value)) {}
+ TestType(const TestType&) = default;
+ TestType(TestType&&) = default;
+
+ TestType& operator=(const TestType&) = default;
+ TestType& operator=(TestType&&) = default;
+
+ const T& get() const { return value_; }
+ T&& take() { return std::move(value_); }
+
+ private:
+ T value_;
+};
+
+template <typename T>
+class InstrumentType {
+ public:
+ InstrumentType(const T& value) : value_(value) { constructor_count_++; }
+ InstrumentType(T&& value) : value_(std::move(value)) { constructor_count_++; }
+ InstrumentType(const InstrumentType& other) : value_(other.value_) {
+ constructor_count_++;
+ }
+ InstrumentType(InstrumentType&& other) : value_(std::move(other.value_)) {
+ constructor_count_++;
+ }
+ InstrumentType(const TestType<T>& other) : value_(other.get()) {
+ constructor_count_++;
+ }
+ InstrumentType(TestType<T>&& other) : value_(other.take()) {
+ constructor_count_++;
+ }
+ ~InstrumentType() { destructor_count_++; }
+
+ InstrumentType& operator=(const InstrumentType& other) {
+ copy_assignment_count_++;
+ value_ = other.value_;
+ return *this;
+ }
+ InstrumentType& operator=(InstrumentType&& other) {
+ move_assignment_count_++;
+ value_ = std::move(other.value_);
+ return *this;
+ }
+
+ InstrumentType& operator=(const TestType<T>& other) {
+ copy_assignment_count_++;
+ value_ = other.get();
+ return *this;
+ }
+ InstrumentType& operator=(TestType<T>&& other) {
+ move_assignment_count_++;
+ value_ = other.take();
+ return *this;
+ }
+
+ static std::size_t constructor_count() { return constructor_count_; }
+ static std::size_t destructor_count() { return destructor_count_; }
+ static std::size_t move_assignment_count() { return move_assignment_count_; }
+ static std::size_t copy_assignment_count() { return copy_assignment_count_; }
+
+ const T& get() const { return value_; }
+ T&& take() { return std::move(value_); }
+
+ static void clear() {
+ constructor_count_ = 0;
+ destructor_count_ = 0;
+ move_assignment_count_ = 0;
+ copy_assignment_count_ = 0;
+ }
+
+ private:
+ T value_;
+
+ static std::size_t constructor_count_;
+ static std::size_t destructor_count_;
+ static std::size_t move_assignment_count_;
+ static std::size_t copy_assignment_count_;
+};
+
+template <typename T>
+std::size_t InstrumentType<T>::constructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::destructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::move_assignment_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::copy_assignment_count_ = 0;
+
+} // anonymous namespace
+
+TEST(Variant, Assignment) {
+ // Assert basic type properties.
+ {
+ Variant<int, bool, float> v;
+ ASSERT_EQ(-1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = 10;
+ ASSERT_EQ(0, v.index());
+ ASSERT_TRUE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ EXPECT_EQ(10, std::get<int>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = false;
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ EXPECT_EQ(false, std::get<bool>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = 1.0f;
+ ASSERT_EQ(2, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.0f, std::get<float>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ // ERROR: More than one type is implicitly convertible from double.
+ // v = 1.0;
+ v = static_cast<float>(1.0);
+ }
+
+ {
+ Variant<int, bool, float> v;
+
+ double x = 1.1;
+ v = static_cast<float>(x);
+ ASSERT_EQ(2, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ ASSERT_EQ(-1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<std::string>());
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = 20;
+ ASSERT_EQ(0, v.index());
+ ASSERT_TRUE(v.is<int>());
+ ASSERT_FALSE(v.is<std::string>());
+ EXPECT_EQ(20, std::get<int>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = std::string("test");
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = "test";
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<const char*> v1;
+ Variant<std::string> v2;
+
+ v1 = "test";
+ ASSERT_TRUE(v1.is<const char*>());
+ v2 = v1;
+ ASSERT_TRUE(v2.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v2));
+ }
+
+ {
+ Variant<int> a(1);
+ Variant<int> b;
+ ASSERT_TRUE(!a.empty());
+ ASSERT_TRUE(b.empty());
+
+ a = b;
+ ASSERT_TRUE(a.empty());
+ ASSERT_TRUE(b.empty());
+ }
+
+ {
+ Variant<int*, char*> v;
+
+ // ERROR: More than one type is implicitly convertible from nullptr.
+ // v = nullptr;
+
+ v = static_cast<int*>(nullptr);
+ EXPECT_TRUE(v.is<int*>());
+
+ v = static_cast<char*>(nullptr);
+ EXPECT_TRUE(v.is<char*>());
+ }
+
+ {
+ Variant<int*, char*> v;
+ int a = 10;
+ char b = 20;
+
+ v = &b;
+ ASSERT_TRUE(v.is<char*>());
+ EXPECT_EQ(&b, std::get<char*>(v));
+ EXPECT_EQ(b, *std::get<char*>(v));
+
+ v = &a;
+ ASSERT_TRUE(v.is<int*>());
+ EXPECT_EQ(&a, std::get<int*>(v));
+ EXPECT_EQ(a, *std::get<int*>(v));
+ }
+
+ {
+ using IntRef = std::reference_wrapper<int>;
+ Variant<IntRef> v;
+ int a = 10;
+
+ v = a;
+ ASSERT_TRUE(v.is<IntRef>());
+ EXPECT_EQ(a, std::get<IntRef>(v));
+
+ a = 20;
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ }
+}
+
+TEST(Variant, MoveAssignment) {
+ {
+ Variant<std::string> v;
+ std::string s = "test";
+ v = std::move(s);
+
+ EXPECT_TRUE(s.empty());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<std::string> v("test");
+ std::string s = "fizz";
+ s = std::move(std::get<std::string>(v));
+
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ EXPECT_EQ("test", s);
+ }
+
+ {
+ Variant<std::string> a("test");
+ Variant<std::string> b;
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<std::string> a("test");
+ Variant<std::string> b("fizz");
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a("test");
+ Variant<int, std::string> b(10);
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a(10);
+ Variant<int, std::string> b("test");
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<int>());
+ ASSERT_TRUE(b.is<int>());
+ EXPECT_EQ(10, std::get<int>(a));
+ EXPECT_EQ(10, std::get<int>(b));
+ }
+}
+
+TEST(Variant, Constructor) {
+ {
+ Variant<int, bool, float> v(true);
+ EXPECT_TRUE(v.is<bool>());
+ }
+
+ {
+ Variant<int, bool, float> v(10);
+ EXPECT_TRUE(v.is<int>());
+ }
+
+ {
+ Variant<int, bool, float> v(10.1f);
+ EXPECT_TRUE(v.is<float>());
+ }
+
+ {
+ Variant<float, std::string> v(10.);
+ EXPECT_TRUE(v.is<float>());
+ }
+
+ {
+ TestType<int> i(1);
+ Variant<int, bool, float> v(i.take());
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(1, std::get<int>(v));
+ }
+
+ {
+ TestType<bool> b(true);
+ Variant<int, bool, float> v(b.take());
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+ }
+
+ {
+ Variant<const char*> c("test");
+ Variant<std::string> s(c);
+ ASSERT_TRUE(s.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(s));
+ }
+
+ {
+ Variant<int, bool, float> a(true);
+ Variant<int, bool, float> b(a);
+
+ ASSERT_TRUE(b.is<bool>());
+ }
+
+ {
+ using IntRef = std::reference_wrapper<int>;
+ int a = 10;
+ Variant<IntRef> v(a);
+ TestType<IntRef> t(a);
+
+ ASSERT_TRUE(v.is<IntRef>());
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ EXPECT_EQ(a, t.get());
+
+ a = 20;
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ EXPECT_EQ(a, t.get());
+ }
+}
+
+// Verify correct ctor/dtor and assignment behavior used an instrumented type.
+TEST(Variant, CopyMoveConstructAssign) {
+ {
+ InstrumentType<int>::clear();
+
+ // Default construct to empty, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v;
+ ASSERT_EQ(0u, InstrumentType<int>::constructor_count());
+ ASSERT_EQ(0u, InstrumentType<int>::destructor_count());
+ ASSERT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ ASSERT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from int type, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v;
+ v = 10;
+ EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from int type, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v(10);
+ EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v;
+ v = InstrumentType<int>(25);
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ // Assign from temporary, temporary ctor/dtor.
+ v = InstrumentType<int>(35);
+ EXPECT_EQ(3u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ // dtor.
+ v = 10;
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ // Assign from other temporary.
+ v = TestType<int>(11);
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ // Assign from empty Variant.
+ v = Variant<int, InstrumentType<int>>();
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ TestType<int> other(10);
+ // Construct from other.
+ Variant<int, InstrumentType<int>> v(other);
+
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(0));
+ TestType<int> other(10);
+ // Assign from other.
+ v = other;
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(1u, InstrumentType<int>::copy_assignment_count());
+ }
+}
+
+TEST(Variant, MoveConstructor) {
+ {
+ std::unique_ptr<int> pointer = std::make_unique<int>(10);
+ Variant<std::unique_ptr<int>> v(std::move(pointer));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) != nullptr);
+ EXPECT_TRUE(pointer == nullptr);
+ }
+
+ {
+ Variant<std::unique_ptr<int>> a(std::make_unique<int>(10));
+ Variant<std::unique_ptr<int>> b(std::move(a));
+
+ ASSERT_TRUE(a.is<std::unique_ptr<int>>());
+ ASSERT_TRUE(b.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(a) == nullptr);
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(b) != nullptr);
+ }
+}
+
+TEST(Variant, IndexOf) {
+ Variant<int, bool, float> v1;
+
+ EXPECT_EQ(0, v1.index_of<int>());
+ EXPECT_EQ(1, v1.index_of<bool>());
+ EXPECT_EQ(2, v1.index_of<float>());
+
+ Variant<int, bool, float, int> v2;
+
+ EXPECT_EQ(0, v2.index_of<int>());
+ EXPECT_EQ(1, v2.index_of<bool>());
+ EXPECT_EQ(2, v2.index_of<float>());
+}
+
+struct Visitor {
+ int int_value = 0;
+ bool bool_value = false;
+ float float_value = 0.0;
+ bool empty_value = false;
+
+ void Visit(int value) { int_value = value; }
+ void Visit(bool value) { bool_value = value; }
+ void Visit(float value) { float_value = value; }
+ void Visit(EmptyVariant) { empty_value = true; }
+};
+
+TEST(Variant, Visit) {
+ {
+ Variant<int, bool, float> v(10);
+ EXPECT_TRUE(v.is<int>());
+
+ Visitor visitor;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_EQ(10, visitor.int_value);
+
+ visitor = {};
+ v = true;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_EQ(true, visitor.bool_value);
+ }
+
+ {
+ Variant<int, bool, float> v;
+ EXPECT_EQ(-1, v.index());
+
+ Visitor visitor;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_TRUE(visitor.empty_value);
+ }
+
+ {
+ Variant<std::string> v("test");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_FALSE(std::get<std::string>(v).empty());
+
+ v.Visit([](auto&& value) {
+ std::remove_reference_t<decltype(value)> empty;
+ std::swap(empty, value);
+ });
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ }
+}
+
+TEST(Variant, Become) {
+ {
+ Variant<int, bool, float> v;
+
+ v.Become(0);
+ EXPECT_TRUE(v.is<int>());
+
+ v.Become(1);
+ EXPECT_TRUE(v.is<bool>());
+
+ v.Become(2);
+ EXPECT_TRUE(v.is<float>());
+
+ v.Become(3);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-1);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-2);
+ EXPECT_TRUE(v.empty());
+ }
+
+ {
+ Variant<int, bool, float> v;
+
+ v.Become(0, 10);
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(10, std::get<int>(v));
+
+ v.Become(1, true);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+
+ v.Become(2, 2.0f);
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(2.0f, std::get<float>(v));
+
+ v.Become(3, 10);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-1, 10);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-2, 20);
+ EXPECT_TRUE(v.empty());
+ }
+
+ {
+ Variant<std::string> v;
+
+ v.Become(0);
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ }
+
+ {
+ Variant<std::string> v;
+
+ v.Become(0, "test");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<std::string> v("foo");
+
+ v.Become(0, "bar");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("foo", std::get<std::string>(v));
+ }
+}
+
+TEST(Variant, Swap) {
+ {
+ Variant<std::string> a;
+ Variant<std::string> b;
+
+ std::swap(a, b);
+ EXPECT_TRUE(a.empty());
+ EXPECT_TRUE(b.empty());
+ }
+
+ {
+ Variant<std::string> a("1");
+ Variant<std::string> b;
+
+ std::swap(a, b);
+ EXPECT_TRUE(!a.empty());
+ EXPECT_TRUE(!b.empty());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+
+ {
+ Variant<std::string> a;
+ Variant<std::string> b("1");
+
+ std::swap(a, b);
+ EXPECT_TRUE(!a.empty());
+ EXPECT_TRUE(!b.empty());
+ ASSERT_TRUE(a.is<std::string>());
+ EXPECT_EQ("1", std::get<std::string>(a));
+ }
+
+ {
+ Variant<std::string> a("1");
+ Variant<std::string> b("2");
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ("2", std::get<std::string>(a));
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a(10);
+ Variant<int, std::string> b("1");
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<int>());
+ EXPECT_EQ("1", std::get<std::string>(a));
+ EXPECT_EQ(10, std::get<int>(b));
+ }
+
+ {
+ Variant<int, std::string> a("1");
+ Variant<int, std::string> b(10);
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<int>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ(10, std::get<int>(a));
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+}
+
+TEST(Variant, Get) {
+ {
+ Variant<int, bool, float, int> v;
+
+ EXPECT_EQ(nullptr, &std::get<int>(v));
+ EXPECT_EQ(nullptr, &std::get<bool>(v));
+ EXPECT_EQ(nullptr, &std::get<float>(v));
+ EXPECT_EQ(nullptr, &std::get<0>(v));
+ EXPECT_EQ(nullptr, &std::get<1>(v));
+ EXPECT_EQ(nullptr, &std::get<2>(v));
+ EXPECT_EQ(nullptr, &std::get<3>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = 9;
+ ASSERT_TRUE(v.is<int>())
+ << "Expected type " << v.index_of<int>() << " got type " << v.index();
+ EXPECT_EQ(9, std::get<int>(v));
+ EXPECT_EQ(9, std::get<0>(v));
+
+ std::get<int>(v) = 10;
+ EXPECT_EQ(10, std::get<int>(v));
+ EXPECT_EQ(10, std::get<0>(v));
+
+ std::get<0>(v) = 11;
+ EXPECT_EQ(11, std::get<int>(v));
+ EXPECT_EQ(11, std::get<0>(v));
+
+ std::get<3>(v) = 12;
+ EXPECT_EQ(12, std::get<int>(v));
+ EXPECT_EQ(12, std::get<3>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = false;
+ ASSERT_TRUE(v.is<bool>())
+ << "Expected type " << v.index_of<bool>() << " got type " << v.index();
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+
+ std::get<bool>(v) = true;
+ EXPECT_EQ(true, std::get<bool>(v));
+ EXPECT_EQ(true, std::get<1>(v));
+
+ std::get<bool>(v) = false;
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+
+ std::get<1>(v) = true;
+ EXPECT_EQ(true, std::get<bool>(v));
+ EXPECT_EQ(true, std::get<1>(v));
+
+ std::get<1>(v) = false;
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = 1.0f;
+ ASSERT_TRUE(v.is<float>())
+ << "Expected type " << v.index_of<float>() << " got type " << v.index();
+ EXPECT_EQ(2, v.index());
+ EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.0, std::get<2>(v));
+
+ std::get<float>(v) = 1.1;
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+ std::get<float>(v) = -3.0;
+ EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+
+ std::get<2>(v) = 1.1;
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+ std::get<2>(v) = -3.0;
+ EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+ }
+
+ {
+ Variant<std::unique_ptr<int>> v(std::make_unique<int>(10));
+ std::unique_ptr<int> pointer = std::move(std::get<std::unique_ptr<int>>(v));
+ ASSERT_FALSE(v.empty());
+ EXPECT_TRUE(pointer != nullptr);
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ }
+
+ {
+ Variant<std::string> v("test");
+ std::string s = std::get<std::string>(std::move(v));
+ EXPECT_EQ("test", s);
+ }
+}
+
+TEST(Variant, IfAnyOf) {
+ {
+ Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ const Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Call(&v, [&b](const auto& value) { b = value; }));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((
+ IfAnyOf<int, float>::Call(&v, [&f](const auto& value) { f = value; })));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+ std::unique_ptr<int> u(std::make_unique<int>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Take(&v, &u));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<std::unique_ptr<DerivedType>, int> v(
+ std::make_unique<DerivedType>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+ const DerivedType* original_v =
+ std::get<std::unique_ptr<DerivedType>>(v).get();
+
+ std::unique_ptr<BaseType> u(std::make_unique<BaseType>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<DerivedType>>::Take(&v, &u));
+ ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<DerivedType>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+ std::unique_ptr<int> u(std::make_unique<int>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Call(
+ &v, [&u](auto&& value) { u = std::move(value); }));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<int, bool, float> v(true);
+ ASSERT_TRUE(v.is<bool>());
+
+ float f = 0.f;
+ EXPECT_FALSE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(0.f, f);
+ }
+
+ {
+ Variant<std::string, int> v("foo");
+ ASSERT_TRUE(v.is<std::string>());
+
+ std::string s = "bar";
+ EXPECT_TRUE(IfAnyOf<std::string>::Swap(&v, &s));
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("bar", std::get<std::string>(v));
+ EXPECT_EQ("foo", s);
+ }
+
+ {
+ Variant<std::string, const char*> v(static_cast<const char*>("foo"));
+ ASSERT_TRUE(v.is<const char*>());
+
+ std::string s = "bar";
+ EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ ASSERT_TRUE(v.is<const char*>());
+ EXPECT_EQ("foo", std::get<const char*>(v));
+ EXPECT_EQ("foo", s);
+
+ v = std::string("bar");
+ ASSERT_TRUE(v.is<std::string>());
+
+ EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("bar", s);
+ }
+
+ {
+ Variant<std::string, const char*> v;
+ ASSERT_TRUE(v.empty());
+
+ std::string s = "bar";
+ EXPECT_FALSE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ EXPECT_EQ("bar", s);
+ }
+
+ {
+ Variant<std::string, const char*> v(static_cast<const char*>("test"));
+ ASSERT_TRUE(v.is<const char*>());
+
+ std::string s;
+ EXPECT_FALSE(IfAnyOf<>::Take(&v, &s));
+ EXPECT_TRUE(s.empty());
+ }
+}
+
+TEST(Variant, ConstVolatile) {
+ {
+ Variant<const int> v(10);
+ ASSERT_TRUE(v.is<const int>());
+ EXPECT_EQ(10, std::get<const int>(v));
+ }
+
+ {
+ Variant<const std::string> v("test");
+ ASSERT_TRUE(v.is<const std::string>());
+ EXPECT_EQ("test", std::get<const std::string>(v));
+ }
+
+ {
+ Variant<volatile int, std::string> v(10);
+ ASSERT_TRUE(v.is<volatile int>());
+ EXPECT_EQ(10, std::get<volatile int>(v));
+ }
+}
+
+TEST(Variant, HasType) {
+ EXPECT_TRUE((detail::HasType<int, int, float, bool>::value));
+ EXPECT_FALSE((detail::HasType<char, int, float, bool>::value));
+ EXPECT_FALSE(detail::HasType<>::value);
+
+ EXPECT_TRUE((detail::HasTypeIgnoreRef<int&, int, float, bool>::value));
+ EXPECT_FALSE((detail::HasTypeIgnoreRef<char&, int, float, bool>::value));
+}
+
+TEST(Variant, Set) {
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<int, bool,
+ float>::value));
+ EXPECT_TRUE(
+ (detail::Set<int, bool, float>::template IsSubset<bool, float>::value));
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<float>::value));
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<>::value));
+
+ EXPECT_FALSE(
+ (detail::Set<int, bool, float>::template IsSubset<int, bool, float,
+ char>::value));
+ EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<bool, float,
+ char>::value));
+ EXPECT_FALSE(
+ (detail::Set<int, bool, float>::template IsSubset<float, char>::value));
+ EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<char>::value));
+
+ EXPECT_TRUE(detail::Set<>::template IsSubset<>::value);
+ EXPECT_FALSE(detail::Set<>::template IsSubset<int>::value);
+ EXPECT_FALSE((detail::Set<>::template IsSubset<int, float>::value));
+}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
new file mode 100644
index 0000000..655adb8
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -0,0 +1,69 @@
+cc_defaults {
+ name: "pdx_default_transport_compiler_defaults",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
+
+cc_defaults {
+ name: "pdx_default_transport_lib_defaults",
+ export_include_dirs: ["private"],
+ whole_static_libs: ["libpdx"],
+}
+
+cc_defaults {
+ name: "pdx_use_transport_servicefs",
+ export_include_dirs: ["private/servicefs"],
+ whole_static_libs: ["libpdx_servicefs", "libservicefs"],
+}
+
+cc_defaults {
+ name: "pdx_use_transport_uds",
+ export_include_dirs: ["private/uds"],
+ whole_static_libs: ["libpdx_uds"],
+}
+
+cc_library_static {
+ name: "libpdx_default_transport",
+ defaults: [
+ "pdx_default_transport_compiler_defaults",
+ "pdx_default_transport_lib_defaults",
+ "pdx_use_transport_uds",
+ ],
+}
+
+cc_binary {
+ name: "servicetool",
+ defaults: ["pdx_default_transport_compiler_defaults"],
+ srcs: [
+ "servicetool.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ static_libs: [
+ "libpdx_default_transport",
+ ],
+}
+
+// Benchmarks.
+cc_binary {
+ name: "pdx_benchmarks",
+ defaults: ["pdx_default_transport_compiler_defaults"],
+ srcs: [
+ "pdx_benchmarks.cpp",
+ ],
+ shared_libs: [
+ "libchrome",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libpdx_default_transport",
+ ],
+}
+
diff --git a/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
new file mode 100644
index 0000000..de02401
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
@@ -0,0 +1,1098 @@
+// Use ALWAYS at the tag level. Control is performed manually during command
+// line processing.
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <utils/Trace.h>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/service.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <cstdlib>
+#include <functional>
+#include <future>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+using android::pdx::Channel;
+using android::pdx::ClientBase;
+using android::pdx::Endpoint;
+using android::pdx::Message;
+using android::pdx::Service;
+using android::pdx::ServiceBase;
+using android::pdx::default_transport::ClientChannelFactory;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::BufferWrapper;
+using android::pdx::rpc::DefaultInitializationAllocator;
+using android::pdx::rpc::MessageBuffer;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::RemoteMethodReturn;
+using android::pdx::rpc::ReplyBuffer;
+using android::pdx::rpc::Void;
+using android::pdx::rpc::WrapBuffer;
+
+namespace {
+
+constexpr size_t kMaxMessageSize = 4096 * 1024;
+
+std::string GetServicePath(const std::string& path, int instance_id) {
+ return path + std::to_string(instance_id);
+}
+
+void SetThreadName(const std::string& name) {
+ prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0);
+}
+
+constexpr uint64_t kNanosPerSecond = 1000000000llu;
+
+uint64_t GetClockNs() {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return kNanosPerSecond * t.tv_sec + t.tv_nsec;
+}
+
+template <typename T>
+ssize_t ssizeof(const T&) {
+ return static_cast<ssize_t>(sizeof(T));
+}
+
+class SchedStats {
+ public:
+ SchedStats() : SchedStats(gettid()) {}
+ SchedStats(pid_t task_id) : task_id_(task_id) {}
+ SchedStats(const SchedStats&) = default;
+ SchedStats& operator=(const SchedStats&) = default;
+
+ void Update() {
+ const std::string stats_path =
+ "/proc/" + std::to_string(task_id_) + "/schedstat";
+
+ std::string line;
+ base::ReadFileToString(base::FilePath{stats_path}, &line);
+ std::vector<std::string> stats = base::SplitString(
+ line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ CHECK_EQ(3u, stats.size());
+
+ // Calculate the deltas since the last update. Each value is absolute since
+ // the task started.
+ uint64_t current_cpu_time_ns = std::stoull(stats[0]);
+ uint64_t current_wait_ns = std::stoull(stats[1]);
+ uint64_t current_timeslices = std::stoull(stats[2]);
+ cpu_time_ns_ = current_cpu_time_ns - last_cpu_time_ns_;
+ wait_ns_ = current_wait_ns - last_wait_ns_;
+ timeslices_ = current_timeslices - last_timeslices_;
+ last_cpu_time_ns_ = current_cpu_time_ns;
+ last_wait_ns_ = current_wait_ns;
+ last_timeslices_ = current_timeslices;
+ }
+
+ pid_t task_id() const { return task_id_; }
+ uint64_t cpu_time_ns() const { return cpu_time_ns_; }
+ uint64_t wait_ns() const { return wait_ns_; }
+ uint64_t timeslices() const { return timeslices_; }
+
+ double cpu_time_s() const {
+ return static_cast<double>(cpu_time_ns_) / kNanosPerSecond;
+ }
+ double wait_s() const {
+ return static_cast<double>(wait_ns_) / kNanosPerSecond;
+ }
+
+ private:
+ int32_t task_id_;
+ uint64_t cpu_time_ns_ = 0;
+ uint64_t last_cpu_time_ns_ = 0;
+ uint64_t wait_ns_ = 0;
+ uint64_t last_wait_ns_ = 0;
+ uint64_t timeslices_ = 0;
+ uint64_t last_timeslices_ = 0;
+
+ PDX_SERIALIZABLE_MEMBERS(SchedStats, task_id_, cpu_time_ns_, wait_ns_,
+ timeslices_);
+};
+
+// Opcodes for client/service protocol.
+struct BenchmarkOps {
+ enum : int {
+ Nop,
+ Read,
+ Write,
+ Echo,
+ Stats,
+ WriteVector,
+ EchoVector,
+ Quit,
+ };
+};
+
+struct BenchmarkRPC {
+ PDX_REMOTE_METHOD(Stats, BenchmarkOps::Stats,
+ std::tuple<uint64_t, uint64_t, SchedStats>(Void));
+ PDX_REMOTE_METHOD(WriteVector, BenchmarkOps::WriteVector,
+ int(const BufferWrapper<std::vector<uint8_t>> data));
+ PDX_REMOTE_METHOD(EchoVector, BenchmarkOps::EchoVector,
+ BufferWrapper<std::vector<uint8_t>>(
+ const BufferWrapper<std::vector<uint8_t>> data));
+};
+
+struct BenchmarkResult {
+ int thread_id = 0;
+ int service_id = 0;
+ double time_delta_s = 0.0;
+ uint64_t bytes_sent = 0;
+ SchedStats sched_stats = {};
+};
+
+// Global command line option values.
+struct Options {
+ bool verbose = false;
+ int threads = 1;
+ int opcode = BenchmarkOps::Read;
+ int blocksize = 1;
+ int count = 1;
+ int instances = 1;
+ int timeout = 1;
+ int warmup = 0;
+} ProgramOptions;
+
+// Command line option names.
+const char kOptionService[] = "service";
+const char kOptionClient[] = "client";
+const char kOptionVerbose[] = "verbose";
+const char kOptionOpcode[] = "op";
+const char kOptionBlocksize[] = "bs";
+const char kOptionCount[] = "count";
+const char kOptionThreads[] = "threads";
+const char kOptionInstances[] = "instances";
+const char kOptionTimeout[] = "timeout";
+const char kOptionTrace[] = "trace";
+const char kOptionWarmup[] = "warmup";
+
+// getopt() long options.
+static option long_options[] = {
+ {kOptionService, required_argument, 0, 0},
+ {kOptionClient, required_argument, 0, 0},
+ {kOptionVerbose, no_argument, 0, 0},
+ {kOptionOpcode, required_argument, 0, 0},
+ {kOptionBlocksize, required_argument, 0, 0},
+ {kOptionCount, required_argument, 0, 0},
+ {kOptionThreads, required_argument, 0, 0},
+ {kOptionInstances, required_argument, 0, 0},
+ {kOptionTimeout, required_argument, 0, 0},
+ {kOptionTrace, no_argument, 0, 0},
+ {kOptionWarmup, required_argument, 0, 0},
+ {0, 0, 0, 0},
+};
+
+// Parses the argument for kOptionOpcode and sets the value of
+// ProgramOptions.opcode.
+void ParseOpcodeOption(const std::string& argument) {
+ if (argument == "read") {
+ ProgramOptions.opcode = BenchmarkOps::Read;
+ } else if (argument == "write") {
+ ProgramOptions.opcode = BenchmarkOps::Write;
+ } else if (argument == "echo") {
+ ProgramOptions.opcode = BenchmarkOps::Echo;
+ } else if (argument == "writevec") {
+ ProgramOptions.opcode = BenchmarkOps::WriteVector;
+ } else if (argument == "echovec") {
+ ProgramOptions.opcode = BenchmarkOps::EchoVector;
+ } else if (argument == "quit") {
+ ProgramOptions.opcode = BenchmarkOps::Quit;
+ } else if (argument == "nop") {
+ ProgramOptions.opcode = BenchmarkOps::Nop;
+ } else if (argument == "stats") {
+ ProgramOptions.opcode = BenchmarkOps::Stats;
+ } else {
+ ProgramOptions.opcode = std::stoi(argument);
+ }
+}
+
+// Implements the service side of the benchmark.
+class BenchmarkService : public ServiceBase<BenchmarkService> {
+ public:
+ std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
+ VLOG(1) << "BenchmarkService::OnChannelCreate: cid="
+ << message.GetChannelId();
+ return nullptr;
+ }
+
+ void OnChannelClose(Message& message,
+ const std::shared_ptr<Channel>& /*channel*/) override {
+ VLOG(1) << "BenchmarkService::OnChannelClose: cid="
+ << message.GetChannelId();
+ }
+
+ int HandleMessage(Message& message) override {
+ ATRACE_NAME("BenchmarkService::HandleMessage");
+
+ switch (message.GetOp()) {
+ case BenchmarkOps::Nop:
+ VLOG(1) << "BenchmarkService::HandleMessage: op=nop";
+ {
+ ATRACE_NAME("Reply");
+ CHECK(message.Reply(0) == 0);
+ }
+ return 0;
+
+ case BenchmarkOps::Write: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=write send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ const ssize_t expected_length =
+ static_cast<ssize_t>(message.GetSendLength());
+ const ssize_t actual_length =
+ expected_length > 0
+ ? message.Read(send_buffer.data(), message.GetSendLength())
+ : 0;
+
+ {
+ ATRACE_NAME("Reply");
+ if (actual_length < expected_length)
+ CHECK(message.ReplyError(EIO) == 0);
+ else
+ CHECK(message.Reply(actual_length) == 0);
+ }
+ return 0;
+ }
+
+ case BenchmarkOps::Read: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=read send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ const ssize_t expected_length =
+ static_cast<ssize_t>(message.GetReceiveLength());
+ const ssize_t actual_length =
+ expected_length > 0
+ ? message.Write(receive_buffer.data(),
+ message.GetReceiveLength())
+ : 0;
+
+ {
+ ATRACE_NAME("Reply");
+ if (actual_length < expected_length)
+ CHECK(message.ReplyError(EIO) == 0);
+ else
+ CHECK(message.Reply(actual_length) == 0);
+ }
+ return 0;
+ }
+
+ case BenchmarkOps::Echo: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ const ssize_t expected_length =
+ static_cast<ssize_t>(message.GetSendLength());
+ ssize_t actual_length =
+ expected_length > 0
+ ? message.Read(send_buffer.data(), message.GetSendLength())
+ : 0;
+
+ if (actual_length < expected_length) {
+ CHECK(message.ReplyError(EIO) == 0);
+ return 0;
+ }
+
+ actual_length =
+ expected_length > 0
+ ? message.Write(send_buffer.data(), message.GetSendLength())
+ : 0;
+
+ {
+ ATRACE_NAME("Reply");
+ if (actual_length < expected_length)
+ CHECK(message.ReplyError(EIO) == 0);
+ else
+ CHECK(message.Reply(actual_length) == 0);
+ }
+ return 0;
+ }
+
+ case BenchmarkOps::Stats: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ // Snapshot the stats when the message is received.
+ const uint64_t receive_time_ns = GetClockNs();
+ sched_stats_.Update();
+
+ // Use the RPC system to return the results.
+ RemoteMethodReturn<BenchmarkRPC::Stats>(
+ message, BenchmarkRPC::Stats::Return{receive_time_ns, GetClockNs(),
+ sched_stats_});
+ return 0;
+ }
+
+ case BenchmarkOps::WriteVector:
+ VLOG(1) << "BenchmarkService::HandleMessage: op=writevec send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ DispatchRemoteMethod<BenchmarkRPC::WriteVector>(
+ *this, &BenchmarkService::OnWriteVector, message, kMaxMessageSize);
+ return 0;
+
+ case BenchmarkOps::EchoVector:
+ VLOG(1) << "BenchmarkService::HandleMessage: op=echovec send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ DispatchRemoteMethod<BenchmarkRPC::EchoVector>(
+ *this, &BenchmarkService::OnEchoVector, message, kMaxMessageSize);
+ return 0;
+
+ case BenchmarkOps::Quit:
+ Cancel();
+ return -ESHUTDOWN;
+
+ default:
+ VLOG(1) << "BenchmarkService::HandleMessage: default case; op="
+ << message.GetOp();
+ return Service::DefaultHandleMessage(message);
+ }
+ }
+
+ // Updates the scheduler stats from procfs for this thread.
+ void UpdateSchedStats() { sched_stats_.Update(); }
+
+ private:
+ friend BASE;
+
+ BenchmarkService(std::unique_ptr<Endpoint> endpoint)
+ : BASE("BenchmarkService", std::move(endpoint)),
+ send_buffer(kMaxMessageSize),
+ receive_buffer(kMaxMessageSize) {}
+
+ std::vector<uint8_t> send_buffer;
+ std::vector<uint8_t> receive_buffer;
+
+ // Each service thread has its own scheduler stats object.
+ static thread_local SchedStats sched_stats_;
+
+ using BufferType = BufferWrapper<
+ std::vector<uint8_t, DefaultInitializationAllocator<uint8_t>>>;
+
+ int OnWriteVector(Message&, const BufferType& data) { return data.size(); }
+ BufferType OnEchoVector(Message&, BufferType&& data) {
+ return std::move(data);
+ }
+
+ BenchmarkService(const BenchmarkService&) = delete;
+ void operator=(const BenchmarkService&) = delete;
+};
+
+thread_local SchedStats BenchmarkService::sched_stats_;
+
+// Implements the client side of the benchmark.
+class BenchmarkClient : public ClientBase<BenchmarkClient> {
+ public:
+ int Nop() {
+ ATRACE_NAME("BenchmarkClient::Nop");
+ VLOG(1) << "BenchmarkClient::Nop";
+ Transaction transaction{*this};
+ return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Nop));
+ }
+
+ int Write(const void* buffer, size_t length) {
+ ATRACE_NAME("BenchmarkClient::Write");
+ VLOG(1) << "BenchmarkClient::Write: buffer=" << buffer
+ << " length=" << length;
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(BenchmarkOps::Write, buffer, length, nullptr, 0));
+ // return write(endpoint_fd(), buffer, length);
+ }
+
+ int Read(void* buffer, size_t length) {
+ ATRACE_NAME("BenchmarkClient::Read");
+ VLOG(1) << "BenchmarkClient::Read: buffer=" << buffer
+ << " length=" << length;
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(BenchmarkOps::Read, nullptr, 0, buffer, length));
+ // return read(endpoint_fd(), buffer, length);
+ }
+
+ int Echo(const void* send_buffer, size_t send_length, void* receive_buffer,
+ size_t receive_length) {
+ ATRACE_NAME("BenchmarkClient::Echo");
+ VLOG(1) << "BenchmarkClient::Echo: send_buffer=" << send_buffer
+ << " send_length=" << send_length
+ << " receive_buffer=" << receive_buffer
+ << " receive_length=" << receive_length;
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(BenchmarkOps::Echo, send_buffer, send_length,
+ receive_buffer, receive_length));
+ }
+
+ int Stats(std::tuple<uint64_t, uint64_t, SchedStats>* stats_out) {
+ ATRACE_NAME("BenchmarkClient::Stats");
+ VLOG(1) << "BenchmarkClient::Stats";
+
+ auto status = InvokeRemoteMethodInPlace<BenchmarkRPC::Stats>(stats_out);
+ return status ? 0 : -status.error();
+ }
+
+ int WriteVector(const BufferWrapper<std::vector<uint8_t>>& data) {
+ ATRACE_NAME("BenchmarkClient::Stats");
+ VLOG(1) << "BenchmarkClient::Stats";
+
+ auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data);
+ return ReturnStatusOrError(status);
+ }
+
+ template <typename T>
+ int WriteVector(const BufferWrapper<T>& data) {
+ ATRACE_NAME("BenchmarkClient::WriteVector");
+ VLOG(1) << "BenchmarkClient::WriteVector";
+
+ auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data);
+ return ReturnStatusOrError(status);
+ }
+
+ template <typename T, typename U>
+ int EchoVector(const BufferWrapper<T>& data, BufferWrapper<U>* data_out) {
+ ATRACE_NAME("BenchmarkClient::EchoVector");
+ VLOG(1) << "BenchmarkClient::EchoVector";
+
+ MessageBuffer<ReplyBuffer>::Reserve(kMaxMessageSize - 1);
+ auto status =
+ InvokeRemoteMethodInPlace<BenchmarkRPC::EchoVector>(data_out, data);
+ return status ? 0 : -status.error();
+ }
+
+ int Quit() {
+ VLOG(1) << "BenchmarkClient::Quit";
+ Transaction transaction{*this};
+ return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Echo));
+ }
+
+ private:
+ friend BASE;
+
+ BenchmarkClient(const std::string& service_path)
+ : BASE(ClientChannelFactory::Create(service_path),
+ ProgramOptions.timeout) {}
+
+ BenchmarkClient(const BenchmarkClient&) = delete;
+ void operator=(const BenchmarkClient&) = delete;
+};
+
+// Creates a benchmark service at |path| and dispatches messages.
+int ServiceCommand(const std::string& path) {
+ if (path.empty())
+ return -EINVAL;
+
+ // Start the requested number of dispatch threads.
+ std::vector<std::thread> dispatch_threads;
+ int service_count = ProgramOptions.instances;
+ int service_id_counter = 0;
+ int thread_id_counter = 0;
+ std::atomic<bool> done(false);
+
+ while (service_count--) {
+ std::cerr << "Starting service instance " << service_id_counter
+ << std::endl;
+ auto service = BenchmarkService::Create(
+ android::pdx::default_transport::Endpoint::Create(
+ GetServicePath(path, service_id_counter),
+ android::pdx::default_transport::Endpoint::kDefaultMode,
+ android::pdx::default_transport::Endpoint::kBlocking));
+ if (!service) {
+ std::cerr << "Failed to create service instance!!" << std::endl;
+ done = true;
+ break;
+ }
+
+ int thread_count = ProgramOptions.threads;
+ while (thread_count--) {
+ std::cerr << "Starting dispatch thread " << thread_id_counter
+ << " service " << service_id_counter << std::endl;
+
+ dispatch_threads.emplace_back(
+ [&](const int thread_id, const int service_id,
+ const std::shared_ptr<BenchmarkService>& local_service) {
+ SetThreadName("service" + std::to_string(service_id));
+
+ // Read the inital schedstats for this thread from procfs.
+ local_service->UpdateSchedStats();
+
+ ATRACE_NAME("BenchmarkService::Dispatch");
+ while (!done) {
+ const int ret = local_service->ReceiveAndDispatch();
+ if (ret < 0) {
+ if (ret != -ESHUTDOWN) {
+ std::cerr << "Error while dispatching message on thread "
+ << thread_id << " service " << service_id << ": "
+ << strerror(-ret) << std::endl;
+ } else {
+ std::cerr << "Quitting thread " << thread_id << " service "
+ << service_id << std::endl;
+ }
+ done = true;
+ return;
+ }
+ }
+ },
+ thread_id_counter++, service_id_counter, service);
+ }
+
+ service_id_counter++;
+ }
+
+ // Wait for the dispatch threads to exit.
+ for (auto& thread : dispatch_threads) {
+ thread.join();
+ }
+
+ return 0;
+}
+
+int ClientCommand(const std::string& path) {
+ // Start the requested number of client threads.
+ std::vector<std::thread> client_threads;
+ std::vector<std::future<BenchmarkResult>> client_results;
+ int service_count = ProgramOptions.instances;
+ int thread_id_counter = 0;
+ int service_id_counter = 0;
+
+ // Aggregate statistics, updated when worker threads exit.
+ std::atomic<uint64_t> total_bytes(0);
+ std::atomic<uint64_t> total_time_ns(0);
+
+ // Samples for variance calculation.
+ std::vector<uint64_t> latency_samples_ns(
+ ProgramOptions.instances * ProgramOptions.threads * ProgramOptions.count);
+ const size_t samples_per_thread = ProgramOptions.count;
+
+ std::vector<uint8_t> send_buffer(ProgramOptions.blocksize);
+ std::vector<uint8_t> receive_buffer(kMaxMessageSize);
+
+ // Barriers for synchronizing thread start.
+ std::vector<std::future<void>> ready_barrier_futures;
+ std::promise<void> go_barrier_promise;
+ std::future<void> go_barrier_future = go_barrier_promise.get_future();
+
+ // Barrier for synchronizing thread tear down.
+ std::promise<void> done_barrier_promise;
+ std::future<void> done_barrier_future = done_barrier_promise.get_future();
+
+ while (service_count--) {
+ int thread_count = ProgramOptions.threads;
+ while (thread_count--) {
+ std::cerr << "Starting client thread " << thread_id_counter << " service "
+ << service_id_counter << std::endl;
+
+ std::promise<BenchmarkResult> result_promise;
+ client_results.push_back(result_promise.get_future());
+
+ std::promise<void> ready_barrier_promise;
+ ready_barrier_futures.push_back(ready_barrier_promise.get_future());
+
+ client_threads.emplace_back(
+ [&](const int thread_id, const int service_id,
+ std::promise<BenchmarkResult> result, std::promise<void> ready) {
+ SetThreadName("client" + std::to_string(thread_id) + "/" +
+ std::to_string(service_id));
+
+ ATRACE_NAME("BenchmarkClient::Dispatch");
+
+ auto client =
+ BenchmarkClient::Create(GetServicePath(path, service_id));
+ if (!client) {
+ std::cerr << "Failed to create client for service " << service_id
+ << std::endl;
+ return -ENOMEM;
+ }
+
+ uint64_t* thread_samples =
+ &latency_samples_ns[samples_per_thread * thread_id];
+
+ // Per-thread statistics.
+ uint64_t bytes_sent = 0;
+ uint64_t time_start_ns;
+ uint64_t time_end_ns;
+ SchedStats sched_stats;
+
+ // Signal ready and wait for go.
+ ready.set_value();
+ go_barrier_future.wait();
+
+ // Warmup the scheduler.
+ int warmup = ProgramOptions.warmup;
+ while (warmup--) {
+ for (int i = 0; i < 1000000; i++)
+ ;
+ }
+
+ sched_stats.Update();
+ time_start_ns = GetClockNs();
+
+ int count = ProgramOptions.count;
+ while (count--) {
+ uint64_t iteration_start_ns = GetClockNs();
+
+ switch (ProgramOptions.opcode) {
+ case BenchmarkOps::Nop: {
+ const int ret = client->Nop();
+ if (ret < 0) {
+ std::cerr << "Failed to send nop: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ }
+ break;
+ }
+
+ case BenchmarkOps::Read: {
+ const int ret = client->Read(receive_buffer.data(),
+ ProgramOptions.blocksize);
+ if (ret < 0) {
+ std::cerr << "Failed to read: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else if (ret != ProgramOptions.blocksize) {
+ std::cerr << "Expected ret=" << ProgramOptions.blocksize
+ << "; actual ret=" << ret << std::endl;
+ return -EINVAL;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret;
+ }
+ break;
+ }
+
+ case BenchmarkOps::Write: {
+ const int ret =
+ client->Write(send_buffer.data(), send_buffer.size());
+ if (ret < 0) {
+ std::cerr << "Failed to write: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else if (ret != ProgramOptions.blocksize) {
+ std::cerr << "Expected ret=" << ProgramOptions.blocksize
+ << "; actual ret=" << ret << std::endl;
+ return -EINVAL;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret;
+ }
+ break;
+ }
+
+ case BenchmarkOps::Echo: {
+ const int ret = client->Echo(
+ send_buffer.data(), send_buffer.size(),
+ receive_buffer.data(), receive_buffer.size());
+ if (ret < 0) {
+ std::cerr << "Failed to echo: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else if (ret != ProgramOptions.blocksize) {
+ std::cerr << "Expected ret=" << ProgramOptions.blocksize
+ << "; actual ret=" << ret << std::endl;
+ return -EINVAL;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret * 2;
+ }
+ break;
+ }
+
+ case BenchmarkOps::Stats: {
+ std::tuple<uint64_t, uint64_t, SchedStats> stats;
+ const int ret = client->Stats(&stats);
+ if (ret < 0) {
+ std::cerr << "Failed to get stats: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ std::cerr
+ << "Round trip: receive_time_ns=" << std::get<0>(stats)
+ << " reply_time_ns=" << std::get<1>(stats)
+ << " cpu_time_s=" << std::get<2>(stats).cpu_time_s()
+ << " wait_s=" << std::get<2>(stats).wait_s()
+ << std::endl;
+ }
+ break;
+ }
+
+ case BenchmarkOps::WriteVector: {
+ const int ret = client->WriteVector(
+ WrapBuffer(send_buffer.data(), ProgramOptions.blocksize));
+ if (ret < 0) {
+ std::cerr << "Failed to write vector: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret;
+ }
+ break;
+ }
+
+ case BenchmarkOps::EchoVector: {
+ thread_local BufferWrapper<std::vector<
+ uint8_t, DefaultInitializationAllocator<uint8_t>>>
+ response_buffer;
+ const int ret = client->EchoVector(
+ WrapBuffer(send_buffer.data(), ProgramOptions.blocksize),
+ &response_buffer);
+ if (ret < 0) {
+ std::cerr << "Failed to echo vector: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += send_buffer.size() + response_buffer.size();
+ }
+ break;
+ }
+
+ case BenchmarkOps::Quit: {
+ const int ret = client->Quit();
+ if (ret < 0 && ret != -ESHUTDOWN) {
+ std::cerr << "Failed to send quit: " << strerror(-ret);
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ }
+ break;
+ }
+
+ default:
+ std::cerr
+ << "Invalid client operation: " << ProgramOptions.opcode
+ << std::endl;
+ return -EINVAL;
+ }
+
+ uint64_t iteration_end_ns = GetClockNs();
+ uint64_t iteration_delta_ns =
+ iteration_end_ns - iteration_start_ns;
+ thread_samples[count] = iteration_delta_ns;
+
+ if (iteration_delta_ns > (kNanosPerSecond / 100)) {
+ SchedStats stats = sched_stats;
+ stats.Update();
+ std::cerr << "Thread " << thread_id << " iteration_delta_s="
+ << (static_cast<double>(iteration_delta_ns) /
+ kNanosPerSecond)
+ << " " << stats.cpu_time_s() << " " << stats.wait_s()
+ << std::endl;
+ }
+ }
+
+ time_end_ns = GetClockNs();
+ sched_stats.Update();
+
+ const double time_delta_s =
+ static_cast<double>(time_end_ns - time_start_ns) /
+ kNanosPerSecond;
+
+ total_bytes += bytes_sent;
+ total_time_ns += time_end_ns - time_start_ns;
+
+ result.set_value(
+ {thread_id, service_id, time_delta_s, bytes_sent, sched_stats});
+ done_barrier_future.wait();
+
+ return 0;
+ },
+ thread_id_counter++, service_id_counter, std::move(result_promise),
+ std::move(ready_barrier_promise));
+ }
+
+ service_id_counter++;
+ }
+
+ // Wait for workers to be ready.
+ std::cerr << "Waiting for workers to be ready..." << std::endl;
+ for (auto& ready : ready_barrier_futures)
+ ready.wait();
+
+ // Signal workers to go.
+ std::cerr << "Kicking off benchmark." << std::endl;
+ go_barrier_promise.set_value();
+
+ // Wait for all the worker threas to finish.
+ for (auto& result : client_results)
+ result.wait();
+
+ // Report worker thread results.
+ for (auto& result : client_results) {
+ BenchmarkResult benchmark_result = result.get();
+ std::cerr << std::fixed << "Thread " << benchmark_result.thread_id
+ << " service " << benchmark_result.service_id << ":" << std::endl;
+ std::cerr << "\t " << benchmark_result.bytes_sent << " bytes in "
+ << benchmark_result.time_delta_s << " seconds ("
+ << std::setprecision(0) << (benchmark_result.bytes_sent / 1024.0 /
+ benchmark_result.time_delta_s)
+ << " K/s; " << std::setprecision(3)
+ << (ProgramOptions.count / benchmark_result.time_delta_s)
+ << " txn/s; " << std::setprecision(9)
+ << (benchmark_result.time_delta_s / ProgramOptions.count)
+ << " s/txn)" << std::endl;
+ std::cerr << "\tStats: " << benchmark_result.sched_stats.cpu_time_s() << " "
+ << (benchmark_result.sched_stats.cpu_time_s() /
+ ProgramOptions.count)
+ << " " << benchmark_result.sched_stats.wait_s() << " "
+ << (benchmark_result.sched_stats.wait_s() / ProgramOptions.count)
+ << " " << benchmark_result.sched_stats.timeslices() << std::endl;
+ }
+
+ // Signal worker threads to exit.
+ done_barrier_promise.set_value();
+
+ // Wait for the worker threads to exit.
+ for (auto& thread : client_threads) {
+ thread.join();
+ }
+
+ // Report aggregate results.
+ const int total_threads = ProgramOptions.threads * ProgramOptions.instances;
+ const int iterations = ProgramOptions.count;
+ const double total_time_s =
+ static_cast<double>(total_time_ns) / kNanosPerSecond;
+ // This is about how much wall time it took to completely transfer all the
+ // paylaods.
+ const double average_time_s = total_time_s / total_threads;
+
+ const uint64_t min_sample_time_ns =
+ *std::min_element(latency_samples_ns.begin(), latency_samples_ns.end());
+ const double min_sample_time_s =
+ static_cast<double>(min_sample_time_ns) / kNanosPerSecond;
+
+ const uint64_t max_sample_time_ns =
+ *std::max_element(latency_samples_ns.begin(), latency_samples_ns.end());
+ const double max_sample_time_s =
+ static_cast<double>(max_sample_time_ns) / kNanosPerSecond;
+
+ const double total_sample_time_s =
+ std::accumulate(latency_samples_ns.begin(), latency_samples_ns.end(), 0.0,
+ [](double s, uint64_t ns) {
+ return s + static_cast<double>(ns) / kNanosPerSecond;
+ });
+ const double average_sample_time_s =
+ total_sample_time_s / latency_samples_ns.size();
+
+ const double sum_of_squared_deviations = std::accumulate(
+ latency_samples_ns.begin(), latency_samples_ns.end(), 0.0,
+ [&](double s, uint64_t ns) {
+ const double delta =
+ static_cast<double>(ns) / kNanosPerSecond - average_sample_time_s;
+ return s + delta * delta;
+ });
+ const double variance = sum_of_squared_deviations / latency_samples_ns.size();
+ const double standard_deviation = std::sqrt(variance);
+
+ const int num_buckets = 200;
+ const uint64_t sample_range_ns = max_sample_time_ns - min_sample_time_ns;
+ const uint64_t ns_per_bucket = sample_range_ns / num_buckets;
+ std::array<uint64_t, num_buckets> sample_buckets = {{0}};
+
+ // Count samples in each bucket range.
+ for (uint64_t sample_ns : latency_samples_ns) {
+ sample_buckets[(sample_ns - min_sample_time_ns) / (ns_per_bucket + 1)] += 1;
+ }
+
+ // Calculate population percentiles.
+ const uint64_t percent_50 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.5);
+ const uint64_t percent_90 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.9);
+ const uint64_t percent_95 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.95);
+ const uint64_t percent_99 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.99);
+
+ uint64_t sample_count = 0;
+ double latency_50th_percentile_s, latency_90th_percentile_s,
+ latency_95th_percentile_s, latency_99th_percentile_s;
+ for (int i = 0; i < num_buckets; i++) {
+ // Report the midpoint of the bucket range as the value of the
+ // corresponding
+ // percentile.
+ const double bucket_midpoint_time_s =
+ (ns_per_bucket * i + 0.5 * ns_per_bucket + min_sample_time_ns) /
+ kNanosPerSecond;
+ if (sample_count < percent_50 &&
+ (sample_count + sample_buckets[i]) >= percent_50) {
+ latency_50th_percentile_s = bucket_midpoint_time_s;
+ }
+ if (sample_count < percent_90 &&
+ (sample_count + sample_buckets[i]) >= percent_90) {
+ latency_90th_percentile_s = bucket_midpoint_time_s;
+ }
+ if (sample_count < percent_95 &&
+ (sample_count + sample_buckets[i]) >= percent_95) {
+ latency_95th_percentile_s = bucket_midpoint_time_s;
+ }
+ if (sample_count < percent_99 &&
+ (sample_count + sample_buckets[i]) >= percent_99) {
+ latency_99th_percentile_s = bucket_midpoint_time_s;
+ }
+ sample_count += sample_buckets[i];
+ }
+
+ std::cerr << std::fixed << "Total throughput over " << total_threads
+ << " threads:\n\t " << total_bytes << " bytes in " << average_time_s
+ << " seconds (" << std::setprecision(0)
+ << (total_bytes / 1024.0 / average_time_s) << " K/s; "
+ << std::setprecision(3)
+ << (iterations * total_threads / average_time_s)
+ << std::setprecision(9) << " txn/s; "
+ << (average_time_s / (iterations * total_threads)) << " s/txn)"
+ << std::endl;
+ std::cerr << "Sample statistics: " << std::endl;
+ std::cerr << total_sample_time_s << " s total sample time" << std::endl;
+ std::cerr << average_sample_time_s << " s avg" << std::endl;
+ std::cerr << standard_deviation << " s std dev" << std::endl;
+ std::cerr << min_sample_time_s << " s min" << std::endl;
+ std::cerr << max_sample_time_s << " s max" << std::endl;
+ std::cerr << "Latency percentiles:" << std::endl;
+ std::cerr << "50th: " << latency_50th_percentile_s << " s" << std::endl;
+ std::cerr << "90th: " << latency_90th_percentile_s << " s" << std::endl;
+ std::cerr << "95th: " << latency_95th_percentile_s << " s" << std::endl;
+ std::cerr << "99th: " << latency_99th_percentile_s << " s" << std::endl;
+
+ std::cout << total_time_ns << " " << std::fixed << std::setprecision(9)
+ << average_sample_time_s << " " << std::fixed
+ << std::setprecision(9) << standard_deviation << std::endl;
+ return 0;
+}
+
+int Usage(const std::string& command_name) {
+ // clang-format off
+ std::cout << "Usage: " << command_name << " [options]" << std::endl;
+ std::cout << "\t--verbose : Use verbose messages." << std::endl;
+ std::cout << "\t--service <endpoint path> : Start service at the given path." << std::endl;
+ std::cout << "\t--client <endpoint path> : Start client to the given path." << std::endl;
+ std::cout << "\t--op <read | write | echo> : Sepcify client operation mode." << std::endl;
+ std::cout << "\t--bs <block size bytes> : Sepcify block size to use." << std::endl;
+ std::cout << "\t--count <count> : Sepcify number of transactions to make." << std::endl;
+ std::cout << "\t--instances <count> : Specify number of service instances." << std::endl;
+ std::cout << "\t--threads <count> : Sepcify number of threads per instance." << std::endl;
+ std::cout << "\t--timeout <timeout ms | -1> : Timeout to wait for services." << std::endl;
+ std::cout << "\t--trace : Enable systrace logging." << std::endl;
+ std::cout << "\t--warmup <iterations> : Busy loops before running benchmarks." << std::endl;
+ // clang-format on
+ return -1;
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ logging::LoggingSettings logging_settings;
+ logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(logging_settings);
+
+ int getopt_code;
+ int option_index;
+ std::string option = "";
+ std::string command = "";
+ std::string command_argument = "";
+ bool tracing_enabled = false;
+
+ // Process command line options.
+ while ((getopt_code =
+ getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ option = long_options[option_index].name;
+ VLOG(1) << "option=" << option;
+ switch (getopt_code) {
+ case 0:
+ if (option == kOptionVerbose) {
+ ProgramOptions.verbose = true;
+ logging::SetMinLogLevel(-1);
+ } else if (option == kOptionOpcode) {
+ ParseOpcodeOption(optarg);
+ } else if (option == kOptionBlocksize) {
+ ProgramOptions.blocksize = std::stoi(optarg);
+ if (ProgramOptions.blocksize < 0) {
+ std::cerr << "Invalid blocksize argument: "
+ << ProgramOptions.blocksize << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionCount) {
+ ProgramOptions.count = std::stoi(optarg);
+ if (ProgramOptions.count < 1) {
+ std::cerr << "Invalid count argument: " << ProgramOptions.count
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionThreads) {
+ ProgramOptions.threads = std::stoi(optarg);
+ if (ProgramOptions.threads < 1) {
+ std::cerr << "Invalid threads argument: " << ProgramOptions.threads
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionInstances) {
+ ProgramOptions.instances = std::stoi(optarg);
+ if (ProgramOptions.instances < 1) {
+ std::cerr << "Invalid instances argument: "
+ << ProgramOptions.instances << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionTimeout) {
+ ProgramOptions.timeout = std::stoi(optarg);
+ } else if (option == kOptionTrace) {
+ tracing_enabled = true;
+ } else if (option == kOptionWarmup) {
+ ProgramOptions.warmup = std::stoi(optarg);
+ } else {
+ command = option;
+ if (optarg)
+ command_argument = optarg;
+ }
+ break;
+ }
+ }
+
+ // Setup ATRACE/systrace based on command line.
+ atrace_setup();
+ atrace_set_tracing_enabled(tracing_enabled);
+
+ VLOG(1) << "command=" << command << " command_argument=" << command_argument;
+
+ if (command == "") {
+ return Usage(argv[0]);
+ } else if (command == kOptionService) {
+ return ServiceCommand(command_argument);
+ } else if (command == kOptionClient) {
+ return ClientCommand(command_argument);
+ } else {
+ return Usage(argv[0]);
+ }
+}
diff --git a/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
new file mode 100644
index 0000000..22c6b3f
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
@@ -0,0 +1,45 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/service.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+class ServiceUtility : public ClientBase<ServiceUtility> {
+ public:
+ Status<int> ReloadSystemProperties() {
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(opcodes::REPORT_SYSPROP_CHANGE));
+ }
+
+ static std::string GetRootEndpointPath() {
+ return ClientChannelFactory::GetRootEndpointPath();
+ }
+ static std::string GetEndpointPath(const std::string& endpoint_path) {
+ return ClientChannelFactory::GetEndpointPath(endpoint_path);
+ }
+
+ private:
+ friend BASE;
+
+ ServiceUtility(const std::string& endpoint_path, int* error = nullptr)
+ : BASE(ClientChannelFactory::Create(endpoint_path)) {
+ if (error)
+ *error = Client::error();
+ }
+
+ ServiceUtility(const ServiceUtility&) = delete;
+ void operator=(const ServiceUtility&) = delete;
+};
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h
new file mode 100644
index 0000000..11163b3
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
+
+#include <servicefs/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelManager = ::android::pdx::servicefs::ChannelManager;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h
new file mode 100644
index 0000000..d171780
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
+
+#include <servicefs/client_channel.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannel = ::android::pdx::servicefs::ClientChannel;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h
new file mode 100644
index 0000000..77b5cac
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <servicefs/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannelFactory = ::android::pdx::servicefs::ClientChannelFactory;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
new file mode 100644
index 0000000..158871c
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
+
+#include <servicefs/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ServiceDispatcher = ::android::pdx::servicefs::ServiceDispatcher;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h
new file mode 100644
index 0000000..8f413c1
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_
+
+#include <servicefs/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using Endpoint = ::android::pdx::servicefs::Endpoint;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h
new file mode 100644
index 0000000..f34636f
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
+
+#include <uds/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelManager = ::android::pdx::uds::ChannelManager;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h
new file mode 100644
index 0000000..bf632d7
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
+
+#include <uds/client_channel.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannel = ::android::pdx::uds::ClientChannel;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h
new file mode 100644
index 0000000..e5c4e30
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <uds/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannelFactory = ::android::pdx::uds::ClientChannelFactory;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
new file mode 100644
index 0000000..7cb7a80
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
+
+#include <uds/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ServiceDispatcher = ::android::pdx::uds::ServiceDispatcher;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h
new file mode 100644
index 0000000..1fd6103
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_
+
+#include <uds/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using Endpoint = ::android::pdx::uds::Endpoint;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_default_transport/servicetool.cpp b/libs/vr/libpdx_default_transport/servicetool.cpp
new file mode 100644
index 0000000..60eedb3
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/servicetool.cpp
@@ -0,0 +1,244 @@
+#include <errno.h>
+#include <ftw.h>
+#include <getopt.h>
+#include <pdx/client.h>
+#include <pdx/service.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <pdx/default_transport/client_channel_factory.h>
+
+using android::pdx::default_transport::ClientChannelFactory;
+
+namespace {
+
+constexpr long kClientTimeoutMs = 0; // Don't wait for non-existent services.
+constexpr int kDumpBufferSize = 2 * 4096; // Two pages.
+
+class ControlClient : public android::pdx::ClientBase<ControlClient> {
+ public:
+ explicit ControlClient(const std::string& service_path, long timeout_ms);
+
+ void Reload();
+ std::string Dump();
+
+ private:
+ friend BASE;
+
+ ControlClient(const ControlClient&) = delete;
+ void operator=(const ControlClient&) = delete;
+};
+
+bool option_verbose = false;
+
+static struct option long_options[] = {
+ {"reload", required_argument, 0, 0},
+ {"dump", required_argument, 0, 0},
+ {"verbose", no_argument, 0, 0},
+ {0, 0, 0, 0},
+};
+
+#define printf_verbose(fmt, ... /*args*/) \
+ do { \
+ if (option_verbose) \
+ printf(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+void HexDump(const void* pointer, size_t length);
+
+ControlClient::ControlClient(const std::string& service_path, long timeout_ms)
+ : BASE{ClientChannelFactory::Create(service_path), timeout_ms} {}
+
+void ControlClient::Reload() {
+ android::pdx::Transaction trans{*this};
+ auto status = trans.Send<void>(android::pdx::opcodes::REPORT_SYSPROP_CHANGE,
+ nullptr, 0, nullptr, 0);
+ if (!status) {
+ fprintf(stderr, "Failed to send reload: %s\n",
+ status.GetErrorMessage().c_str());
+ }
+}
+
+std::string ControlClient::Dump() {
+ android::pdx::Transaction trans{*this};
+ std::vector<char> buffer(kDumpBufferSize);
+ auto status = trans.Send<int>(android::pdx::opcodes::DUMP_STATE, nullptr, 0,
+ buffer.data(), buffer.size());
+
+ printf_verbose("ControlClient::Dump: ret=%d\n", ReturnStatusOrError(status));
+
+ if (!status) {
+ fprintf(stderr, "Failed to send dump request: %s\n",
+ status.GetErrorMessage().c_str());
+ return "";
+ } else if (status.get() > static_cast<ssize_t>(buffer.capacity())) {
+ fprintf(stderr, "Service returned a larger size than requested: %d\n",
+ status.get());
+ return "";
+ }
+
+ if (option_verbose)
+ HexDump(buffer.data(), status.get());
+
+ return std::string(buffer.data(), status.get());
+}
+
+int Usage(const std::string& command_name) {
+ printf("Usage: %s [options]\n", command_name.c_str());
+ printf("\t--verbose : Use verbose messages.\n");
+ printf(
+ "\t--reload <all | service path> : Ask service(s) to reload system "
+ "properties.\n");
+ printf("\t--dump <all | service path> : Dump service(s) state.\n");
+ return -1;
+}
+
+typedef int (*CallbackType)(const char* path, const struct stat* sb,
+ int type_flag, FTW* ftw_buffer);
+
+int ReloadCommandCallback(const char* path, const struct stat* sb,
+ int type_flag, FTW* ftw_buffer);
+int DumpCommandCallback(const char* path, const struct stat* sb, int type_flag,
+ FTW* ftw_buffer);
+
+void CallOnAllFiles(CallbackType callback, const std::string& base_path) {
+ const int kMaxDepth = 32;
+ nftw(base_path.c_str(), callback, kMaxDepth, FTW_PHYS);
+}
+
+int ReloadCommand(const std::string& service_path) {
+ printf_verbose("ReloadCommand: service_path=%s\n", service_path.c_str());
+
+ if (service_path == "" || service_path == "all") {
+ CallOnAllFiles(ReloadCommandCallback,
+ ClientChannelFactory::GetRootEndpointPath());
+ return 0;
+ } else {
+ auto client = ControlClient::Create(service_path, kClientTimeoutMs);
+ if (!client) {
+ fprintf(stderr, "Failed to open service at \"%s\".\n",
+ service_path.c_str());
+ return -1;
+ }
+
+ client->Reload();
+ return 0;
+ }
+}
+
+int DumpCommand(const std::string& service_path) {
+ printf_verbose("DumpCommand: service_path=%s\n", service_path.c_str());
+
+ if (service_path == "" || service_path == "all") {
+ CallOnAllFiles(DumpCommandCallback,
+ ClientChannelFactory::GetRootEndpointPath());
+ return 0;
+ } else {
+ auto client = ControlClient::Create(service_path, kClientTimeoutMs);
+ if (!client) {
+ fprintf(stderr, "Failed to open service at \"%s\".\n",
+ service_path.c_str());
+ return -1;
+ }
+
+ std::string response = client->Dump();
+ if (!response.empty()) {
+ printf(
+ "--------------------------------------------------------------------"
+ "---\n");
+ printf("%s:\n", service_path.c_str());
+ printf("%s\n", response.c_str());
+ }
+ return 0;
+ }
+}
+
+int ReloadCommandCallback(const char* path, const struct stat*, int type_flag,
+ FTW*) {
+ if (type_flag == FTW_F)
+ ReloadCommand(path);
+ return 0;
+}
+
+int DumpCommandCallback(const char* path, const struct stat*, int type_flag,
+ FTW*) {
+ if (type_flag == FTW_F)
+ DumpCommand(path);
+ return 0;
+}
+
+void HexDump(const void* pointer, size_t length) {
+ uintptr_t address = reinterpret_cast<uintptr_t>(pointer);
+
+ for (size_t count = 0; count < length; count += 16, address += 16) {
+ printf("0x%08lx: ", static_cast<unsigned long>(address));
+
+ for (size_t i = 0; i < 16u; i++) {
+ if (i < std::min(length - count, static_cast<size_t>(16))) {
+ printf("%02x ", *reinterpret_cast<const uint8_t*>(address + i));
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf("|");
+
+ for (size_t i = 0; i < 16u; i++) {
+ if (i < std::min(length - count, static_cast<size_t>(16))) {
+ char c = *reinterpret_cast<const char*>(address + i);
+ if (isalnum(c) || c == ' ') {
+ printf("%c", c);
+ } else {
+ printf(".");
+ }
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf("|\n");
+ }
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ int getopt_code;
+ int option_index;
+ std::string option = "";
+ std::string command = "";
+ std::string command_argument = "";
+
+ // Process command line options.
+ while ((getopt_code =
+ getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ option = long_options[option_index].name;
+ printf_verbose("option=%s\n", option.c_str());
+ switch (getopt_code) {
+ case 0:
+ if (option == "verbose") {
+ option_verbose = true;
+ } else {
+ command = option;
+ if (optarg)
+ command_argument = optarg;
+ }
+ break;
+ }
+ }
+
+ printf_verbose("command=%s command_argument=%s\n", command.c_str(),
+ command_argument.c_str());
+
+ if (command == "") {
+ return Usage(argv[0]);
+ } else if (command == "reload") {
+ return ReloadCommand(command_argument);
+ } else if (command == "dump") {
+ return DumpCommand(command_argument);
+ } else {
+ return Usage(argv[0]);
+ }
+}
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
new file mode 100644
index 0000000..9c4a3b0
--- /dev/null
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -0,0 +1,44 @@
+cc_library_static {
+ name: "libpdx_uds",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ export_include_dirs: ["private"],
+ local_include_dirs: ["private"],
+ srcs: [
+ "channel_manager.cpp",
+ "client_channel_factory.cpp",
+ "client_channel.cpp",
+ "ipc_helper.cpp",
+ "service_dispatcher.cpp",
+ "service_endpoint.cpp",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+}
+
+cc_test {
+ name: "libpdx_uds_tests",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "remote_method_tests.cpp",
+ "service_framework_tests.cpp",
+ ],
+ static_libs: [
+ "libpdx_uds",
+ "libpdx",
+ ],
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp
new file mode 100644
index 0000000..387625c
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_manager.cpp
@@ -0,0 +1,44 @@
+#include <uds/channel_manager.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+ChannelManager& ChannelManager::Get() {
+ static ChannelManager instance;
+ return instance;
+}
+
+void ChannelManager::CloseHandle(int32_t handle) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ auto channel = channels_.find(handle);
+ if (channel == channels_.end()) {
+ ALOGE("Invalid channel handle: %d", handle);
+ } else {
+ channels_.erase(channel);
+ }
+}
+
+LocalChannelHandle ChannelManager::CreateHandle(LocalHandle data_fd,
+ LocalHandle event_fd) {
+ if (data_fd && event_fd) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ int32_t handle = data_fd.Get();
+ channels_.emplace(handle,
+ ChannelData{std::move(data_fd), std::move(event_fd)});
+ return LocalChannelHandle(this, handle);
+ }
+ return LocalChannelHandle(nullptr, -1);
+}
+
+int ChannelManager::GetEventFd(int32_t handle) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ auto channel = channels_.find(handle);
+ return channel != channels_.end() ? channel->second.event_fd.Get() : -1;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
new file mode 100644
index 0000000..3394759
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -0,0 +1,304 @@
+#include "uds/client_channel.h"
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <pdx/client.h>
+#include <pdx/service_endpoint.h>
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+namespace {
+
+struct TransactionState {
+ bool GetLocalFileHandle(int index, LocalHandle* handle) {
+ if (index < 0) {
+ handle->Reset(index);
+ } else if (static_cast<size_t>(index) < response.file_descriptors.size()) {
+ *handle = std::move(response.file_descriptors[index]);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
+ if (index < 0) {
+ *handle = LocalChannelHandle{nullptr, index};
+ } else if (static_cast<size_t>(index) < response.channels.size()) {
+ auto& channel_info = response.channels[index];
+ *handle = ChannelManager::Get().CreateHandle(
+ std::move(channel_info.data_fd), std::move(channel_info.event_fd));
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ FileReference PushFileHandle(BorrowedHandle handle) {
+ if (!handle)
+ return handle.Get();
+ request.file_descriptors.push_back(std::move(handle));
+ return request.file_descriptors.size() - 1;
+ }
+
+ ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
+ if (!handle)
+ return handle.value();
+ ChannelInfo<BorrowedHandle> channel_info;
+ channel_info.data_fd.Reset(handle.value());
+ channel_info.event_fd.Reset(
+ ChannelManager::Get().GetEventFd(handle.value()));
+ request.channels.push_back(std::move(channel_info));
+ return request.channels.size() - 1;
+ }
+
+ RequestHeader<BorrowedHandle> request;
+ ResponseHeader<LocalHandle> response;
+};
+
+Status<void> ReadAndDiscardData(int socket_fd, size_t size) {
+ while (size > 0) {
+ // If there is more data to read in the message than the buffers provided
+ // by the caller, read and discard the extra data from the socket.
+ char buffer[1024];
+ size_t size_to_read = std::min(sizeof(buffer), size);
+ auto status = ReceiveData(socket_fd, buffer, size_to_read);
+ if (!status)
+ return status;
+ size -= size_to_read;
+ }
+ // We still want to return EIO error to the caller in case we had unexpected
+ // data in the socket stream.
+ return ErrorStatus(EIO);
+}
+
+Status<void> SendRequest(int socket_fd, TransactionState* transaction_state,
+ int opcode, const iovec* send_vector,
+ size_t send_count, size_t max_recv_len) {
+ size_t send_len = CountVectorSize(send_vector, send_count);
+ InitRequest(&transaction_state->request, opcode, send_len, max_recv_len,
+ false);
+ auto status = SendData(socket_fd, transaction_state->request);
+ if (status && send_len > 0)
+ status = SendDataVector(socket_fd, send_vector, send_count);
+ return status;
+}
+
+Status<void> ReceiveResponse(int socket_fd, TransactionState* transaction_state,
+ const iovec* receive_vector, size_t receive_count,
+ size_t max_recv_len) {
+ auto status = ReceiveData(socket_fd, &transaction_state->response);
+ if (!status)
+ return status;
+
+ if (transaction_state->response.recv_len > 0) {
+ std::vector<iovec> read_buffers;
+ size_t size_remaining = 0;
+ if (transaction_state->response.recv_len != max_recv_len) {
+ // If the receive buffer not exactly the size of data available, recreate
+ // the vector list to consume the data exactly since ReceiveDataVector()
+ // validates that the number of bytes received equals the number of bytes
+ // requested.
+ size_remaining = transaction_state->response.recv_len;
+ for (size_t i = 0; i < receive_count && size_remaining > 0; i++) {
+ read_buffers.push_back(receive_vector[i]);
+ iovec& last_vec = read_buffers.back();
+ if (last_vec.iov_len > size_remaining)
+ last_vec.iov_len = size_remaining;
+ size_remaining -= last_vec.iov_len;
+ }
+ receive_vector = read_buffers.data();
+ receive_count = read_buffers.size();
+ }
+ status = ReceiveDataVector(socket_fd, receive_vector, receive_count);
+ if (status && size_remaining > 0)
+ status = ReadAndDiscardData(socket_fd, size_remaining);
+ }
+ return status;
+}
+
+} // anonymous namespace
+
+ClientChannel::ClientChannel(LocalChannelHandle channel_handle)
+ : channel_handle_{std::move(channel_handle)} {
+ int data_fd = channel_handle_.value();
+ int event_fd =
+ channel_handle_ ? ChannelManager::Get().GetEventFd(data_fd) : -1;
+
+ if (event_fd >= 0) {
+ epoll_fd_.Reset(epoll_create(1));
+ if (epoll_fd_) {
+ epoll_event data_ev;
+ data_ev.events = EPOLLHUP;
+ data_ev.data.fd = data_fd;
+
+ epoll_event event_ev;
+ event_ev.events = EPOLLIN;
+ event_ev.data.fd = event_fd;
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd, &data_ev) < 0 ||
+ epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd, &event_ev) < 0) {
+ ALOGE("Failed to add fd to epoll fd because: %s\n", strerror(errno));
+ epoll_fd_.Close();
+ }
+ }
+ }
+}
+
+std::unique_ptr<pdx::ClientChannel> ClientChannel::Create(
+ LocalChannelHandle channel_handle) {
+ return std::unique_ptr<pdx::ClientChannel>{
+ new ClientChannel{std::move(channel_handle)}};
+}
+
+ClientChannel::~ClientChannel() {
+ if (channel_handle_)
+ shutdown(channel_handle_.value(), SHUT_WR);
+}
+
+void* ClientChannel::AllocateTransactionState() { return new TransactionState; }
+
+void ClientChannel::FreeTransactionState(void* state) {
+ delete static_cast<TransactionState*>(state);
+}
+
+Status<void> ClientChannel::SendImpulse(int opcode, const void* buffer,
+ size_t length) {
+ Status<void> status;
+ android::pdx::uds::RequestHeader<BorrowedHandle> request;
+ if (length > request.impulse_payload.size() ||
+ (buffer == nullptr && length != 0)) {
+ status.SetError(EINVAL);
+ return status;
+ }
+
+ InitRequest(&request, opcode, length, 0, true);
+ memcpy(request.impulse_payload.data(), buffer, length);
+ return SendData(channel_handle_.value(), request);
+}
+
+Status<int> ClientChannel::SendAndReceive(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ Status<int> result;
+ if ((send_vector == nullptr && send_count != 0) ||
+ (receive_vector == nullptr && receive_count != 0)) {
+ result.SetError(EINVAL);
+ return result;
+ }
+
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ size_t max_recv_len = CountVectorSize(receive_vector, receive_count);
+
+ auto status = SendRequest(channel_handle_.value(), state, opcode, send_vector,
+ send_count, max_recv_len);
+ if (status) {
+ status = ReceiveResponse(channel_handle_.value(), state, receive_vector,
+ receive_count, max_recv_len);
+ }
+ if (!result.PropagateError(status)) {
+ const int return_code = state->response.ret_code;
+ if (return_code >= 0)
+ result.SetValue(return_code);
+ else
+ result.SetError(-return_code);
+ }
+ return result;
+}
+
+Status<int> ClientChannel::SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ return SendAndReceive(transaction_state, opcode, send_vector, send_count,
+ receive_vector, receive_count);
+}
+
+Status<LocalHandle> ClientChannel::SendWithFileHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) {
+ Status<int> int_status =
+ SendAndReceive(transaction_state, opcode, send_vector, send_count,
+ receive_vector, receive_count);
+ Status<LocalHandle> status;
+ if (status.PropagateError(int_status))
+ return status;
+
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ LocalHandle handle;
+ if (state->GetLocalFileHandle(int_status.get(), &handle)) {
+ status.SetValue(std::move(handle));
+ } else {
+ status.SetError(EINVAL);
+ }
+ return status;
+}
+
+Status<LocalChannelHandle> ClientChannel::SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) {
+ Status<int> int_status =
+ SendAndReceive(transaction_state, opcode, send_vector, send_count,
+ receive_vector, receive_count);
+ Status<LocalChannelHandle> status;
+ if (status.PropagateError(int_status))
+ return status;
+
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ LocalChannelHandle handle;
+ if (state->GetLocalChannelHandle(int_status.get(), &handle)) {
+ status.SetValue(std::move(handle));
+ } else {
+ status.SetError(EINVAL);
+ }
+ return status;
+}
+
+FileReference ClientChannel::PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushFileHandle(handle.Borrow());
+}
+
+FileReference ClientChannel::PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushFileHandle(handle.Duplicate());
+}
+
+ChannelReference ClientChannel::PushChannelHandle(
+ void* transaction_state, const LocalChannelHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushChannelHandle(handle.Borrow());
+}
+
+ChannelReference ClientChannel::PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushChannelHandle(handle.Duplicate());
+}
+
+bool ClientChannel::GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->GetLocalFileHandle(ref, handle);
+}
+
+bool ClientChannel::GetChannelHandle(void* transaction_state,
+ ChannelReference ref,
+ LocalChannelHandle* handle) const {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->GetLocalChannelHandle(ref, handle);
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel_factory.cpp b/libs/vr/libpdx_uds/client_channel_factory.cpp
new file mode 100644
index 0000000..1879127
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_factory.cpp
@@ -0,0 +1,89 @@
+#include <uds/client_channel_factory.h>
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <uds/channel_manager.h>
+#include <uds/client_channel.h>
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+std::string ClientChannelFactory::GetRootEndpointPath() {
+ return "/dev/socket/pdx";
+}
+
+std::string ClientChannelFactory::GetEndpointPath(
+ const std::string& endpoint_path) {
+ std::string path;
+ if (!endpoint_path.empty()) {
+ if (endpoint_path.front() == '/')
+ path = endpoint_path;
+ else
+ path = GetRootEndpointPath() + '/' + endpoint_path;
+ }
+ return path;
+}
+
+ClientChannelFactory::ClientChannelFactory(const std::string& endpoint_path)
+ : endpoint_path_{GetEndpointPath(endpoint_path)} {}
+
+std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
+ const std::string& endpoint_path) {
+ return std::unique_ptr<pdx::ClientChannelFactory>{
+ new ClientChannelFactory{endpoint_path}};
+}
+
+Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
+ int64_t timeout_ms) const {
+ auto status = WaitForEndpoint(endpoint_path_, timeout_ms);
+ if (!status)
+ return ErrorStatus(status.error());
+
+ LocalHandle socket_fd{socket(AF_UNIX, SOCK_STREAM, 0)};
+ if (!socket_fd) {
+ ALOGE("ClientChannelFactory::Connect: socket error %s", strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ sockaddr_un remote;
+ remote.sun_family = AF_UNIX;
+ strncpy(remote.sun_path, endpoint_path_.c_str(), sizeof(remote.sun_path));
+ remote.sun_path[sizeof(remote.sun_path) - 1] = '\0';
+
+ int ret = RETRY_EINTR(connect(
+ socket_fd.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote)));
+ if (ret == -1) {
+ ALOGE(
+ "ClientChannelFactory::Connect: Failed to initialize connection when "
+ "connecting %s",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ RequestHeader<BorrowedHandle> request;
+ InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false);
+ status = SendData(socket_fd.Get(), request);
+ if (!status)
+ return ErrorStatus(status.error());
+ ResponseHeader<LocalHandle> response;
+ status = ReceiveData(socket_fd.Get(), &response);
+ if (!status)
+ return ErrorStatus(status.error());
+ int ref = response.ret_code;
+ if (ref < 0 || static_cast<size_t>(ref) > response.file_descriptors.size())
+ return ErrorStatus(EIO);
+
+ LocalHandle event_fd = std::move(response.file_descriptors[ref]);
+ return ClientChannel::Create(ChannelManager::Get().CreateHandle(
+ std::move(socket_fd), std::move(event_fd)));
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
new file mode 100644
index 0000000..dca23ef
--- /dev/null
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -0,0 +1,408 @@
+#include "uds/ipc_helper.h"
+
+#include <alloca.h>
+#include <errno.h>
+#include <log/log.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+
+#include <pdx/service.h>
+#include <pdx/utility.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+uint32_t kMagicPreamble = 0x7564736d; // 'udsm'.
+
+struct MessagePreamble {
+ uint32_t magic{0};
+ uint32_t data_size{0};
+ uint32_t fd_count{0};
+};
+
+Status<void> SendPayload::Send(int socket_fd) {
+ return Send(socket_fd, nullptr);
+}
+
+Status<void> SendPayload::Send(int socket_fd, const ucred* cred) {
+ MessagePreamble preamble;
+ preamble.magic = kMagicPreamble;
+ preamble.data_size = buffer_.size();
+ preamble.fd_count = file_handles_.size();
+
+ ssize_t ret =
+ RETRY_EINTR(send(socket_fd, &preamble, sizeof(preamble), MSG_NOSIGNAL));
+ if (ret < 0)
+ return ErrorStatus(errno);
+ if (ret != sizeof(preamble))
+ return ErrorStatus(EIO);
+
+ msghdr msg = {};
+ iovec recv_vect = {buffer_.data(), buffer_.size()};
+ msg.msg_iov = &recv_vect;
+ msg.msg_iovlen = 1;
+
+ if (cred || !file_handles_.empty()) {
+ const size_t fd_bytes = file_handles_.size() * sizeof(int);
+ msg.msg_controllen = (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
+ (fd_bytes == 0 ? 0 : CMSG_SPACE(fd_bytes));
+ msg.msg_control = alloca(msg.msg_controllen);
+
+ cmsghdr* control = CMSG_FIRSTHDR(&msg);
+ if (cred) {
+ control->cmsg_level = SOL_SOCKET;
+ control->cmsg_type = SCM_CREDENTIALS;
+ control->cmsg_len = CMSG_LEN(sizeof(ucred));
+ memcpy(CMSG_DATA(control), cred, sizeof(ucred));
+ control = CMSG_NXTHDR(&msg, control);
+ }
+
+ if (fd_bytes) {
+ control->cmsg_level = SOL_SOCKET;
+ control->cmsg_type = SCM_RIGHTS;
+ control->cmsg_len = CMSG_LEN(fd_bytes);
+ memcpy(CMSG_DATA(control), file_handles_.data(), fd_bytes);
+ }
+ }
+
+ ret = RETRY_EINTR(sendmsg(socket_fd, &msg, MSG_NOSIGNAL));
+ if (ret < 0)
+ return ErrorStatus(errno);
+ if (static_cast<size_t>(ret) != buffer_.size())
+ return ErrorStatus(EIO);
+ return {};
+}
+
+// MessageWriter
+void* SendPayload::GetNextWriteBufferSection(size_t size) {
+ return buffer_.grow_by(size);
+}
+
+OutputResourceMapper* SendPayload::GetOutputResourceMapper() { return this; }
+
+// OutputResourceMapper
+FileReference SendPayload::PushFileHandle(const LocalHandle& handle) {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+}
+
+FileReference SendPayload::PushFileHandle(const BorrowedHandle& handle) {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+}
+
+FileReference SendPayload::PushFileHandle(const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+ChannelReference SendPayload::PushChannelHandle(
+ const LocalChannelHandle& /*handle*/) {
+ return -1;
+}
+ChannelReference SendPayload::PushChannelHandle(
+ const BorrowedChannelHandle& /*handle*/) {
+ return -1;
+}
+ChannelReference SendPayload::PushChannelHandle(
+ const RemoteChannelHandle& /*handle*/) {
+ return -1;
+}
+
+Status<void> ReceivePayload::Receive(int socket_fd) {
+ return Receive(socket_fd, nullptr);
+}
+
+Status<void> ReceivePayload::Receive(int socket_fd, ucred* cred) {
+ MessagePreamble preamble;
+ ssize_t ret =
+ RETRY_EINTR(recv(socket_fd, &preamble, sizeof(preamble), MSG_WAITALL));
+ if (ret < 0)
+ return ErrorStatus(errno);
+ if (ret != sizeof(preamble) || preamble.magic != kMagicPreamble)
+ return ErrorStatus(EIO);
+
+ buffer_.resize(preamble.data_size);
+ file_handles_.clear();
+ read_pos_ = 0;
+
+ msghdr msg = {};
+ iovec recv_vect = {buffer_.data(), buffer_.size()};
+ msg.msg_iov = &recv_vect;
+ msg.msg_iovlen = 1;
+
+ if (cred || preamble.fd_count) {
+ const size_t receive_fd_bytes = preamble.fd_count * sizeof(int);
+ msg.msg_controllen =
+ (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
+ (receive_fd_bytes == 0 ? 0 : CMSG_SPACE(receive_fd_bytes));
+ msg.msg_control = alloca(msg.msg_controllen);
+ }
+
+ ret = RETRY_EINTR(recvmsg(socket_fd, &msg, MSG_WAITALL));
+ if (ret < 0)
+ return ErrorStatus(errno);
+ if (static_cast<uint32_t>(ret) != preamble.data_size)
+ return ErrorStatus(EIO);
+
+ bool cred_available = false;
+ file_handles_.reserve(preamble.fd_count);
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ while (cmsg) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cred && cmsg->cmsg_len == CMSG_LEN(sizeof(ucred))) {
+ cred_available = true;
+ memcpy(cred, CMSG_DATA(cmsg), sizeof(ucred));
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ socklen_t payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ const int* fds = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+ size_t fd_count = payload_len / sizeof(int);
+ std::transform(fds, fds + fd_count, std::back_inserter(file_handles_),
+ [](int fd) { return LocalHandle{fd}; });
+ }
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ if (cred && !cred_available) {
+ return ErrorStatus(EIO);
+ }
+
+ return {};
+}
+
+// MessageReader
+MessageReader::BufferSection ReceivePayload::GetNextReadBufferSection() {
+ return {buffer_.data() + read_pos_, &*buffer_.end()};
+}
+
+void ReceivePayload::ConsumeReadBufferSectionData(const void* new_start) {
+ read_pos_ = PointerDistance(new_start, buffer_.data());
+}
+
+InputResourceMapper* ReceivePayload::GetInputResourceMapper() { return this; }
+
+// InputResourceMapper
+bool ReceivePayload::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ if (ref < 0) {
+ *handle = LocalHandle{ref};
+ return true;
+ }
+ if (static_cast<size_t>(ref) > file_handles_.size())
+ return false;
+ *handle = std::move(file_handles_[ref]);
+ return true;
+}
+
+bool ReceivePayload::GetChannelHandle(ChannelReference /*ref*/,
+ LocalChannelHandle* /*handle*/) {
+ return false;
+}
+
+Status<void> SendData(int socket_fd, const void* data, size_t size) {
+ ssize_t size_written = RETRY_EINTR(send(socket_fd, data, size, MSG_NOSIGNAL));
+ if (size_written < 0)
+ return ErrorStatus(errno);
+ if (static_cast<size_t>(size_written) != size)
+ return ErrorStatus(EIO);
+ return {};
+}
+
+Status<void> SendDataVector(int socket_fd, const iovec* data, size_t count) {
+ msghdr msg = {};
+ msg.msg_iov = const_cast<iovec*>(data);
+ msg.msg_iovlen = count;
+ ssize_t size_written = RETRY_EINTR(sendmsg(socket_fd, &msg, MSG_NOSIGNAL));
+ if (size_written < 0)
+ return ErrorStatus(errno);
+ if (static_cast<size_t>(size_written) != CountVectorSize(data, count))
+ return ErrorStatus(EIO);
+ return {};
+}
+
+Status<void> ReceiveData(int socket_fd, void* data, size_t size) {
+ ssize_t size_read = RETRY_EINTR(recv(socket_fd, data, size, MSG_WAITALL));
+ if (size_read < 0)
+ return ErrorStatus(errno);
+ if (static_cast<size_t>(size_read) != size)
+ return ErrorStatus(EIO);
+ return {};
+}
+
+Status<void> ReceiveDataVector(int socket_fd, const iovec* data, size_t count) {
+ msghdr msg = {};
+ msg.msg_iov = const_cast<iovec*>(data);
+ msg.msg_iovlen = count;
+ ssize_t size_read = RETRY_EINTR(recvmsg(socket_fd, &msg, MSG_WAITALL));
+ if (size_read < 0)
+ return ErrorStatus(errno);
+ if (static_cast<size_t>(size_read) != CountVectorSize(data, count))
+ return ErrorStatus(EIO);
+ return {};
+}
+
+size_t CountVectorSize(const iovec* vector, size_t count) {
+ return std::accumulate(
+ vector, vector + count, size_t{0},
+ [](size_t size, const iovec& vec) { return size + vec.iov_len; });
+}
+
+void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request,
+ int opcode, uint32_t send_len, uint32_t max_recv_len,
+ bool is_impulse) {
+ request->op = opcode;
+ request->cred.pid = getpid();
+ request->cred.uid = geteuid();
+ request->cred.gid = getegid();
+ request->send_len = send_len;
+ request->max_recv_len = max_recv_len;
+ request->is_impulse = is_impulse;
+}
+
+Status<void> WaitForEndpoint(const std::string& endpoint_path,
+ int64_t timeout_ms) {
+ // Endpoint path must be absolute.
+ if (endpoint_path.empty() || endpoint_path.front() != '/')
+ return ErrorStatus(EINVAL);
+
+ // Create inotify fd.
+ LocalHandle fd{inotify_init()};
+ if (!fd)
+ return ErrorStatus(errno);
+
+ // Set the inotify fd to non-blocking.
+ int ret = fcntl(fd.Get(), F_GETFL);
+ fcntl(fd.Get(), F_SETFL, ret | O_NONBLOCK);
+
+ // Setup the pollfd.
+ pollfd pfd = {fd.Get(), POLLIN, 0};
+
+ // Find locations of each path separator.
+ std::vector<size_t> separators{0}; // The path is absolute, so '/' is at #0.
+ size_t pos = endpoint_path.find('/', 1);
+ while (pos != std::string::npos) {
+ separators.push_back(pos);
+ pos = endpoint_path.find('/', pos + 1);
+ }
+ separators.push_back(endpoint_path.size());
+
+ // Walk down the path, checking for existence and waiting if needed.
+ pos = 1;
+ size_t links = 0;
+ std::string current;
+ while (pos < separators.size() && links <= MAXSYMLINKS) {
+ std::string previous = current;
+ current = endpoint_path.substr(0, separators[pos]);
+
+ // Check for existence; proceed to setup a watch if not.
+ if (access(current.c_str(), F_OK) < 0) {
+ if (errno != ENOENT)
+ return ErrorStatus(errno);
+
+ // Extract the name of the path component to wait for.
+ std::string next = current.substr(
+ separators[pos - 1] + 1, separators[pos] - separators[pos - 1] - 1);
+
+ // Add a watch on the last existing directory we reach.
+ int wd = inotify_add_watch(
+ fd.Get(), previous.c_str(),
+ IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO);
+ if (wd < 0) {
+ if (errno != ENOENT)
+ return ErrorStatus(errno);
+ // Restart at the beginning if previous was deleted.
+ links = 0;
+ current.clear();
+ pos = 1;
+ continue;
+ }
+
+ // Make sure current didn't get created before the watch was added.
+ ret = access(current.c_str(), F_OK);
+ if (ret < 0) {
+ if (errno != ENOENT)
+ return ErrorStatus(errno);
+
+ bool exit_poll = false;
+ while (!exit_poll) {
+ // Wait for an event or timeout.
+ ret = poll(&pfd, 1, timeout_ms);
+ if (ret <= 0)
+ return ErrorStatus(ret == 0 ? ETIMEDOUT : errno);
+
+ // Read events.
+ char buffer[sizeof(inotify_event) + NAME_MAX + 1];
+
+ ret = read(fd.Get(), buffer, sizeof(buffer));
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+ else
+ return ErrorStatus(errno);
+ } else if (static_cast<size_t>(ret) < sizeof(struct inotify_event)) {
+ return ErrorStatus(EIO);
+ }
+
+ auto* event = reinterpret_cast<const inotify_event*>(buffer);
+ auto* end = reinterpret_cast<const inotify_event*>(buffer + ret);
+ while (event < end) {
+ std::string event_for;
+ if (event->len > 0)
+ event_for = event->name;
+
+ if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
+ // See if this is the droid we're looking for.
+ if (next == event_for) {
+ exit_poll = true;
+ break;
+ }
+ } else if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {
+ // Restart at the beginning if our watch dir is deleted.
+ links = 0;
+ current.clear();
+ pos = 0;
+ exit_poll = true;
+ break;
+ }
+
+ event = reinterpret_cast<const inotify_event*>(AdvancePointer(
+ event, sizeof(struct inotify_event) + event->len));
+ } // while (event < end)
+ } // while (!exit_poll)
+ } // Current dir doesn't exist.
+ ret = inotify_rm_watch(fd.Get(), wd);
+ if (ret < 0 && errno != EINVAL)
+ return ErrorStatus(errno);
+ } // if (access(current.c_str(), F_OK) < 0)
+
+ // Check for symbolic link and update link count.
+ struct stat stat_buf;
+ ret = lstat(current.c_str(), &stat_buf);
+ if (ret < 0 && errno != ENOENT)
+ return ErrorStatus(errno);
+ else if (ret == 0 && S_ISLNK(stat_buf.st_mode))
+ links++;
+ pos++;
+ } // while (pos < separators.size() && links <= MAXSYMLINKS)
+
+ return {};
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h
new file mode 100644
index 0000000..e8d3f6e
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
+
+#include <mutex>
+#include <unordered_map>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelManager : public ChannelManagerInterface {
+ public:
+ static ChannelManager& Get();
+
+ LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd);
+ int GetEventFd(int32_t handle);
+
+ private:
+ struct ChannelData {
+ LocalHandle data_fd;
+ LocalHandle event_fd;
+ };
+ ChannelManager() = default;
+
+ void CloseHandle(int32_t handle) override;
+
+ std::mutex mutex_;
+ std::unordered_map<int32_t, ChannelData> channels_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h
new file mode 100644
index 0000000..12a40e7
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -0,0 +1,71 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+
+#include <pdx/client_channel.h>
+
+#include <uds/channel_manager.h>
+#include <uds/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ClientChannel : public pdx::ClientChannel {
+ public:
+ ~ClientChannel() override;
+
+ static std::unique_ptr<pdx::ClientChannel> Create(
+ LocalChannelHandle channel_handle);
+
+ uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; }
+ int event_fd() const override { return epoll_fd_.Get(); }
+ LocalChannelHandle& GetChannelHandle() override { return channel_handle_; }
+ void* AllocateTransactionState() override;
+ void FreeTransactionState(void* state) override;
+
+ Status<void> SendImpulse(int opcode, const void* buffer,
+ size_t length) override;
+
+ Status<int> SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) override;
+ Status<LocalHandle> SendWithFileHandle(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) override;
+ Status<LocalChannelHandle> SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count) override;
+
+ FileReference PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) override;
+ FileReference PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) override;
+ ChannelReference PushChannelHandle(void* transaction_state,
+ const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) override;
+ bool GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const override;
+ bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle) const override;
+
+ private:
+ explicit ClientChannel(LocalChannelHandle channel_handle);
+
+ Status<int> SendAndReceive(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+
+ LocalChannelHandle channel_handle_;
+ LocalHandle epoll_fd_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel_factory.h b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
new file mode 100644
index 0000000..6f80d31
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <string>
+
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ClientChannelFactory : public pdx::ClientChannelFactory {
+ public:
+ static std::unique_ptr<pdx::ClientChannelFactory> Create(
+ const std::string& endpoint_path);
+
+ Status<std::unique_ptr<pdx::ClientChannel>> Connect(
+ int64_t timeout_ms) const override;
+
+ static std::string GetRootEndpointPath();
+ static std::string GetEndpointPath(const std::string& endpoint_path);
+
+ private:
+ explicit ClientChannelFactory(const std::string& endpoint_path);
+
+ std::string endpoint_path_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
new file mode 100644
index 0000000..00f3490
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -0,0 +1,164 @@
+#ifndef ANDROID_PDX_UDS_IPC_HELPER_H_
+#define ANDROID_PDX_UDS_IPC_HELPER_H_
+
+#include <sys/socket.h>
+#include <utility>
+#include <vector>
+
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/status.h>
+#include <pdx/utility.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+#define RETRY_EINTR(fnc_call) \
+ ([&]() -> decltype(fnc_call) { \
+ decltype(fnc_call) result; \
+ do { \
+ result = (fnc_call); \
+ } while (result == -1 && errno == EINTR); \
+ return result; \
+ })()
+
+class SendPayload : public MessageWriter, public OutputResourceMapper {
+ public:
+ Status<void> Send(int socket_fd);
+ Status<void> Send(int socket_fd, const ucred* cred);
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override;
+ OutputResourceMapper* GetOutputResourceMapper() override;
+
+ // OutputResourceMapper
+ FileReference PushFileHandle(const LocalHandle& handle) override;
+ FileReference PushFileHandle(const BorrowedHandle& handle) override;
+ FileReference PushFileHandle(const RemoteHandle& handle) override;
+ ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ private:
+ ByteBuffer buffer_;
+ std::vector<int> file_handles_;
+};
+
+class ReceivePayload : public MessageReader, public InputResourceMapper {
+ public:
+ Status<void> Receive(int socket_fd);
+ Status<void> Receive(int socket_fd, ucred* cred);
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override;
+ void ConsumeReadBufferSectionData(const void* new_start) override;
+ InputResourceMapper* GetInputResourceMapper() override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ private:
+ ByteBuffer buffer_;
+ std::vector<LocalHandle> file_handles_;
+ size_t read_pos_{0};
+};
+
+template <typename FileHandleType>
+class ChannelInfo {
+ public:
+ FileHandleType data_fd;
+ FileHandleType event_fd;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, event_fd);
+};
+
+template <typename FileHandleType>
+class RequestHeader {
+ public:
+ int32_t op{0};
+ ucred cred;
+ uint32_t send_len{0};
+ uint32_t max_recv_len{0};
+ std::vector<FileHandleType> file_descriptors;
+ std::vector<ChannelInfo<FileHandleType>> channels;
+ std::array<uint8_t, 32> impulse_payload;
+ bool is_impulse{false};
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(RequestHeader, op, send_len, max_recv_len,
+ file_descriptors, channels, impulse_payload,
+ is_impulse);
+};
+
+template <typename FileHandleType>
+class ResponseHeader {
+ public:
+ int32_t ret_code{0};
+ uint32_t recv_len{0};
+ std::vector<FileHandleType> file_descriptors;
+ std::vector<ChannelInfo<FileHandleType>> channels;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(ResponseHeader, ret_code, recv_len, file_descriptors,
+ channels);
+};
+
+template <typename T>
+inline Status<void> SendData(int socket_fd, const T& data) {
+ SendPayload payload;
+ rpc::Serialize(data, &payload);
+ return payload.Send(socket_fd);
+}
+
+template <typename FileHandleType>
+inline Status<void> SendData(int socket_fd,
+ const RequestHeader<FileHandleType>& request) {
+ SendPayload payload;
+ rpc::Serialize(request, &payload);
+ return payload.Send(socket_fd, &request.cred);
+}
+
+Status<void> SendData(int socket_fd, const void* data, size_t size);
+Status<void> SendDataVector(int socket_fd, const iovec* data, size_t count);
+
+template <typename T>
+inline Status<void> ReceiveData(int socket_fd, T* data) {
+ ReceivePayload payload;
+ Status<void> status = payload.Receive(socket_fd);
+ if (status && rpc::Deserialize(data, &payload) != rpc::ErrorCode::NO_ERROR)
+ status.SetError(EIO);
+ return status;
+}
+
+template <typename FileHandleType>
+inline Status<void> ReceiveData(int socket_fd,
+ RequestHeader<FileHandleType>* request) {
+ ReceivePayload payload;
+ Status<void> status = payload.Receive(socket_fd, &request->cred);
+ if (status && rpc::Deserialize(request, &payload) != rpc::ErrorCode::NO_ERROR)
+ status.SetError(EIO);
+ return status;
+}
+
+Status<void> ReceiveData(int socket_fd, void* data, size_t size);
+Status<void> ReceiveDataVector(int socket_fd, const iovec* data, size_t count);
+
+size_t CountVectorSize(const iovec* data, size_t count);
+void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request,
+ int opcode, uint32_t send_len, uint32_t max_recv_len,
+ bool is_impulse);
+
+Status<void> WaitForEndpoint(const std::string& endpoint_path,
+ int64_t timeout_ms);
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_IPC_HELPER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
new file mode 100644
index 0000000..23af4f4
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include <pdx/file_handle.h>
+#include <pdx/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ServiceDispatcher : public pdx::ServiceDispatcher {
+ public:
+ // Get a new instance of ServiceDispatcher, or return nullptr if init failed.
+ static std::unique_ptr<pdx::ServiceDispatcher> Create();
+
+ ~ServiceDispatcher() override;
+ int AddService(const std::shared_ptr<Service>& service) override;
+ int RemoveService(const std::shared_ptr<Service>& service) override;
+ int ReceiveAndDispatch() override;
+ int ReceiveAndDispatch(int timeout) override;
+ int EnterDispatchLoop() override;
+ void SetCanceled(bool cancel) override;
+ bool IsCanceled() const override;
+
+ private:
+ ServiceDispatcher();
+
+ // Internal thread accounting.
+ int ThreadEnter();
+ void ThreadExit();
+
+ std::mutex mutex_;
+ std::condition_variable condition_;
+ std::atomic<bool> canceled_{false};
+
+ std::list<std::shared_ptr<Service>> services_;
+
+ int thread_count_ = 0;
+ LocalHandle event_fd_;
+ LocalHandle epoll_fd_;
+
+ ServiceDispatcher(const ServiceDispatcher&) = delete;
+ void operator=(const ServiceDispatcher&) = delete;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
new file mode 100644
index 0000000..0f69400
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -0,0 +1,144 @@
+#ifndef ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_
+
+#include <sys/stat.h>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/service.h>
+#include <pdx/service_endpoint.h>
+#include <uds/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class Endpoint : public pdx::Endpoint {
+ public:
+ enum {
+ kIpcTag = 0x00736674, // 'uds'
+ };
+
+ // Blocking modes for service endpoint. Controls whether the epoll set is in
+ // blocking mode or not for message receive.
+ enum {
+ kBlocking = true,
+ kNonBlocking = false,
+ kDefaultBlocking = kNonBlocking,
+ };
+
+ enum : mode_t {
+ kDefaultMode = 0,
+ };
+
+ ~Endpoint() override = default;
+
+ uint32_t GetIpcTag() const override { return kIpcTag; }
+ int SetService(Service* service) override;
+ int SetChannel(int channel_id, Channel* channel) override;
+ int CloseChannel(int channel_id) override;
+ int ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) override;
+ Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel,
+ int* channel_id) override;
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) override;
+ int DefaultHandleMessage(const MessageInfo& info) override;
+ int MessageReceive(Message* message) override;
+ int MessageReply(Message* message, int return_code) override;
+ int MessageReplyFd(Message* message, unsigned int push_fd) override;
+ int MessageReplyChannelHandle(Message* message,
+ const LocalChannelHandle& handle) override;
+ int MessageReplyChannelHandle(Message* message,
+ const BorrowedChannelHandle& handle) override;
+ int MessageReplyChannelHandle(Message* message,
+ const RemoteChannelHandle& handle) override;
+ ssize_t ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) override;
+ ssize_t WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) override;
+ FileReference PushFileHandle(Message* message,
+ const LocalHandle& handle) override;
+ FileReference PushFileHandle(Message* message,
+ const BorrowedHandle& handle) override;
+ FileReference PushFileHandle(Message* message,
+ const RemoteHandle& handle) override;
+ ChannelReference PushChannelHandle(Message* message,
+ const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) override;
+ LocalHandle GetFileHandle(Message* message, FileReference ref) const override;
+ LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const override;
+
+ void* AllocateMessageState() override;
+ void FreeMessageState(void* state) override;
+
+ int Cancel() override;
+
+ // Open an endpoint at the given path.
+ // Second parameter is unused for UDS, but we have it here for compatibility
+ // in signature with servicefs::Endpoint::Create().
+ static std::unique_ptr<Endpoint> Create(const std::string& endpoint_path,
+ mode_t /*unused_mode*/ = kDefaultMode,
+ bool blocking = kDefaultBlocking);
+
+ int epoll_fd() const { return epoll_fd_.Get(); }
+
+ private:
+ struct ChannelData {
+ LocalHandle data_fd;
+ LocalHandle event_fd;
+ uint32_t event_mask{0};
+ Channel* channel_state{nullptr};
+ };
+
+ // This class must be instantiated using Create() static methods above.
+ Endpoint(const std::string& endpoint_path, bool blocking);
+
+ Endpoint(const Endpoint&) = delete;
+ void operator=(const Endpoint&) = delete;
+
+ uint32_t GetNextAvailableMessageId() {
+ return next_message_id_.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ Status<void> AcceptConnection(Message* message);
+ Status<void> ReceiveMessageForChannel(int channel_id, Message* message);
+ Status<void> OnNewChannel(LocalHandle channel_fd);
+ Status<ChannelData*> OnNewChannelLocked(LocalHandle channel_fd,
+ Channel* channel_state);
+ int CloseChannelLocked(int channel_id);
+ Status<void> ReenableEpollEvent(int fd);
+ Channel* GetChannelState(int channel_id);
+ int GetChannelSocketFd(int channel_id);
+ int GetChannelEventFd(int channel_id);
+
+ std::string endpoint_path_;
+ bool is_blocking_;
+ LocalHandle socket_fd_;
+ LocalHandle cancel_event_fd_;
+ LocalHandle epoll_fd_;
+
+ mutable std::mutex channel_mutex_;
+ std::map<int, ChannelData> channels_;
+
+ mutable std::mutex service_mutex_;
+ std::condition_variable condition_;
+
+ Service* service_{nullptr};
+ std::atomic<uint32_t> next_message_id_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_uds/remote_method_tests.cpp b/libs/vr/libpdx_uds/remote_method_tests.cpp
new file mode 100644
index 0000000..299910c
--- /dev/null
+++ b/libs/vr/libpdx_uds/remote_method_tests.cpp
@@ -0,0 +1,907 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/service.h>
+#include <uds/client_channel.h>
+#include <uds/client_channel_factory.h>
+#include <uds/service_dispatcher.h>
+#include <uds/service_endpoint.h>
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::ClientBase;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::uds::Endpoint;
+using namespace android::pdx::rpc;
+
+namespace {
+
+// Defines a serializable user type that may be transferred between client and
+// service.
+struct TestType {
+ int a;
+ float b;
+ std::string c;
+
+ TestType() {}
+ TestType(int a, float b, const std::string& c) : a(a), b(b), c(c) {}
+
+ // Make gtest expressions simpler by defining equality operator. This is not
+ // needed for serialization.
+ bool operator==(const TestType& other) const {
+ return a == other.a && b == other.b && c == other.c;
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c);
+};
+
+struct DerivedTestType : public TestType {
+ DerivedTestType() : TestType() {}
+ DerivedTestType(int a, float b) : TestType(a, b, "constant") {}
+};
+
+// Defines a serializable user type with a LocalHandle member.
+struct TestFdType {
+ int a;
+ LocalHandle fd;
+
+ TestFdType() {}
+ TestFdType(int a, LocalHandle fd) : a(a), fd(std::move(fd)) {}
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestFdType, a, fd);
+};
+
+// Defines a serializable user template type with a FileHandle member.
+template <typename FileHandleType>
+struct TestTemplateType {
+ FileHandleType fd;
+
+ TestTemplateType() {}
+ TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+struct BasicStruct {
+ int a;
+ int b;
+ std::string c;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(BasicStruct, a, b, c);
+};
+
+using BasicStructTraits = SerializableTraits<BasicStruct>;
+
+struct NonSerializableType {
+ int a;
+ int b;
+ std::string c;
+};
+
+struct IncorrectlyDefinedSerializableType {
+ int a;
+ int b;
+
+ private:
+ using SerializableMembers = std::tuple<int, int>;
+};
+
+// Defines the contract between the client and service, including ServiceFS
+// endpoint path, method opcodes, and remote method signatures.
+struct TestInterface final {
+ // Service path.
+ static constexpr char kClientPath[] = "socket_test";
+
+ // Op codes.
+ enum {
+ kOpAdd = 0,
+ kOpFoo,
+ kOpConcatenate,
+ kOpWriteBuffer,
+ kOpStringLength,
+ kOpSendTestType,
+ kOpSendBasicStruct,
+ kOpSendVector,
+ kOpRot13,
+ kOpNoArgs,
+ kOpSendFile,
+ kOpGetFile,
+ kOpGetTestFdType,
+ kOpOpenFiles,
+ kOpReadFile,
+ kOpPushChannel,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+ PDX_REMOTE_METHOD(Foo, kOpFoo, int(int, const std::string&));
+ PDX_REMOTE_METHOD(Concatenate, kOpConcatenate,
+ std::string(const std::string&, const std::string&));
+ PDX_REMOTE_METHOD(SumVector, kOpWriteBuffer, int(const std::vector<int>&));
+ PDX_REMOTE_METHOD(StringLength, kOpStringLength, int(const std::string&));
+ PDX_REMOTE_METHOD(SendTestType, kOpSendTestType, TestType(const TestType&));
+ PDX_REMOTE_METHOD(SendBasicStruct, kOpSendBasicStruct,
+ BasicStruct(const BasicStruct&));
+ PDX_REMOTE_METHOD(SendVector, kOpSendVector,
+ std::string(const std::vector<TestType>&));
+ PDX_REMOTE_METHOD(Rot13, kOpRot13, std::string(const std::string&));
+ PDX_REMOTE_METHOD(NoArgs, kOpNoArgs, int(Void));
+ PDX_REMOTE_METHOD(SendFile, kOpSendFile, int(const LocalHandle& fd));
+ PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+ PDX_REMOTE_METHOD(GetTestFdType, kOpGetTestFdType,
+ TestFdType(int, const std::string&, int));
+ PDX_REMOTE_METHOD(OpenFiles, kOpOpenFiles,
+ std::vector<LocalHandle>(
+ const std::vector<std::pair<std::string, int>>&));
+ PDX_REMOTE_METHOD(ReadFile, kOpReadFile,
+ std::pair<int, BufferWrapper<std::uint8_t*>>(
+ const std::string&, int, std::size_t));
+ PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+
+ PDX_REMOTE_API(API, Add, Foo, Concatenate, SumVector, StringLength,
+ SendTestType, SendVector, Rot13, NoArgs, SendFile, GetFile,
+ GetTestFdType, OpenFiles, PushChannel);
+};
+
+constexpr char TestInterface::kClientPath[];
+
+// Test client to send messages to the test service.
+class TestClient : public ClientBase<TestClient> {
+ public:
+ int Add(int a, int b) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Add>(a, b));
+ }
+
+ int Foo(int a, const std::string& b) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Foo>(a, b));
+ }
+
+ std::string Concatenate(const std::string& a, const std::string& b) {
+ std::string return_value;
+
+ Status<std::string> status =
+ InvokeRemoteMethod<TestInterface::Concatenate>(a, b);
+ if (!status)
+ return std::string("[Error]");
+ else
+ return status.take();
+ }
+
+ int SumVector(const int* buffer, std::size_t size) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<TestInterface::SumVector>(WrapArray(buffer, size)));
+ }
+
+ int SumVector(const std::vector<int>& buffer) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<TestInterface::SumVector>(buffer));
+ }
+
+ int StringLength(const char* string, std::size_t size) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::StringLength>(
+ WrapString(string, size)));
+ }
+
+ int StringLength(const std::string& string) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<TestInterface::StringLength>(string));
+ }
+
+ TestType SendTestType(const TestType& tt) {
+ Status<TestType> status =
+ InvokeRemoteMethod<TestInterface::SendTestType>(tt);
+ if (!status)
+ return TestType(0, 0.0, "[Error]");
+ else
+ return status.take();
+ }
+
+ BasicStruct SendBasicStruct(const BasicStruct& bs) {
+ Status<BasicStruct> status =
+ InvokeRemoteMethod<TestInterface::SendBasicStruct>(bs);
+ if (!status)
+ return BasicStruct{0, 0, "[Error]"};
+ else
+ return status.take();
+ }
+
+ std::string SendVector(const std::vector<TestType>& v) {
+ Status<std::string> status =
+ InvokeRemoteMethod<TestInterface::SendVector>(v);
+ if (!status)
+ return "[Error]";
+ else
+ return status.take();
+ }
+
+ std::string Rot13(const std::string& string) {
+ Status<std::string> status =
+ InvokeRemoteMethod<TestInterface::Rot13>(string);
+ return status ? status.get() : string;
+ }
+
+ int NoArgs() {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::NoArgs>());
+ }
+
+ int SendFile(const LocalHandle& fd) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::SendFile>(fd));
+ }
+
+ LocalHandle GetFile(const std::string& path, int mode) {
+ Status<LocalHandle> status =
+ InvokeRemoteMethod<TestInterface::GetFile>(path, mode);
+ if (!status)
+ return LocalHandle(-status.error());
+ else
+ return status.take();
+ }
+
+ int GetFile(const std::string& path, int mode, LocalHandle* fd_out) {
+ Status<void> status =
+ InvokeRemoteMethodInPlace<TestInterface::GetFile>(fd_out, path, mode);
+ return status ? 0 : -status.error();
+ }
+
+ TestFdType GetTestFdType(int a, const std::string& path, int mode) {
+ Status<TestFdType> status =
+ InvokeRemoteMethod<TestInterface::GetTestFdType>(a, path, mode);
+ if (!status)
+ return {};
+ else
+ return status.take();
+ }
+
+ std::vector<LocalHandle> OpenFiles(
+ const std::vector<std::pair<std::string, int>>& file_specs) {
+ Status<std::vector<LocalHandle>> status =
+ InvokeRemoteMethod<TestInterface::OpenFiles>(file_specs);
+ if (!status)
+ return {};
+ else
+ return status.take();
+ }
+
+ int ReadFile(void* buffer, std::size_t size, const std::string& path,
+ int mode) {
+ auto buffer_wrapper = WrapBuffer(buffer, size);
+ auto return_value = std::make_pair(-1, buffer_wrapper);
+
+ Status<void> status = InvokeRemoteMethodInPlace<TestInterface::ReadFile>(
+ &return_value, path, mode, size);
+ return status ? return_value.first : -status.error();
+ }
+
+ int PushChannel(LocalChannelHandle* fd_out) {
+ auto status = InvokeRemoteMethodInPlace<TestInterface::PushChannel>(fd_out);
+ return status ? 0 : -status.error();
+ }
+
+ int GetFd() const { return event_fd(); }
+
+ private:
+ friend BASE;
+
+ TestClient(LocalChannelHandle channel_handle)
+ : BASE{android::pdx::uds::ClientChannel::Create(
+ std::move(channel_handle))} {}
+ TestClient()
+ : BASE{android::pdx::uds::ClientChannelFactory::Create(
+ TestInterface::kClientPath)} {}
+
+ TestClient(const TestClient&) = delete;
+ void operator=(const TestClient&) = delete;
+};
+
+// Test service that encodes/decodes messages from clients.
+class TestService : public ServiceBase<TestService> {
+ public:
+ int HandleMessage(Message& message) override {
+ switch (message.GetOp()) {
+ case TestInterface::Add::Opcode:
+ DispatchRemoteMethod<TestInterface::Add>(*this, &TestService::OnAdd,
+ message);
+ return 0;
+
+ case TestInterface::Foo::Opcode:
+ DispatchRemoteMethod<TestInterface::Foo>(*this, &TestService::OnFoo,
+ message);
+ return 0;
+
+ case TestInterface::Concatenate::Opcode:
+ DispatchRemoteMethod<TestInterface::Concatenate>(
+ *this, &TestService::OnConcatenate, message);
+ return 0;
+
+ case TestInterface::SumVector::Opcode:
+ DispatchRemoteMethod<TestInterface::SumVector>(
+ *this, &TestService::OnSumVector, message);
+ return 0;
+
+ case TestInterface::StringLength::Opcode:
+ DispatchRemoteMethod<TestInterface::StringLength>(
+ *this, &TestService::OnStringLength, message);
+ return 0;
+
+ case TestInterface::SendTestType::Opcode:
+ DispatchRemoteMethod<TestInterface::SendTestType>(
+ *this, &TestService::OnSendTestType, message);
+ return 0;
+
+ case TestInterface::SendVector::Opcode:
+ DispatchRemoteMethod<TestInterface::SendVector>(
+ *this, &TestService::OnSendVector, message);
+ return 0;
+
+ case TestInterface::Rot13::Opcode:
+ DispatchRemoteMethod<TestInterface::Rot13>(*this, &TestService::OnRot13,
+ message);
+ return 0;
+
+ case TestInterface::NoArgs::Opcode:
+ DispatchRemoteMethod<TestInterface::NoArgs>(
+ *this, &TestService::OnNoArgs, message);
+ return 0;
+
+ case TestInterface::SendFile::Opcode:
+ DispatchRemoteMethod<TestInterface::SendFile>(
+ *this, &TestService::OnSendFile, message);
+ return 0;
+
+ case TestInterface::GetFile::Opcode:
+ DispatchRemoteMethod<TestInterface::GetFile>(
+ *this, &TestService::OnGetFile, message);
+ return 0;
+
+ case TestInterface::GetTestFdType::Opcode:
+ DispatchRemoteMethod<TestInterface::GetTestFdType>(
+ *this, &TestService::OnGetTestFdType, message);
+ return 0;
+
+ case TestInterface::OpenFiles::Opcode:
+ DispatchRemoteMethod<TestInterface::OpenFiles>(
+ *this, &TestService::OnOpenFiles, message);
+ return 0;
+
+ case TestInterface::ReadFile::Opcode:
+ DispatchRemoteMethod<TestInterface::ReadFile>(
+ *this, &TestService::OnReadFile, message);
+ return 0;
+
+ case TestInterface::PushChannel::Opcode:
+ DispatchRemoteMethod<TestInterface::PushChannel>(
+ *this, &TestService::OnPushChannel, message);
+ return 0;
+
+ default:
+ return Service::DefaultHandleMessage(message);
+ }
+ }
+
+ private:
+ friend BASE;
+
+ TestService()
+ : BASE("TestService", Endpoint::Create(TestInterface::kClientPath)) {}
+
+ int OnAdd(Message&, int a, int b) { return a + b; }
+
+ int OnFoo(Message&, int a, const std::string& b) { return a + b.length(); }
+
+ std::string OnConcatenate(Message&, const std::string& a,
+ const std::string& b) {
+ return a + b;
+ }
+
+ int OnSumVector(Message&, const std::vector<int>& vector) {
+ return std::accumulate(vector.begin(), vector.end(), 0);
+ }
+
+ int OnStringLength(Message&, const std::string& string) {
+ return string.length();
+ }
+
+ TestType OnSendTestType(Message&, const TestType& tt) {
+ return TestType(tt.a + 20, tt.b - 2.0, tt.c + "foo");
+ }
+
+ std::string OnSendVector(Message&, const std::vector<TestType>& v) {
+ std::string return_value = "";
+
+ for (const auto& tt : v)
+ return_value += tt.c;
+
+ return return_value;
+ }
+
+ std::string OnRot13(Message&, const std::string& s) {
+ std::string text = s;
+ std::transform(std::begin(text), std::end(text), std::begin(text),
+ [](char c) -> char {
+ if (!std::isalpha(c)) {
+ return c;
+ } else {
+ const char pivot = std::isupper(c) ? 'A' : 'a';
+ return (c - pivot + 13) % 26 + pivot;
+ }
+ });
+ return text;
+ }
+
+ int OnNoArgs(Message&) { return 1; }
+
+ int OnSendFile(Message&, const LocalHandle& fd) { return fd.Get(); }
+
+ LocalHandle OnGetFile(Message& message, const std::string& path, int mode) {
+ LocalHandle fd(path.c_str(), mode);
+ if (!fd)
+ message.ReplyError(errno);
+ return fd;
+ }
+
+ TestFdType OnGetTestFdType(Message& message, int a, const std::string& path,
+ int mode) {
+ TestFdType return_value(a, LocalHandle(path, mode));
+ if (!return_value.fd)
+ message.ReplyError(errno);
+ return return_value;
+ }
+
+ std::vector<LocalHandle> OnOpenFiles(
+ Message&, const std::vector<std::pair<std::string, int>>& file_specs) {
+ std::vector<LocalHandle> return_value;
+ for (auto& spec : file_specs) {
+ LocalHandle fd(spec.first, spec.second);
+ if (fd)
+ return_value.emplace_back(std::move(fd));
+ else
+ return_value.emplace_back(-errno);
+ }
+ return return_value;
+ }
+
+ std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> OnReadFile(
+ Message& message, const std::string& path, int mode, std::size_t length) {
+ std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> return_value;
+ LocalHandle fd(path, mode);
+ if (!fd) {
+ message.ReplyError(errno);
+ return return_value;
+ }
+
+ return_value.second.reserve(length);
+ const int ret = read(fd.Get(), return_value.second.data(), length);
+ if (ret < 0) {
+ message.ReplyError(errno);
+ return return_value;
+ }
+
+ return_value.second.resize(ret);
+ return_value.first = ret;
+ return return_value;
+ }
+
+ RemoteChannelHandle OnPushChannel(Message& message) {
+ auto status = message.PushChannel(0, nullptr, nullptr);
+ if (!status) {
+ message.ReplyError(status.error());
+ return {};
+ }
+ return status.take();
+ }
+
+ TestService(const TestService&) = delete;
+ void operator=(const TestService&) = delete;
+};
+
+} // anonymous namespace
+
+// Use a test fixture to ensure proper order of cleanup between clients,
+// services, and the dispatcher. As these objects are cleaned up in the same
+// thread, either the service or client must be destroyed before stopping the
+// dispatcher. The reason for this is that clients send blocking "close"
+// messages to their respective services on destruction. If this happens after
+// stopping the dispatcher the client destructor will get blocked waiting for a
+// reply that will never come. In normal use of the service framework this is
+// never an issue because clients and the dispatcher for the same service are
+// never destructed in the same thread (they live in different processes).
+class RemoteMethodTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<ServiceDispatcher> dispatcher_;
+ std::thread dispatch_thread_;
+
+ void SetUp() override {
+ // Create a dispatcher to handle messages to services.
+ dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+ ASSERT_NE(nullptr, dispatcher_);
+
+ // Start the message dispatch loop in a separate thread.
+ dispatch_thread_ = std::thread(
+ std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+ }
+
+ void TearDown() override {
+ if (dispatcher_) {
+ // Cancel the dispatcher and wait for the thread to terminate.
+ // Explicitly
+ // join the thread so that destruction doesn't deallocate the
+ // dispatcher
+ // before the thread finishes.
+ dispatcher_->SetCanceled(true);
+ dispatch_thread_.join();
+ }
+ }
+};
+
+// Test basic operation of TestService/TestClient classes.
+TEST_F(RemoteMethodTest, BasicClientService) {
+ // Create a test service and add it to the dispatcher.
+
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ const int sum = client->Add(10, 25);
+ EXPECT_GE(35, sum);
+
+ const auto cat = client->Concatenate("This is a string", ", that it is.");
+ EXPECT_EQ("This is a string, that it is.", cat);
+
+ const auto length = client->Foo(10, "123");
+ EXPECT_EQ(13, length);
+
+ const std::vector<int> vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ const int vector_sum = client->SumVector(vector.data(), vector.size());
+ const int vector_sum2 = client->SumVector(vector);
+ EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum);
+ EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum2);
+
+ const auto string_length1 = client->StringLength("This is a string");
+ EXPECT_EQ(16, string_length1);
+
+ const auto string_length2 = client->StringLength("1234567890");
+ EXPECT_EQ(10, string_length2);
+
+ std::string string = "1234567890";
+ const auto string_length3 =
+ client->StringLength(string.c_str(), string.length());
+ EXPECT_EQ(10, string_length3);
+
+ TestType tt{10, 0.0, "string"};
+ const auto tt_result = client->SendTestType(tt);
+ EXPECT_EQ(TestType(30, -2.0, "stringfoo"), tt_result);
+
+ std::vector<TestType> ttv = {TestType(0, 0.0, "abc"),
+ TestType(0, 0.0, "123")};
+ const std::string string_result = client->SendVector(ttv);
+ EXPECT_EQ("abc123", string_result);
+
+ const int int_result = client->NoArgs();
+ EXPECT_EQ(1, int_result);
+}
+
+TEST_F(RemoteMethodTest, LocalHandle) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ LocalHandle fd("/dev/zero", O_RDONLY);
+ ASSERT_TRUE(fd.IsValid());
+
+ int fd_result = client->SendFile(fd);
+ EXPECT_LE(0, fd_result);
+ EXPECT_NE(fd.Get(), fd_result);
+ fd = LocalHandle(-3);
+ fd_result = client->SendFile(fd);
+ EXPECT_EQ(fd.Get(), fd_result);
+
+ fd = client->GetFile("/dev/zero", O_RDONLY);
+ ASSERT_TRUE(fd.IsValid()) << "Error code: " << fd.Get();
+
+ std::array<uint8_t, 10> buffer;
+ buffer.fill(1);
+ EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size()));
+ EXPECT_EQ(buffer, decltype(buffer){{0}});
+ fd.Close();
+
+ const int error = client->GetFile("/dev/zero", O_RDONLY, &fd);
+ EXPECT_EQ(0, error);
+ EXPECT_TRUE(fd.IsValid());
+
+ buffer.fill(1);
+ EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size()));
+ EXPECT_EQ(buffer, decltype(buffer){{0}});
+
+ /*
+ Seg fault.
+ fd = client->GetFile("/dev/foobar", O_RDONLY);
+ EXPECT_FALSE(fd.IsValid());
+ */
+}
+
+TEST_F(RemoteMethodTest, PushChannel) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ // Get a new channel as an fd.
+ LocalChannelHandle channel;
+ const int ret = client->PushChannel(&channel);
+ EXPECT_EQ(0, ret);
+ EXPECT_TRUE(channel.valid());
+
+ // Create a new client from the channel.
+ auto client2 = TestClient::Create(std::move(channel));
+ ASSERT_NE(nullptr, client2);
+
+ // Test that the new channel works.
+ const int sum = client2->Add(10, 25);
+ EXPECT_GE(35, sum);
+}
+
+TEST_F(RemoteMethodTest, AggregateLocalHandle) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ TestFdType result = client->GetTestFdType(10, "/dev/zero", O_RDONLY);
+ EXPECT_TRUE(result.fd.IsValid());
+ EXPECT_EQ(10, result.a);
+
+ std::vector<LocalHandle> files =
+ client->OpenFiles({{{"/dev/zero", O_RDONLY},
+ {"/dev/null", O_WRONLY},
+ {"/dev/zero", O_RDONLY}}});
+ ASSERT_EQ(3u, files.size());
+ EXPECT_TRUE(files[0].IsValid());
+ EXPECT_TRUE(files[1].IsValid());
+ EXPECT_TRUE(files[2].IsValid());
+}
+
+TEST_F(RemoteMethodTest, BufferWrapper) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ const int buffer_size = 20;
+ std::vector<std::uint8_t> buffer(buffer_size, 'x');
+ std::vector<std::uint8_t> expected(buffer_size, 0);
+ int ret =
+ client->ReadFile(buffer.data(), buffer.size(), "/dev/zero", O_RDONLY);
+ EXPECT_EQ(buffer_size, ret);
+ EXPECT_EQ(expected, buffer);
+}
+
+//
+// RemoteMethodFramework: Tests the type-based framework that remote method
+// support is built upon.
+//
+
+// Test logical And template.
+TEST(RemoteMethodFramework, And) {
+ EXPECT_TRUE((And<std::true_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::false_type>::value));
+
+ EXPECT_TRUE((And<std::true_type, std::true_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::true_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::false_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::false_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::true_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::true_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::false_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::false_type, std::false_type>::value));
+}
+
+// Test convertible type constraints.
+TEST(RemoteMethodFramework, IsConvertible) {
+ // std::pair.
+ EXPECT_TRUE(
+ (IsConvertible<std::pair<int, float>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, float>, std::pair<float, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, float>, std::pair<float, int>>::value));
+
+ // Nested std::pair.
+ EXPECT_TRUE((IsConvertible<std::pair<std::pair<int, float>, float>,
+ std::pair<std::pair<int, float>, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::pair<int, float>, float>,
+ std::pair<std::pair<float, int>, float>>::value));
+
+ // std::tuple and std::pair.
+ EXPECT_TRUE(
+ (IsConvertible<std::pair<int, float>, std::tuple<int, float>>::value));
+ EXPECT_TRUE(
+ (IsConvertible<std::tuple<int, float>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<float, float>, std::tuple<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::tuple<float, float>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, int>, std::tuple<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::tuple<int, int>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, int>, std::tuple<int, int, int>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::tuple<int, int, int>, std::pair<int, int>>::value));
+
+ // std::vector.
+ EXPECT_TRUE((IsConvertible<std::vector<int>, std::vector<int>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<int>, std::vector<float>>::value));
+
+ // Nested std::vector.
+ EXPECT_TRUE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<int, int>>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<int, float>>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<float, int>>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<float, float>>>::value));
+
+ // std::vector with nested convertible types.
+ EXPECT_TRUE((IsConvertible<std::vector<StringWrapper<char>>,
+ std::vector<std::string>>::value));
+
+ // std::map and std::unordered_map.
+ EXPECT_TRUE((IsConvertible<std::map<int, float>,
+ std::unordered_map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::map<float, float>,
+ std::unordered_map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::map<float, float>,
+ std::unordered_map<float, int>>::value));
+ EXPECT_FALSE((IsConvertible<std::map<float, float>,
+ std::unordered_map<int, int>>::value));
+ EXPECT_TRUE((IsConvertible<std::unordered_map<int, float>,
+ std::map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+ std::map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+ std::map<float, int>>::value));
+ EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+ std::map<int, int>>::value));
+
+ // std::map with nested convertible types.
+ EXPECT_TRUE((IsConvertible<std::map<int, std::string>,
+ std::map<int, StringWrapper<char>>>::value));
+ EXPECT_TRUE(
+ (IsConvertible<std::map<std::tuple<int, int>, std::string>,
+ std::map<std::pair<int, int>, std::string>>::value));
+
+ // std::unordered_map with nested convertible types.
+ EXPECT_TRUE(
+ (IsConvertible<std::unordered_map<int, std::string>,
+ std::unordered_map<int, StringWrapper<char>>>::value));
+ EXPECT_TRUE((IsConvertible<
+ std::unordered_map<std::tuple<int, int>, std::string>,
+ std::unordered_map<std::pair<int, int>, std::string>>::value));
+
+ // std::string.
+ EXPECT_TRUE((IsConvertible<std::string, std::string>::value));
+ EXPECT_FALSE((IsConvertible<std::string, int>::value));
+ EXPECT_FALSE((IsConvertible<int, std::string>::value));
+
+ // Nested std::string.
+ EXPECT_TRUE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<std::string, std::string>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<std::string, int>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<int, std::string>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<int, int>>::value));
+
+ // StringWrapper.
+ EXPECT_TRUE((IsConvertible<StringWrapper<char>, StringWrapper<char>>::value));
+ EXPECT_TRUE((IsConvertible<StringWrapper<char>, std::string>::value));
+ EXPECT_TRUE((IsConvertible<std::string, StringWrapper<char>>::value));
+ EXPECT_FALSE((IsConvertible<StringWrapper<char>, int>::value));
+ EXPECT_FALSE(
+ (IsConvertible<StringWrapper<char>, BufferWrapper<char*>>::value));
+
+ // BufferWrapper.
+ EXPECT_TRUE(
+ (IsConvertible<BufferWrapper<char*>, BufferWrapper<char*>>::value));
+ EXPECT_TRUE(
+ (IsConvertible<BufferWrapper<char*>, BufferWrapper<const char*>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<BufferWrapper<char*>, BufferWrapper<int*>>::value));
+ EXPECT_TRUE((IsConvertible<BufferWrapper<char*>,
+ BufferWrapper<std::vector<char>>>::value));
+
+ // RemoteHandle and BorrowedHandle.
+ EXPECT_TRUE((IsConvertible<LocalHandle, RemoteHandle>::value));
+ EXPECT_TRUE((IsConvertible<LocalHandle, BorrowedHandle>::value));
+
+ // Test rewriting user defined types.
+ EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>,
+ TestTemplateType<RemoteHandle>>::value));
+ EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>,
+ TestTemplateType<BorrowedHandle>>::value));
+ EXPECT_FALSE((IsConvertible<TestTemplateType<RemoteHandle>,
+ TestTemplateType<LocalHandle>>::value));
+ EXPECT_FALSE((IsConvertible<TestTemplateType<BorrowedHandle>,
+ TestTemplateType<LocalHandle>>::value));
+
+ // TODO(eieio): More thorough testing of convertible types.
+}
+
+TEST(RemoteMethodFramework, SerializableMembers) {
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value);
+
+ EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+ TestTemplateType<LocalHandle>>>::value);
+ EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+ TestTemplateType<RemoteHandle>>>::value);
+ EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+ TestTemplateType<BorrowedHandle>>>::value);
+
+ EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value);
+
+ EXPECT_TRUE(HasSerializableMembers<BasicStruct>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestType>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value);
+ EXPECT_FALSE(HasSerializableMembers<NonSerializableType>::value);
+ EXPECT_FALSE(
+ HasSerializableMembers<IncorrectlyDefinedSerializableType>::value);
+}
+
+TEST(RemoteMethodFramework, RemoteAPITypes) {
+ EXPECT_EQ(0u, TestInterface::API::MethodIndex<TestInterface::Add>());
+}
diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx_uds/service_dispatcher.cpp
new file mode 100644
index 0000000..8a40158
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_dispatcher.cpp
@@ -0,0 +1,205 @@
+#define LOG_TAG "ServiceDispatcher"
+#include "uds/service_dispatcher.h"
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include "pdx/service.h"
+#include "uds/service_endpoint.h"
+
+#define TRACE 0
+
+static const int kMaxEventsPerLoop = 128;
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+std::unique_ptr<pdx::ServiceDispatcher> ServiceDispatcher::Create() {
+ std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()};
+ if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) {
+ dispatcher.reset();
+ }
+
+ return std::move(dispatcher);
+}
+
+ServiceDispatcher::ServiceDispatcher() {
+ event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (!event_fd_) {
+ ALOGE("Failed to create event fd because: %s\n", strerror(errno));
+ return;
+ }
+
+ epoll_fd_.Reset(epoll_create(1)); // Size arg is ignored, but must be > 0.
+ if (!epoll_fd_) {
+ ALOGE("Failed to create epoll fd because: %s\n", strerror(errno));
+ return;
+ }
+
+ // Use "this" as a unique pointer to distinguish the event fd from all
+ // the other entries that point to instances of Service.
+ epoll_event event;
+ event.events = EPOLLIN;
+ event.data.ptr = this;
+
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
+ ALOGE("Failed to add event fd to epoll fd because: %s\n", strerror(errno));
+
+ // Close the fds here and signal failure to the factory method.
+ event_fd_.Close();
+ epoll_fd_.Close();
+ }
+}
+
+ServiceDispatcher::~ServiceDispatcher() { SetCanceled(true); }
+
+int ServiceDispatcher::ThreadEnter() {
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ if (canceled_)
+ return -EBUSY;
+
+ thread_count_++;
+ return 0;
+}
+
+void ServiceDispatcher::ThreadExit() {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ thread_count_--;
+ condition_.notify_one();
+}
+
+int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) {
+ if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag)
+ return -EINVAL;
+
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ auto* endpoint = static_cast<Endpoint*>(service->endpoint());
+ epoll_event event;
+ event.events = EPOLLIN;
+ event.data.ptr = service.get();
+
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, endpoint->epoll_fd(), &event) <
+ 0) {
+ ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ services_.push_back(service);
+ return 0;
+}
+
+int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) {
+ if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag)
+ return -EINVAL;
+
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ // It's dangerous to remove a service while other threads may be using it.
+ if (thread_count_ > 0)
+ return -EBUSY;
+
+ epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+
+ auto* endpoint = static_cast<Endpoint*>(service->endpoint());
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, endpoint->epoll_fd(), &dummy) <
+ 0) {
+ ALOGE("Failed to remove service from dispatcher because: %s\n",
+ strerror(errno));
+ return -errno;
+ }
+
+ services_.remove(service);
+ return 0;
+}
+
+int ServiceDispatcher::ReceiveAndDispatch() { return ReceiveAndDispatch(-1); }
+
+int ServiceDispatcher::ReceiveAndDispatch(int timeout) {
+ int ret = ThreadEnter();
+ if (ret < 0)
+ return ret;
+
+ epoll_event events[kMaxEventsPerLoop];
+
+ int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, timeout);
+ if (count <= 0) {
+ ALOGE_IF(count < 0, "Failed to wait for epoll events because: %s\n",
+ strerror(errno));
+ ThreadExit();
+ return count < 0 ? -errno : -ETIMEDOUT;
+ }
+
+ for (int i = 0; i < count; i++) {
+ if (events[i].data.ptr == this) {
+ ThreadExit();
+ return -EBUSY;
+ } else {
+ Service* service = static_cast<Service*>(events[i].data.ptr);
+
+ ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
+ static_cast<Endpoint*>(service->endpoint())->epoll_fd());
+ service->ReceiveAndDispatch();
+ }
+ }
+
+ ThreadExit();
+ return 0;
+}
+
+int ServiceDispatcher::EnterDispatchLoop() {
+ int ret = ThreadEnter();
+ if (ret < 0)
+ return ret;
+
+ epoll_event events[kMaxEventsPerLoop];
+
+ while (!IsCanceled()) {
+ int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, -1);
+ if (count < 0 && errno != EINTR) {
+ ALOGE("Failed to wait for epoll events because: %s\n", strerror(errno));
+ ThreadExit();
+ return -errno;
+ }
+
+ for (int i = 0; i < count; i++) {
+ if (events[i].data.ptr == this) {
+ ThreadExit();
+ return -EBUSY;
+ } else {
+ Service* service = static_cast<Service*>(events[i].data.ptr);
+
+ ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
+ static_cast<Endpoint*>(service->endpoint())->epoll_fd());
+ service->ReceiveAndDispatch();
+ }
+ }
+ }
+
+ ThreadExit();
+ return 0;
+}
+
+void ServiceDispatcher::SetCanceled(bool cancel) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ canceled_ = cancel;
+
+ if (canceled_ && thread_count_ > 0) {
+ eventfd_write(event_fd_.Get(), 1); // Signal threads to quit.
+
+ condition_.wait(lock, [this] { return !(canceled_ && thread_count_ > 0); });
+
+ eventfd_t value;
+ eventfd_read(event_fd_.Get(), &value); // Unsignal.
+ }
+}
+
+bool ServiceDispatcher::IsCanceled() const { return canceled_; }
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
new file mode 100644
index 0000000..b6021e8
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -0,0 +1,632 @@
+#include "uds/service_endpoint.h"
+
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <algorithm> // std::min
+
+#include <pdx/service.h>
+#include <uds/channel_manager.h>
+#include <uds/client_channel_factory.h>
+#include <uds/ipc_helper.h>
+
+namespace {
+
+constexpr int kMaxBackLogForSocketListen = 1;
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ChannelReference;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+using android::pdx::uds::ChannelInfo;
+using android::pdx::uds::ChannelManager;
+
+struct MessageState {
+ bool GetLocalFileHandle(int index, LocalHandle* handle) {
+ if (index < 0) {
+ handle->Reset(index);
+ } else if (static_cast<size_t>(index) < request.file_descriptors.size()) {
+ *handle = std::move(request.file_descriptors[index]);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
+ if (index < 0) {
+ *handle = LocalChannelHandle{nullptr, index};
+ } else if (static_cast<size_t>(index) < request.channels.size()) {
+ auto& channel_info = request.channels[index];
+ *handle = ChannelManager::Get().CreateHandle(
+ std::move(channel_info.data_fd), std::move(channel_info.event_fd));
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ FileReference PushFileHandle(BorrowedHandle handle) {
+ if (!handle)
+ return handle.Get();
+ response.file_descriptors.push_back(std::move(handle));
+ return response.file_descriptors.size() - 1;
+ }
+
+ ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
+ if (!handle)
+ return handle.value();
+ ChannelInfo<BorrowedHandle> channel_info;
+ channel_info.data_fd.Reset(handle.value());
+ channel_info.event_fd.Reset(
+ ChannelManager::Get().GetEventFd(handle.value()));
+ response.channels.push_back(std::move(channel_info));
+ return response.channels.size() - 1;
+ }
+
+ ChannelReference PushChannelHandle(BorrowedHandle data_fd,
+ BorrowedHandle event_fd) {
+ if (!data_fd || !event_fd)
+ return -1;
+ ChannelInfo<BorrowedHandle> channel_info;
+ channel_info.data_fd = std::move(data_fd);
+ channel_info.event_fd = std::move(event_fd);
+ response.channels.push_back(std::move(channel_info));
+ return response.channels.size() - 1;
+ }
+
+ ssize_t WriteData(const iovec* vector, size_t vector_length) {
+ ssize_t size = 0;
+ for (size_t i = 0; i < vector_length; i++) {
+ const auto* data = reinterpret_cast<const uint8_t*>(vector[i].iov_base);
+ response_data.insert(response_data.end(), data, data + vector[i].iov_len);
+ size += vector[i].iov_len;
+ }
+ return size;
+ }
+
+ ssize_t ReadData(const iovec* vector, size_t vector_length) {
+ size_t size_remaining = request_data.size() - request_data_read_pos;
+ ssize_t size = 0;
+ for (size_t i = 0; i < vector_length && size_remaining > 0; i++) {
+ size_t size_to_copy = std::min(size_remaining, vector[i].iov_len);
+ memcpy(vector[i].iov_base, request_data.data() + request_data_read_pos,
+ size_to_copy);
+ size += size_to_copy;
+ request_data_read_pos += size_to_copy;
+ size_remaining -= size_to_copy;
+ }
+ return size;
+ }
+
+ android::pdx::uds::RequestHeader<LocalHandle> request;
+ android::pdx::uds::ResponseHeader<BorrowedHandle> response;
+ std::vector<LocalHandle> sockets_to_close;
+ std::vector<uint8_t> request_data;
+ size_t request_data_read_pos{0};
+ std::vector<uint8_t> response_data;
+};
+
+} // anonymous namespace
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+Endpoint::Endpoint(const std::string& endpoint_path, bool blocking)
+ : endpoint_path_{ClientChannelFactory::GetEndpointPath(endpoint_path)},
+ is_blocking_{blocking} {
+ LocalHandle fd{socket(AF_UNIX, SOCK_STREAM, 0)};
+ if (!fd) {
+ ALOGE("Endpoint::Endpoint: Failed to create socket: %s", strerror(errno));
+ return;
+ }
+
+ sockaddr_un local;
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, endpoint_path_.c_str(), sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+ unlink(local.sun_path);
+ if (bind(fd.Get(), (struct sockaddr*)&local, sizeof(local)) == -1) {
+ ALOGE("Endpoint::Endpoint: bind error: %s", strerror(errno));
+ return;
+ }
+ if (listen(fd.Get(), kMaxBackLogForSocketListen) == -1) {
+ ALOGE("Endpoint::Endpoint: listen error: %s", strerror(errno));
+ return;
+ }
+
+ cancel_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (!cancel_event_fd_) {
+ ALOGE("Endpoint::Endpoint: Failed to create event fd: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ epoll_fd_.Reset(epoll_create(1)); // Size arg is ignored, but must be > 0.
+ if (!epoll_fd_) {
+ ALOGE("Endpoint::Endpoint: Failed to create epoll fd: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ // Use "this" as a unique pointer to distinguish the event fd from all
+ // the other entries that point to instances of Service.
+ epoll_event socket_event;
+ socket_event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+ socket_event.data.fd = fd.Get();
+
+ epoll_event cancel_event;
+ cancel_event.events = EPOLLIN;
+ cancel_event.data.fd = cancel_event_fd_.Get();
+
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd.Get(), &socket_event) < 0 ||
+ epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, cancel_event_fd_.Get(),
+ &cancel_event) < 0) {
+ ALOGE("Endpoint::Endpoint: Failed to add event fd to epoll fd: %s\n",
+ strerror(errno));
+ cancel_event_fd_.Close();
+ epoll_fd_.Close();
+ } else {
+ socket_fd_ = std::move(fd);
+ }
+}
+
+void* Endpoint::AllocateMessageState() { return new MessageState; }
+
+void Endpoint::FreeMessageState(void* state) {
+ delete static_cast<MessageState*>(state);
+}
+
+Status<void> Endpoint::AcceptConnection(Message* message) {
+ sockaddr_un remote;
+ socklen_t addrlen = sizeof(remote);
+ LocalHandle channel_fd{
+ accept(socket_fd_.Get(), reinterpret_cast<sockaddr*>(&remote), &addrlen)};
+ if (!channel_fd) {
+ ALOGE("Endpoint::AcceptConnection: failed to accept connection: %s",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ int optval = 1;
+ if (setsockopt(channel_fd.Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+ sizeof(optval)) == -1) {
+ ALOGE(
+ "Endpoint::AcceptConnection: Failed to enable the receiving of the "
+ "credentials for channel %d: %s",
+ channel_fd.Get(), strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ auto status = ReceiveMessageForChannel(channel_fd.Get(), message);
+ if (status)
+ status = OnNewChannel(std::move(channel_fd));
+ return status;
+}
+
+int Endpoint::SetService(Service* service) {
+ service_ = service;
+ return 0;
+}
+
+int Endpoint::SetChannel(int channel_id, Channel* channel) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ if (channel_data == channels_.end())
+ return -EINVAL;
+ channel_data->second.channel_state = channel;
+ return 0;
+}
+
+Status<void> Endpoint::OnNewChannel(LocalHandle channel_fd) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ Status<void> status;
+ status.PropagateError(OnNewChannelLocked(std::move(channel_fd), nullptr));
+ return status;
+}
+
+Status<Endpoint::ChannelData*> Endpoint::OnNewChannelLocked(
+ LocalHandle channel_fd, Channel* channel_state) {
+ epoll_event event;
+ event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+ event.data.fd = channel_fd.Get();
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, channel_fd.Get(), &event) < 0) {
+ ALOGE(
+ "Endpoint::OnNewChannelLocked: Failed to add channel to endpoint: %s\n",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+ ChannelData channel_data;
+ int channel_id = channel_fd.Get();
+ channel_data.data_fd = std::move(channel_fd);
+ channel_data.event_fd.Reset(eventfd(0, 0));
+ channel_data.channel_state = channel_state;
+ auto pair = channels_.emplace(channel_id, std::move(channel_data));
+ return &pair.first->second;
+}
+
+Status<void> Endpoint::ReenableEpollEvent(int fd) {
+ epoll_event event;
+ event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+ event.data.fd = fd;
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, fd, &event) < 0) {
+ ALOGE(
+ "Endpoint::ReenableEpollEvent: Failed to re-enable channel to "
+ "endpoint: %s\n",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+ return {};
+}
+
+int Endpoint::CloseChannel(int channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ return CloseChannelLocked(channel_id);
+}
+
+int Endpoint::CloseChannelLocked(int channel_id) {
+ auto channel_data = channels_.find(channel_id);
+ if (channel_data == channels_.end())
+ return -EINVAL;
+
+ int ret = 0;
+ epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_id, &dummy) < 0) {
+ ret = -errno;
+ ALOGE(
+ "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
+ "%s\n",
+ strerror(errno));
+ }
+
+ channels_.erase(channel_data);
+ return ret;
+}
+
+int Endpoint::ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ if (channel_data == channels_.end())
+ return -EINVAL;
+ int old_mask = channel_data->second.event_mask;
+ int new_mask = (old_mask & ~clear_mask) | set_mask;
+
+ // EPOLLHUP shares the same bitmask with POLLHUP.
+ if ((new_mask & POLLHUP) && !(old_mask & POLLHUP)) {
+ CloseChannelLocked(channel_id);
+ return 0;
+ }
+
+ // EPOLLIN shares the same bitmask with POLLIN and EPOLLPRI shares the same
+ // bitmask with POLLPRI
+ eventfd_t value = 1;
+ if (((new_mask & POLLIN) && !(old_mask & POLLIN)) ||
+ ((new_mask & POLLPRI) && !(old_mask & POLLPRI))) {
+ eventfd_write(channel_data->second.event_fd.Get(), value);
+ } else if ((!(new_mask & POLLIN) && (old_mask & POLLIN)) ||
+ (!(new_mask & POLLPRI) && (old_mask & POLLPRI))) {
+ eventfd_read(channel_data->second.event_fd.Get(), &value);
+ }
+
+ channel_data->second.event_mask = new_mask;
+ return 0;
+}
+
+Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message,
+ int /*flags*/,
+ Channel* channel,
+ int* channel_id) {
+ int channel_pair[2] = {};
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel_pair) == -1) {
+ ALOGE("Endpoint::PushChannel: Failed to create a socket pair: %s",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ LocalHandle local_socket{channel_pair[0]};
+ LocalHandle remote_socket{channel_pair[1]};
+
+ int optval = 1;
+ if (setsockopt(local_socket.Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+ sizeof(optval)) == -1) {
+ ALOGE(
+ "Endpoint::PushChannel: Failed to enable the receiving of the "
+ "credentials for channel %d: %s",
+ local_socket.Get(), strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ *channel_id = local_socket.Get();
+ auto channel_data = OnNewChannelLocked(std::move(local_socket), channel);
+ if (!channel_data)
+ return ErrorStatus(channel_data.error());
+
+ // Flags are ignored for now.
+ // TODO(xiaohuit): Implement those.
+
+ auto* state = static_cast<MessageState*>(message->GetState());
+ ChannelReference ref = state->PushChannelHandle(
+ remote_socket.Borrow(), channel_data.get()->event_fd.Borrow());
+ state->sockets_to_close.push_back(std::move(remote_socket));
+ return RemoteChannelHandle{ref};
+}
+
+Status<int> Endpoint::CheckChannel(const Message* /*message*/,
+ ChannelReference /*ref*/,
+ Channel** /*channel*/) {
+ // TODO(xiaohuit): Implement this.
+ return ErrorStatus(EFAULT);
+}
+
+int Endpoint::DefaultHandleMessage(const MessageInfo& /* info */) {
+ ALOGE(
+ "Endpoint::CheckChannel: Not implemented! Endpoint DefaultHandleMessage "
+ "does nothing!");
+ return 0;
+}
+
+Channel* Endpoint::GetChannelState(int channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ return (channel_data != channels_.end()) ? channel_data->second.channel_state
+ : nullptr;
+}
+
+int Endpoint::GetChannelSocketFd(int channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ return (channel_data != channels_.end()) ? channel_data->second.data_fd.Get()
+ : -1;
+}
+
+int Endpoint::GetChannelEventFd(int channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ return (channel_data != channels_.end()) ? channel_data->second.event_fd.Get()
+ : -1;
+}
+
+Status<void> Endpoint::ReceiveMessageForChannel(int channel_id,
+ Message* message) {
+ RequestHeader<LocalHandle> request;
+ auto status = ReceiveData(channel_id, &request);
+ if (!status) {
+ CloseChannel(channel_id);
+ return status;
+ }
+ MessageInfo info;
+ info.pid = request.cred.pid;
+ info.tid = -1;
+ info.cid = channel_id;
+ info.mid = request.is_impulse ? Message::IMPULSE_MESSAGE_ID
+ : GetNextAvailableMessageId();
+ info.euid = request.cred.uid;
+ info.egid = request.cred.gid;
+ info.op = request.op;
+ info.flags = 0;
+ info.service = service_;
+ info.channel = GetChannelState(channel_id);
+ info.send_len = request.send_len;
+ info.recv_len = request.max_recv_len;
+ info.fd_count = request.file_descriptors.size();
+ static_assert(sizeof(info.impulse) == request.impulse_payload.size(),
+ "Impulse payload sizes must be the same in RequestHeader and "
+ "MessageInfo");
+ memcpy(info.impulse, request.impulse_payload.data(),
+ request.impulse_payload.size());
+ *message = Message{info};
+ auto* state = static_cast<MessageState*>(message->GetState());
+ state->request = std::move(request);
+ if (request.send_len > 0 && !request.is_impulse) {
+ state->request_data.resize(request.send_len);
+ status = ReceiveData(channel_id, state->request_data.data(),
+ state->request_data.size());
+ }
+
+ if (status && request.is_impulse)
+ status = ReenableEpollEvent(channel_id);
+
+ if (!status)
+ CloseChannel(channel_id);
+
+ return status;
+}
+
+int Endpoint::MessageReceive(Message* message) {
+ {
+ std::unique_lock<std::mutex> lock(service_mutex_);
+ condition_.wait(lock, [this] { return service_ != nullptr; });
+ }
+
+ // One event at a time.
+ epoll_event event;
+ int count = RETRY_EINTR(
+ epoll_wait(epoll_fd_.Get(), &event, 1, is_blocking_ ? -1 : 0));
+ if (count < 0) {
+ ALOGE("Endpoint::MessageReceive: Failed to wait for epoll events: %s\n",
+ strerror(errno));
+ return -errno;
+ } else if (count == 0) {
+ return -ETIMEDOUT;
+ }
+
+ if (event.data.fd == cancel_event_fd_.Get()) {
+ return -ESHUTDOWN;
+ }
+
+ if (event.data.fd == socket_fd_.Get()) {
+ auto status = AcceptConnection(message);
+ if (!status)
+ return -status.error();
+ status = ReenableEpollEvent(socket_fd_.Get());
+ return status ? 0 : -status.error();
+ }
+
+ int channel_id = event.data.fd;
+ if (event.events & EPOLLRDHUP) {
+ MessageInfo info;
+ info.pid = -1;
+ info.tid = -1;
+ info.cid = channel_id;
+ info.mid = GetNextAvailableMessageId();
+ info.euid = -1;
+ info.egid = -1;
+ info.op = opcodes::CHANNEL_CLOSE;
+ info.flags = 0;
+ info.service = service_;
+ info.channel = GetChannelState(channel_id);
+ info.send_len = 0;
+ info.recv_len = 0;
+ info.fd_count = 0;
+ *message = Message{info};
+ return 0;
+ }
+
+ auto status = ReceiveMessageForChannel(channel_id, message);
+ if (!status)
+ return -status.error();
+ return 0;
+}
+
+int Endpoint::MessageReply(Message* message, int return_code) {
+ int channel_socket = GetChannelSocketFd(message->GetChannelId());
+ if (channel_socket < 0)
+ return -EBADF;
+
+ auto* state = static_cast<MessageState*>(message->GetState());
+ switch (message->GetOp()) {
+ case opcodes::CHANNEL_CLOSE:
+ return CloseChannel(channel_socket);
+
+ case opcodes::CHANNEL_OPEN:
+ if (return_code < 0)
+ return CloseChannel(channel_socket);
+ // Reply with the event fd.
+ return_code = state->PushFileHandle(
+ BorrowedHandle{GetChannelEventFd(channel_socket)});
+ state->response_data.clear(); // Just in case...
+ break;
+ }
+
+ state->response.ret_code = return_code;
+ state->response.recv_len = state->response_data.size();
+ auto status = SendData(channel_socket, state->response);
+ if (status && !state->response_data.empty()) {
+ status = SendData(channel_socket, state->response_data.data(),
+ state->response_data.size());
+ }
+
+ if (status)
+ status = ReenableEpollEvent(channel_socket);
+
+ return status ? 0 : -status.error();
+}
+
+int Endpoint::MessageReplyFd(Message* message, unsigned int push_fd) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ auto ref = state->PushFileHandle(BorrowedHandle{static_cast<int>(push_fd)});
+ return MessageReply(message, ref);
+}
+
+int Endpoint::MessageReplyChannelHandle(Message* message,
+ const LocalChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ auto ref = state->PushChannelHandle(handle.Borrow());
+ return MessageReply(message, ref);
+}
+
+int Endpoint::MessageReplyChannelHandle(Message* message,
+ const BorrowedChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ auto ref = state->PushChannelHandle(handle.Duplicate());
+ return MessageReply(message, ref);
+}
+
+int Endpoint::MessageReplyChannelHandle(Message* message,
+ const RemoteChannelHandle& handle) {
+ return MessageReply(message, handle.value());
+}
+
+ssize_t Endpoint::ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->ReadData(vector, vector_length);
+}
+
+ssize_t Endpoint::WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->WriteData(vector, vector_length);
+}
+
+FileReference Endpoint::PushFileHandle(Message* message,
+ const LocalHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushFileHandle(handle.Borrow());
+}
+
+FileReference Endpoint::PushFileHandle(Message* message,
+ const BorrowedHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushFileHandle(handle.Duplicate());
+}
+
+FileReference Endpoint::PushFileHandle(Message* /*message*/,
+ const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+ChannelReference Endpoint::PushChannelHandle(Message* message,
+ const LocalChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushChannelHandle(handle.Borrow());
+}
+
+ChannelReference Endpoint::PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushChannelHandle(handle.Duplicate());
+}
+
+ChannelReference Endpoint::PushChannelHandle(
+ Message* /*message*/, const RemoteChannelHandle& handle) {
+ return handle.value();
+}
+
+LocalHandle Endpoint::GetFileHandle(Message* message, FileReference ref) const {
+ LocalHandle handle;
+ auto* state = static_cast<MessageState*>(message->GetState());
+ state->GetLocalFileHandle(ref, &handle);
+ return handle;
+}
+
+LocalChannelHandle Endpoint::GetChannelHandle(Message* message,
+ ChannelReference ref) const {
+ LocalChannelHandle handle;
+ auto* state = static_cast<MessageState*>(message->GetState());
+ state->GetLocalChannelHandle(ref, &handle);
+ return handle;
+}
+
+int Endpoint::Cancel() {
+ return (eventfd_write(cancel_event_fd_.Get(), 1) < 0) ? -errno : 0;
+}
+
+std::unique_ptr<Endpoint> Endpoint::Create(const std::string& endpoint_path,
+ mode_t /*unused_mode*/,
+ bool blocking) {
+ return std::unique_ptr<Endpoint>(new Endpoint(endpoint_path, blocking));
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/service_framework_tests.cpp b/libs/vr/libpdx_uds/service_framework_tests.cpp
new file mode 100644
index 0000000..8891600
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_framework_tests.cpp
@@ -0,0 +1,677 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <array>
+#include <atomic>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/service.h>
+#include <private/android_filesystem_config.h>
+#include <uds/client_channel.h>
+#include <uds/client_channel_factory.h>
+#include <uds/service_dispatcher.h>
+#include <uds/service_endpoint.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ClientBase;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::uds::Endpoint;
+
+namespace {
+
+const size_t kLargeDataSize = 100000;
+
+const char kTestServicePath[] = "socket_test";
+const char kTestService1[] = "1";
+const char kTestService2[] = "2";
+
+enum test_op_codes {
+ TEST_OP_GET_SERVICE_ID,
+ TEST_OP_SET_TEST_CHANNEL,
+ TEST_OP_GET_THIS_CHANNEL_ID,
+ TEST_OP_GET_TEST_CHANNEL_ID,
+ TEST_OP_CHECK_CHANNEL_ID,
+ TEST_OP_CHECK_CHANNEL_OBJECT,
+ TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE,
+ TEST_OP_GET_NEW_CHANNEL,
+ TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE,
+ TEST_OP_GET_THIS_PROCESS_ID,
+ TEST_OP_GET_THIS_THREAD_ID,
+ TEST_OP_GET_THIS_EUID,
+ TEST_OP_GET_THIS_EGID,
+ TEST_OP_IMPULSE,
+ TEST_OP_POLLHUP_FROM_SERVICE,
+ TEST_OP_POLLIN_FROM_SERVICE,
+ TEST_OP_SEND_LARGE_DATA_RETURN_SUM,
+};
+
+using ImpulsePayload = std::array<std::uint8_t, sizeof(MessageInfo::impulse)>;
+
+// The test service creates a TestChannel for every client (channel) that
+// connects. This represents the service-side context for each client.
+class TestChannel : public Channel {
+ public:
+ explicit TestChannel(int channel_id) : channel_id_(channel_id) {}
+
+ int channel_id() const { return channel_id_; }
+
+ private:
+ friend class TestService;
+
+ int channel_id_;
+
+ TestChannel(const TestChannel&) = delete;
+ void operator=(const TestChannel&) = delete;
+};
+
+// Test service that creates a TestChannel for each channel and responds to test
+// messages.
+class TestService : public ServiceBase<TestService> {
+ public:
+ std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
+ return std::make_shared<TestChannel>(message.GetChannelId());
+ }
+
+ void OnChannelClose(Message& /*message*/,
+ const std::shared_ptr<Channel>& channel) override {
+ if (test_channel_ == channel)
+ test_channel_ = nullptr;
+ }
+
+ void HandleImpulse(Message& message) override {
+ switch (message.GetOp()) {
+ case TEST_OP_SET_TEST_CHANNEL:
+ test_channel_ = message.GetChannel<TestChannel>();
+ break;
+
+ case TEST_OP_IMPULSE: {
+ impulse_payload_.fill(0);
+ std::copy(message.ImpulseBegin(), message.ImpulseEnd(),
+ impulse_payload_.begin());
+ break;
+ }
+
+ case TEST_OP_POLLHUP_FROM_SERVICE: {
+ message.ModifyChannelEvents(0, EPOLLHUP);
+ break;
+ }
+ }
+ }
+
+ int HandleMessage(Message& message) override {
+ switch (message.GetOp()) {
+ case TEST_OP_GET_SERVICE_ID:
+ REPLY_MESSAGE_RETURN(message, service_id_, 0);
+
+ // Set the test channel to the TestChannel for the current channel. Other
+ // messages can use this to perform tests.
+ case TEST_OP_SET_TEST_CHANNEL:
+ test_channel_ = message.GetChannel<TestChannel>();
+ REPLY_MESSAGE_RETURN(message, 0, 0);
+
+ // Return the channel id for the current channel.
+ case TEST_OP_GET_THIS_CHANNEL_ID:
+ REPLY_MESSAGE_RETURN(message, message.GetChannelId(), 0);
+
+ // Return the channel id for the test channel.
+ case TEST_OP_GET_TEST_CHANNEL_ID:
+ if (test_channel_)
+ REPLY_MESSAGE_RETURN(message, test_channel_->channel_id(), 0);
+ else
+ REPLY_ERROR_RETURN(message, ENOENT, 0);
+
+ // Test check channel feature.
+ case TEST_OP_CHECK_CHANNEL_ID: {
+ ChannelReference ref = 0;
+ if (message.Read(&ref, sizeof(ref)) < static_cast<ssize_t>(sizeof(ref)))
+ REPLY_ERROR_RETURN(message, EIO, 0);
+
+ const Status<int> ret = message.CheckChannel<TestChannel>(ref, nullptr);
+ REPLY_MESSAGE_RETURN(message, ret, 0);
+ }
+
+ case TEST_OP_CHECK_CHANNEL_OBJECT: {
+ std::shared_ptr<TestChannel> channel;
+ ChannelReference ref = 0;
+ if (message.Read(&ref, sizeof(ref)) < static_cast<ssize_t>(sizeof(ref)))
+ REPLY_ERROR_RETURN(message, EIO, 0);
+
+ const Status<int> ret =
+ message.CheckChannel<TestChannel>(ref, &channel);
+ if (!ret)
+ REPLY_MESSAGE_RETURN(message, ret, 0);
+
+ if (channel != nullptr)
+ REPLY_MESSAGE_RETURN(message, channel->channel_id(), 0);
+ else
+ REPLY_ERROR_RETURN(message, ENODATA, 0);
+ }
+
+ case TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE: {
+ ChannelReference ref = 0;
+ if (message.Read(&ref, sizeof(ref)) < static_cast<ssize_t>(sizeof(ref)))
+ REPLY_ERROR_RETURN(message, EIO, 0);
+
+ const Status<int> ret = message.CheckChannel<TestChannel>(
+ other_service_.get(), ref, nullptr);
+ REPLY_MESSAGE_RETURN(message, ret, 0);
+ }
+
+ case TEST_OP_GET_NEW_CHANNEL: {
+ auto channel = std::make_shared<TestChannel>(-1);
+ Status<RemoteChannelHandle> channel_handle =
+ message.PushChannel(0, channel, &channel->channel_id_);
+ REPLY_MESSAGE_RETURN(message, channel_handle, 0);
+ }
+
+ case TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE: {
+ if (!other_service_)
+ REPLY_ERROR_RETURN(message, EINVAL, 0);
+
+ auto channel = std::make_shared<TestChannel>(-1);
+ Status<RemoteChannelHandle> channel_handle = message.PushChannel(
+ other_service_.get(), 0, channel, &channel->channel_id_);
+ REPLY_MESSAGE_RETURN(message, channel_handle, 0);
+ }
+
+ case TEST_OP_GET_THIS_PROCESS_ID:
+ REPLY_MESSAGE_RETURN(message, message.GetProcessId(), 0);
+
+ case TEST_OP_GET_THIS_THREAD_ID:
+ REPLY_MESSAGE_RETURN(message, message.GetThreadId(), 0);
+
+ case TEST_OP_GET_THIS_EUID:
+ REPLY_MESSAGE_RETURN(message, message.GetEffectiveUserId(), 0);
+
+ case TEST_OP_GET_THIS_EGID:
+ REPLY_MESSAGE_RETURN(message, message.GetEffectiveGroupId(), 0);
+
+ case TEST_OP_POLLIN_FROM_SERVICE:
+ REPLY_MESSAGE_RETURN(message, message.ModifyChannelEvents(0, EPOLLIN),
+ 0);
+
+ case TEST_OP_SEND_LARGE_DATA_RETURN_SUM: {
+ std::array<int, kLargeDataSize> data_array;
+ ssize_t size_to_read = data_array.size() * sizeof(int);
+ ssize_t read = message.Read(data_array.data(), size_to_read);
+ if (read < size_to_read)
+ REPLY_ERROR_RETURN(message, EIO, 0);
+ int sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+ REPLY_MESSAGE_RETURN(message, sum, 0);
+ }
+
+ default:
+ return Service::DefaultHandleMessage(message);
+ }
+ }
+
+ const ImpulsePayload& GetImpulsePayload() const { return impulse_payload_; }
+
+ private:
+ friend BASE;
+
+ std::shared_ptr<TestChannel> test_channel_;
+ std::shared_ptr<TestService> other_service_;
+ int service_id_;
+ ImpulsePayload impulse_payload_;
+
+ static std::atomic<int> service_counter_;
+
+ TestService(const std::string& name,
+ const std::shared_ptr<TestService>& other_service)
+ : TestService(name, other_service, false) {}
+
+ TestService(const std::string& name,
+ const std::shared_ptr<TestService>& other_service, bool blocking)
+ : BASE(std::string("TestService") + name,
+ Endpoint::Create(kTestServicePath + name, blocking)),
+ other_service_(other_service),
+ service_id_(service_counter_++) {}
+
+ explicit TestService(const std::string& name) : TestService(name, nullptr) {}
+
+ TestService(const TestService&) = delete;
+ void operator=(const TestService&) = delete;
+};
+
+std::atomic<int> TestService::service_counter_;
+
+// Test client to send messages to the test service.
+class TestClient : public ClientBase<TestClient> {
+ public:
+ // Requests the service id of the service this channel is connected to.
+ int GetServiceId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_SERVICE_ID));
+ }
+
+ // Requests the test channel to be set to this client's channel.
+ int SetTestChannel() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_SET_TEST_CHANNEL));
+ }
+
+ // Request the test channel to be set to this client's channel using an async
+ // message.
+ int SetTestChannelAsync() {
+ return ReturnStatusOrError(SendImpulse(TEST_OP_SET_TEST_CHANNEL));
+ }
+
+ // Sends a test async message with payload.
+ int SendAsync(const void* buffer, size_t length) {
+ Transaction trans{*this};
+ return ReturnStatusOrError(SendImpulse(TEST_OP_IMPULSE, buffer, length));
+ }
+
+ // Requests the channel id for this client.
+ int GetThisChannelId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_CHANNEL_ID));
+ }
+
+ // Requests the channel id of the test channel.
+ int GetTestChannelId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_TEST_CHANNEL_ID));
+ }
+
+ // Checks whether the fd |channel_id| is a channel to the test service.
+ // Returns the channel id of the channel.
+ int CheckChannelIdArgument(BorrowedChannelHandle channel) {
+ Transaction trans{*this};
+ ChannelReference ref = trans.PushChannelHandle(channel);
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_ID, &ref,
+ sizeof(ref), nullptr, 0));
+ }
+
+ // Checks whether the fd |channel_id| is a channel to the test service.
+ // Returns the channel id of the channel exercising the context pointer.
+ int CheckChannelObjectArgument(BorrowedChannelHandle channel) {
+ Transaction trans{*this};
+ ChannelReference ref = trans.PushChannelHandle(channel);
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_OBJECT,
+ &ref, sizeof(ref), nullptr, 0));
+ }
+
+ // Checks whether the fd |channel_fd| is a channel to the other test service.
+ // Returns 0 on success.
+ int CheckChannelFromOtherService(BorrowedChannelHandle channel) {
+ Transaction trans{*this};
+ ChannelReference ref = trans.PushChannelHandle(channel);
+ return ReturnStatusOrError(
+ trans.Send<int>(TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE, &ref,
+ sizeof(ref), nullptr, 0));
+ }
+
+ // Requests a new channel to the service.
+ std::unique_ptr<TestClient> GetNewChannel() {
+ Transaction trans{*this};
+ auto status = trans.Send<LocalChannelHandle>(TEST_OP_GET_NEW_CHANNEL);
+ if (status)
+ return TestClient::Create(status.take());
+ else
+ return nullptr;
+ }
+
+ // Requests a new channel to the other service.
+ std::unique_ptr<TestClient> GetNewChannelFromOtherService() {
+ Transaction trans{*this};
+ auto status = trans.Send<LocalChannelHandle>(
+ TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE);
+ if (status)
+ return TestClient::Create(status.take());
+ else
+ return nullptr;
+ }
+
+ // Requests an id from the message description.
+ pid_t GetThisProcessId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_PROCESS_ID));
+ }
+ pid_t GetThisThreadId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_THREAD_ID));
+ }
+ uid_t GetThisEffectiveUserId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EUID));
+ }
+ gid_t GetThisEffectiveGroupId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EGID));
+ }
+
+ int SendPollHupEvent() {
+ return ReturnStatusOrError(SendImpulse(TEST_OP_POLLHUP_FROM_SERVICE));
+ }
+
+ int SendPollInEvent() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_POLLIN_FROM_SERVICE));
+ }
+
+ int SendLargeDataReturnSum(
+ const std::array<int, kLargeDataSize>& data_array) {
+ Transaction trans{*this};
+ return ReturnStatusOrError(
+ trans.Send<int>(TEST_OP_SEND_LARGE_DATA_RETURN_SUM, data_array.data(),
+ data_array.size() * sizeof(int), nullptr, 0));
+ }
+
+ using ClientBase<TestClient>::event_fd;
+
+ enum : size_t { kMaxPayload = MAX_IMPULSE_LENGTH };
+
+ private:
+ friend BASE;
+
+ explicit TestClient(const std::string& name)
+ : BASE{android::pdx::uds::ClientChannelFactory::Create(kTestServicePath +
+ name)} {}
+
+ explicit TestClient(LocalChannelHandle channel)
+ : BASE{android::pdx::uds::ClientChannel::Create(std::move(channel))} {}
+
+ TestClient(const TestClient&) = delete;
+ void operator=(const TestClient&) = delete;
+};
+
+} // anonymous namespace
+
+// Use a test fixture to ensure proper order of cleanup between clients,
+// services, and the dispatcher. These objects are cleaned up in the same
+// thread, order is important; either the service or the client must be
+// destroyed before the dispatcher is stopped. The reason for this is that
+// clients send blocking "close" messages to their respective services on
+// destruction. If this happens after the dispatcher is stopped the client
+// destructor will get blocked waiting for a reply that will never come. In
+// normal use of the service framework this is never an issue because clients
+// and the dispatcher for the same service are never destructed in the same
+// thread (they live in different processes).
+class ServiceFrameworkTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<ServiceDispatcher> dispatcher_;
+ std::thread dispatch_thread_;
+
+ void SetUp() override {
+ // Create a dispatcher to handle messages to services.
+ dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+ ASSERT_NE(nullptr, dispatcher_);
+
+ // Start the message dispatch loop in a separate thread.
+ dispatch_thread_ = std::thread(
+ std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+ }
+
+ void TearDown() override {
+ if (dispatcher_) {
+ // Cancel the dispatcher and wait for the thread to terminate. Explicitly
+ // join the thread so that destruction doesn't deallocate the dispatcher
+ // before the thread finishes.
+ dispatcher_->SetCanceled(true);
+ dispatch_thread_.join();
+ }
+ }
+};
+
+// Test basic operation of TestService/TestClient classes.
+TEST_F(ServiceFrameworkTest, BasicClientService) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ // Get the channel id that will be returned by the next tests.
+ const int channel_id = client->GetThisChannelId();
+ EXPECT_LE(0, channel_id);
+
+ // Check return value before test channel is set.
+ EXPECT_EQ(-ENOENT, client->GetTestChannelId());
+
+ // Set test channel and perform the test again.
+ EXPECT_EQ(0, client->SetTestChannel());
+ EXPECT_EQ(channel_id, client->GetTestChannelId());
+}
+
+// Test impulses.
+TEST_F(ServiceFrameworkTest, Impulse) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ // Get the channel id that will be returned by the next tests.
+ const int channel_id = client->GetThisChannelId();
+ EXPECT_LE(0, channel_id);
+
+ // Check return value before test channel is set.
+ EXPECT_EQ(-ENOENT, client->GetTestChannelId());
+
+ // Set test channel with an impulse and perform the test again.
+ EXPECT_EQ(0, client->SetTestChannelAsync());
+ EXPECT_EQ(channel_id, client->GetTestChannelId());
+
+ ImpulsePayload expected_payload = {{'a', 'b', 'c'}};
+ EXPECT_EQ(0, client->SendAsync(expected_payload.data(), 3));
+ // Send a synchronous message to make sure the async message is handled before
+ // we check the payload.
+ client->GetThisChannelId();
+ EXPECT_EQ(expected_payload, service->GetImpulsePayload());
+
+ // Impulse payloads are limited to 4 machine words.
+ EXPECT_EQ(
+ 0, client->SendAsync(expected_payload.data(), TestClient::kMaxPayload));
+ EXPECT_EQ(-EINVAL, client->SendAsync(expected_payload.data(),
+ TestClient::kMaxPayload + 1));
+
+ // Test invalid pointer.
+ const std::uint8_t* invalid_pointer = nullptr;
+ EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int)));
+}
+
+// Test Message::PushChannel/Service::PushChannel API.
+TEST_F(ServiceFrameworkTest, PushChannel) {
+ // Create a test service and add it to the dispatcher.
+ auto other_service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, other_service);
+ ASSERT_EQ(0, dispatcher_->AddService(other_service));
+
+ // Create a second test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService2, other_service);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to the second test service.
+ auto client1 = TestClient::Create(kTestService2);
+ ASSERT_NE(nullptr, client1);
+
+ // Test the creation of new channels using the push APIs.
+ const int channel_id1 = client1->GetThisChannelId();
+ EXPECT_LE(0, channel_id1);
+
+ auto client2 = client1->GetNewChannel();
+ EXPECT_NE(nullptr, client2);
+ EXPECT_NE(client1->event_fd(), client2->event_fd());
+
+ const int channel_id2 = client2->GetThisChannelId();
+ EXPECT_LE(0, channel_id2);
+ EXPECT_NE(channel_id1, channel_id2);
+
+ auto client3 = client1->GetNewChannelFromOtherService();
+ EXPECT_NE(nullptr, client3);
+ EXPECT_NE(client1->event_fd(), client3->event_fd());
+
+ const int channel_id3 = client3->GetThisChannelId();
+ EXPECT_LE(0, channel_id3);
+
+ // Test which services the channels are connected to.
+ const int service_id1 = client1->GetServiceId();
+ EXPECT_LE(0, service_id1);
+
+ const int service_id2 = client2->GetServiceId();
+ EXPECT_LE(0, service_id2);
+
+ const int service_id3 = client3->GetServiceId();
+ EXPECT_LE(0, service_id3);
+
+ EXPECT_EQ(service_id1, service_id2);
+ EXPECT_NE(service_id1, service_id3);
+}
+
+// Tests process id, thread id, effective user id, and effective group id
+// returned in the message description.
+TEST_F(ServiceFrameworkTest, Ids) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ // Pids 0-2 are defined, no user task should have them.
+
+ const pid_t process_id1 = client->GetThisProcessId();
+ EXPECT_LT(2, process_id1);
+
+ pid_t process_id2;
+
+ std::thread thread([&]() {
+ process_id2 = client->GetThisProcessId();
+ });
+ thread.join();
+
+ EXPECT_LT(2, process_id2);
+ EXPECT_EQ(process_id1, process_id2);
+
+ // This test must run as root for the rest of these tests to work.
+ const int euid1 = client->GetThisEffectiveUserId();
+ ASSERT_EQ(0, euid1);
+
+ const int egid1 = client->GetThisEffectiveGroupId();
+ EXPECT_EQ(0, egid1);
+
+ // Set effective uid/gid to system.
+ ASSERT_EQ(0, setegid(AID_SYSTEM));
+ ASSERT_EQ(0, seteuid(AID_SYSTEM));
+
+ const int euid2 = client->GetThisEffectiveUserId();
+ EXPECT_EQ(AID_SYSTEM, euid2);
+
+ const int egid2 = client->GetThisEffectiveGroupId();
+ EXPECT_EQ(AID_SYSTEM, egid2);
+
+ // Set the euid/egid back to root.
+ ASSERT_EQ(0, setegid(0));
+ ASSERT_EQ(0, seteuid(0));
+}
+
+TEST_F(ServiceFrameworkTest, PollIn) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ epoll_event event;
+ int count = epoll_wait(client->event_fd(), &event, 1, 0);
+ ASSERT_EQ(0, count);
+
+ client->SendPollInEvent();
+
+ count = epoll_wait(client->event_fd(), &event, 1, -1);
+ ASSERT_EQ(1, count);
+ ASSERT_TRUE((EPOLLIN & event.events) != 0);
+}
+
+TEST_F(ServiceFrameworkTest, PollHup) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ epoll_event event;
+ int count = epoll_wait(client->event_fd(), &event, 1, 0);
+ ASSERT_EQ(0, count);
+
+ client->SendPollHupEvent();
+
+ count = epoll_wait(client->event_fd(), &event, 1, -1);
+ ASSERT_EQ(1, count);
+ ASSERT_TRUE((EPOLLHUP & event.events) != 0);
+}
+
+TEST_F(ServiceFrameworkTest, LargeDataSum) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ std::array<int, kLargeDataSize> data_array;
+ std::iota(data_array.begin(), data_array.end(), 0);
+ int expected_sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+ int sum = client->SendLargeDataReturnSum(data_array);
+ ASSERT_EQ(expected_sum, sum);
+}
+
+TEST_F(ServiceFrameworkTest, Cancel) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1, nullptr, true);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ auto previous_time = std::chrono::system_clock::now();
+ dispatcher_->ReceiveAndDispatch(100); // 0.1 seconds should block.
+ auto time = std::chrono::system_clock::now();
+ ASSERT_LE(100, std::chrono::duration_cast<std::chrono::milliseconds>(
+ time - previous_time)
+ .count());
+ service->Cancel();
+ // Non-blocking. Return immediately.
+ dispatcher_->ReceiveAndDispatch(-1);
+ dispatcher_->ReceiveAndDispatch(-1);
+}
diff --git a/libs/vr/libperformance/Android.mk b/libs/vr/libperformance/Android.mk
new file mode 100644
index 0000000..aaacb1a
--- /dev/null
+++ b/libs/vr/libperformance/Android.mk
@@ -0,0 +1,43 @@
+# 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 := \
+ performance_client.cpp \
+ performance_rpc.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ liblog \
+ libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libperformance\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libperformance
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/libs/vr/libperformance/include/CPPLINT.cfg b/libs/vr/libperformance/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libperformance/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libperformance/include/dvr/performance_client_api.h b/libs/vr/libperformance/include/dvr/performance_client_api.h
new file mode 100644
index 0000000..2216e38
--- /dev/null
+++ b/libs/vr/libperformance/include/dvr/performance_client_api.h
@@ -0,0 +1,59 @@
+#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
+#define ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
+
+#include <stddef.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Sets the CPU partition for a task.
+///
+/// Sets the CPU partition for a task to the partition described by a CPU
+/// partition path.
+///
+/// TODO(eieio): Describe supported partitions and rules governing assignment.
+///
+/// @param task_id The task id of task to attach to a partition. When task_id is
+/// 0 the current task id is substituted.
+/// @param partition NULL-terminated ASCII string describing the CPU partition
+/// to attach the task to.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetCpuPartition(pid_t task_id, const char* partition);
+
+/// Sets the scheduler class for a task.
+///
+/// Sets the scheduler class for a task to the class described by a semantic
+/// string.
+///
+/// Supported classes for applications are: audio, graphics, normal, and
+/// background. Additional options following a ':' to be supported in the
+/// future.
+///
+/// @param task_id The task id of task to attach to a partition. When task_id is
+/// 0 the current task id is substituted.
+/// @param scheduler_class NULL-terminated ASCII string containing the desired
+/// scheduler class.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetSchedulerClass(pid_t task_id, const char* scheduler_class);
+
+/// Gets the CPU partition for a task.
+///
+/// Gets the CPU partition path for a task as a NULL-terminated ASCII string. If
+/// the path is too large to fit in the supplied buffer, -ENOBUFS is returned.
+///
+/// @param task_id The task id of the task to retrieve the partition for. When
+/// task_id is 0 the current task id is substituted.
+/// @param partition Pointer to an ASCII string buffer to store the partition
+/// path.
+/// @param size Size of the string buffer in bytes, including space for the NULL
+/// terminator.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
diff --git a/libs/vr/libperformance/include/private/dvr/performance_client.h b/libs/vr/libperformance/include/private/dvr/performance_client.h
new file mode 100644
index 0000000..a61c6b2
--- /dev/null
+++ b/libs/vr/libperformance/include/private/dvr/performance_client.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_H_
+#define ANDROID_DVR_PERFORMANCE_CLIENT_H_
+
+#include <sys/types.h>
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/client.h>
+
+namespace android {
+namespace dvr {
+
+class PerformanceClient : public pdx::ClientBase<PerformanceClient> {
+ public:
+ int SetCpuPartition(pid_t task_id, const std::string& partition);
+ int SetCpuPartition(pid_t task_id, const char* partition);
+ int SetSchedulerClass(pid_t task_id, const std::string& scheduler_class);
+ int SetSchedulerClass(pid_t task_id, const char* scheduler_class);
+ int GetCpuPartition(pid_t task_id, std::string* partition_out);
+ int GetCpuPartition(pid_t task_id, char* partition_out, std::size_t size);
+
+ private:
+ friend BASE;
+
+ explicit PerformanceClient(int* error);
+
+ PerformanceClient(const PerformanceClient&) = delete;
+ void operator=(const PerformanceClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCE_CLIENT_H_
diff --git a/libs/vr/libperformance/include/private/dvr/performance_rpc.h b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
new file mode 100644
index 0000000..73bdaa7
--- /dev/null
+++ b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_PERFORMANCE_RPC_H_
+#define ANDROID_DVR_PERFORMANCE_RPC_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include <pdx/rpc/remote_method_type.h>
+
+namespace android {
+namespace dvr {
+
+// Performance Service RPC interface. Defines the endpoint paths, op codes, and
+// method type signatures supported by performanced.
+struct PerformanceRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/performance/client";
+
+ // Op codes.
+ enum {
+ kOpSetCpuPartition = 0,
+ kOpSetSchedulerClass,
+ kOpGetCpuPartition,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(SetCpuPartition, kOpSetCpuPartition,
+ int(pid_t, const std::string&));
+ PDX_REMOTE_METHOD(SetSchedulerClass, kOpSetSchedulerClass,
+ int(pid_t, const std::string&));
+ PDX_REMOTE_METHOD(GetCpuPartition, kOpGetCpuPartition, std::string(pid_t));
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCE_RPC_H_
diff --git a/libs/vr/libperformance/performance_client.cpp b/libs/vr/libperformance/performance_client.cpp
new file mode 100644
index 0000000..2124162
--- /dev/null
+++ b/libs/vr/libperformance/performance_client.cpp
@@ -0,0 +1,119 @@
+#include "include/private/dvr/performance_client.h"
+
+#include <sys/types.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <private/dvr/performance_rpc.h>
+
+using android::pdx::rpc::WrapString;
+
+namespace android {
+namespace dvr {
+
+PerformanceClient::PerformanceClient(int* error)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ PerformanceRPC::kClientPath)) {
+ if (error)
+ *error = Client::error();
+}
+
+int PerformanceClient::SetCpuPartition(pid_t task_id,
+ const std::string& partition) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(task_id, partition));
+}
+
+int PerformanceClient::SetCpuPartition(pid_t task_id, const char* partition) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(
+ task_id, WrapString(partition)));
+}
+
+int PerformanceClient::SetSchedulerClass(pid_t task_id,
+ const std::string& scheduler_class) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(task_id,
+ scheduler_class));
+}
+
+int PerformanceClient::SetSchedulerClass(pid_t task_id,
+ const char* scheduler_class) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+ task_id, WrapString(scheduler_class)));
+}
+
+int PerformanceClient::GetCpuPartition(pid_t task_id,
+ std::string* partition_out) {
+ if (partition_out == nullptr)
+ return -EINVAL;
+
+ if (task_id == 0)
+ task_id = gettid();
+
+ auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>(
+ partition_out, task_id);
+ return status ? 0 : -status.error();
+}
+
+int PerformanceClient::GetCpuPartition(pid_t task_id, char* partition_out,
+ std::size_t size) {
+ if (partition_out == nullptr)
+ return -EINVAL;
+
+ if (task_id == 0)
+ task_id = gettid();
+
+ auto wrapper = WrapString(partition_out, size);
+ auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>(
+ &wrapper, task_id);
+ if (!status)
+ return -status.error();
+
+ if (wrapper.size() < size)
+ partition_out[wrapper.size()] = '\0';
+
+ return 0;
+}
+
+} // namespace dvr
+} // namespace android
+
+extern "C" int dvrSetCpuPartition(pid_t task_id, const char* partition) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->SetCpuPartition(task_id, partition);
+ else
+ return error;
+}
+
+extern "C" int dvrSetSchedulerClass(pid_t task_id,
+ const char* scheduler_class) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->SetSchedulerClass(task_id, scheduler_class);
+ else
+ return error;
+}
+
+extern "C" int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->GetCpuPartition(task_id, partition, size);
+ else
+ return error;
+}
diff --git a/libs/vr/libperformance/performance_rpc.cpp b/libs/vr/libperformance/performance_rpc.cpp
new file mode 100644
index 0000000..9135349
--- /dev/null
+++ b/libs/vr/libperformance/performance_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/performance_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char PerformanceRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libposepredictor/Android.mk b/libs/vr/libposepredictor/Android.mk
new file mode 100644
index 0000000..030f79c
--- /dev/null
+++ b/libs/vr/libposepredictor/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2008 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 := \
+ linear_pose_predictor.cpp \
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libdvrcommon \
+ libsensor \
+ libpdx_default_transport \
+
+sharedLibraries := \
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libposepredictor\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libposepredictor
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+ linear_pose_predictor_tests.cpp \
+
+LOCAL_STATIC_LIBRARIES := libposepredictor $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := pose_predictor_tests
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libposepredictor/include/private/dvr/linear_pose_predictor.h b/libs/vr/libposepredictor/include/private/dvr/linear_pose_predictor.h
new file mode 100644
index 0000000..1efe938
--- /dev/null
+++ b/libs/vr/libposepredictor/include/private/dvr/linear_pose_predictor.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_POSE_PREDICTOR_H_
+#define ANDROID_DVR_POSE_PREDICTOR_H_
+
+#include <private/dvr/pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+// This class makes a linear prediction using the last two samples we received.
+class LinearPosePredictor : public PosePredictor {
+ public:
+ LinearPosePredictor() = default;
+
+ // Add a new sample.
+ void Add(const Sample& sample, DvrPoseAsync* out_pose) override;
+
+ // Predict using the last two samples.
+ void Predict(int64_t left_time_ns, int64_t right_time_ns,
+ DvrPoseAsync* out_pose) const override;
+
+ private:
+ // The index of the last sample we received.
+ size_t current_index_ = 0;
+
+ // The previous two samples.
+ Sample samples_[2];
+
+ // Experimental
+ bool forward_predict_angular_speed_ = false;
+
+ // Transient variables updated when a sample is added.
+ vec3d velocity_ = vec3d::Zero();
+ vec3d rotational_velocity_ = vec3d::Zero();
+ vec3d rotational_axis_ = vec3d::Zero();
+ double last_angular_speed_ = 0;
+ double angular_speed_ = 0;
+ double angular_accel_ = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/private/dvr/pose_predictor.h b/libs/vr/libposepredictor/include/private/dvr/pose_predictor.h
new file mode 100644
index 0000000..719edbe
--- /dev/null
+++ b/libs/vr/libposepredictor/include/private/dvr/pose_predictor.h
@@ -0,0 +1,47 @@
+#ifndef ANDROID_DVR_LINEAR_POSE_PREDICTOR_H_
+#define ANDROID_DVR_LINEAR_POSE_PREDICTOR_H_
+
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// This is an abstract base class for prediction 6dof pose given
+// a set of samples.
+//
+// TODO(okana): Create a framework for testing different subclasses for
+// performance and accuracy.
+class PosePredictor {
+ public:
+ PosePredictor() = default;
+ virtual ~PosePredictor() = default;
+
+ // Encapsulates a pose sample.
+ struct Sample {
+ vec3d position = vec3d::Zero();
+ quatd orientation = quatd::Identity();
+ int64_t time_ns = 0;
+ };
+
+ // Add a pose sample coming from the sensors.
+ // Returns this sample as a dvr pose.
+ //
+ // We will use the returned pose if prediction is not enabled.
+ virtual void Add(const Sample& sample, DvrPoseAsync* out_pose) = 0;
+
+ // Make a pose prediction for the left and right eyes at specific times.
+ virtual void Predict(int64_t left_time_ns, int64_t right_time_ns,
+ DvrPoseAsync* out_pose) const = 0;
+
+ // Helpers
+ static double NsToSeconds(int64_t time_ns) { return time_ns / 1e9; }
+ static int64_t SecondsToNs(double seconds) {
+ return static_cast<int64_t>(seconds * 1e9);
+ }
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LINEAR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/linear_pose_predictor.cpp b/libs/vr/libposepredictor/linear_pose_predictor.cpp
new file mode 100644
index 0000000..a2ce2ca
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_pose_predictor.cpp
@@ -0,0 +1,147 @@
+#include <cutils/log.h>
+
+#include <private/dvr/linear_pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+using AngleAxisd = Eigen::AngleAxis<double>;
+
+void LinearPosePredictor::Add(const Sample& sample, DvrPoseAsync* out_pose) {
+ // If we are receiving a new sample, move the index to the next item.
+ // If the time stamp is the same as the last frame, we will just overwrite
+ // it with the new data.
+ if (sample.time_ns != samples_[current_index_].time_ns) {
+ current_index_ ^= 1;
+ }
+
+ // Save the sample.
+ samples_[current_index_] = sample;
+
+ // The previous sample we received.
+ const auto& previous_sample = samples_[current_index_ ^ 1];
+
+ // Ready to compute velocities.
+ const auto pose_delta_time =
+ NsToSeconds(sample.time_ns - previous_sample.time_ns);
+
+ const double inverse_dt = 1. / pose_delta_time;
+ if (pose_delta_time > 0.0) {
+ velocity_ = (sample.position - previous_sample.position) * inverse_dt;
+ } else {
+ velocity_ = vec3d::Zero();
+ }
+
+ quatd delta_q = sample.orientation.inverse() * previous_sample.orientation;
+ // Check that delta_q.w() == 1, Eigen doesn't respect this convention. If
+ // delta_q.w() == -1, we'll get the opposite velocity.
+ if (delta_q.w() < 0) {
+ delta_q.w() = -delta_q.w();
+ delta_q.vec() = -delta_q.vec();
+ }
+ rotational_velocity_ = -2.0 * delta_q.vec() * inverse_dt;
+
+ // Temporary experiment with acceleration estimate.
+ angular_speed_ = rotational_velocity_.norm();
+ angular_accel_ = 0.0;
+ if (forward_predict_angular_speed_) {
+ angular_accel_ =
+ pose_delta_time > 0.0
+ ? (angular_speed_ - last_angular_speed_) / pose_delta_time
+ : 0.0;
+ }
+ last_angular_speed_ = angular_speed_;
+
+ rotational_axis_ = vec3d(0.0, 1.0, 0.0);
+ if (angular_speed_ > 0.0) {
+ rotational_axis_ = rotational_velocity_ / angular_speed_;
+ }
+
+ out_pose->orientation = {static_cast<float>(sample.orientation.vec().x()),
+ static_cast<float>(sample.orientation.vec().y()),
+ static_cast<float>(sample.orientation.vec().z()),
+ static_cast<float>(sample.orientation.w())};
+
+ out_pose->translation = {static_cast<float>(sample.position.x()),
+ static_cast<float>(sample.position.y()),
+ static_cast<float>(sample.position.z()), 0.0f};
+
+ out_pose->right_orientation = {
+ static_cast<float>(sample.orientation.vec().x()),
+ static_cast<float>(sample.orientation.vec().y()),
+ static_cast<float>(sample.orientation.vec().z()),
+ static_cast<float>(sample.orientation.w())};
+
+ out_pose->right_translation = {static_cast<float>(sample.position.x()),
+ static_cast<float>(sample.position.y()),
+ static_cast<float>(sample.position.z()), 0.0f};
+
+ out_pose->angular_velocity = {static_cast<float>(rotational_velocity_.x()),
+ static_cast<float>(rotational_velocity_.y()),
+ static_cast<float>(rotational_velocity_.z()),
+ 0.0f};
+
+ out_pose->velocity = {static_cast<float>(velocity_.x()),
+ static_cast<float>(velocity_.y()),
+ static_cast<float>(velocity_.z()), 0.0f};
+ out_pose->timestamp_ns = sample.time_ns;
+ out_pose->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+ memset(out_pose->pad, 0, sizeof(out_pose->pad));
+}
+
+void LinearPosePredictor::Predict(int64_t left_time_ns, int64_t right_time_ns,
+ DvrPoseAsync* out_pose) const {
+ const auto& sample = samples_[current_index_];
+
+ double dt = NsToSeconds(left_time_ns - sample.time_ns);
+ double r_dt = NsToSeconds(right_time_ns - sample.time_ns);
+
+ // Temporary forward prediction code.
+ auto start_t_head_future = sample.position + velocity_ * dt;
+ auto r_start_t_head_future = sample.position + velocity_ * r_dt;
+ double angle = angular_speed_ * dt;
+ double r_angle = angular_speed_ * r_dt;
+ if (__builtin_expect(forward_predict_angular_speed_, 0)) {
+ angle += 0.5 * angular_accel_ * dt * dt;
+ r_angle += 0.5 * angular_accel_ * r_dt * r_dt;
+ }
+ auto start_q_head_future =
+ sample.orientation * quatd(AngleAxisd(angle, rotational_axis_));
+ auto r_start_q_head_future =
+ sample.orientation * quatd(AngleAxisd(r_angle, rotational_axis_));
+
+ out_pose->orientation = {static_cast<float>(start_q_head_future.x()),
+ static_cast<float>(start_q_head_future.y()),
+ static_cast<float>(start_q_head_future.z()),
+ static_cast<float>(start_q_head_future.w())};
+
+ out_pose->translation = {static_cast<float>(start_t_head_future.x()),
+ static_cast<float>(start_t_head_future.y()),
+ static_cast<float>(start_t_head_future.z()), 0.0f};
+
+ out_pose->right_orientation = {static_cast<float>(r_start_q_head_future.x()),
+ static_cast<float>(r_start_q_head_future.y()),
+ static_cast<float>(r_start_q_head_future.z()),
+ static_cast<float>(r_start_q_head_future.w())};
+
+ out_pose->right_translation = {static_cast<float>(r_start_t_head_future.x()),
+ static_cast<float>(r_start_t_head_future.y()),
+ static_cast<float>(r_start_t_head_future.z()),
+ 0.0f};
+
+ out_pose->angular_velocity = {static_cast<float>(rotational_velocity_.x()),
+ static_cast<float>(rotational_velocity_.y()),
+ static_cast<float>(rotational_velocity_.z()),
+ 0.0f};
+
+ out_pose->velocity = {static_cast<float>(velocity_.x()),
+ static_cast<float>(velocity_.y()),
+ static_cast<float>(velocity_.z()), 0.0f};
+
+ out_pose->timestamp_ns = left_time_ns;
+ out_pose->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+ memset(out_pose->pad, 0, sizeof(out_pose->pad));
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libposepredictor/linear_pose_predictor_tests.cpp b/libs/vr/libposepredictor/linear_pose_predictor_tests.cpp
new file mode 100644
index 0000000..6c4f58a
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_pose_predictor_tests.cpp
@@ -0,0 +1,185 @@
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include <private/dvr/linear_pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr double kAbsErrorTolerance = 1e-5;
+
+// The default rotation axis we will be using.
+const vec3d kRotationAxis = vec3d(1, 4, 3).normalized();
+
+// Linearly interpolate between a and b.
+vec3d lerp(const vec3d& a, const vec3d& b, double t) { return (b - a) * t + a; }
+
+// Linearly interpolate between two angles and return the resulting rotation as
+// a quaternion (around the kRotationAxis).
+quatd qlerp(double angle1, double angle2, double t) {
+ return quatd(
+ Eigen::AngleAxis<double>((angle2 - angle1) * t + angle1, kRotationAxis));
+}
+
+// Compare two positions.
+void TestPosition(const vec3d& expected, const float32x4_t& actual) {
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_NEAR(expected[i], static_cast<double>(actual[i]),
+ kAbsErrorTolerance);
+ }
+}
+
+// Compare two orientations.
+void TestOrientation(const quatd& expected, const float32x4_t& actual) {
+ // abs(expected.dot(actual)) > 1-eps
+ EXPECT_GE(std::abs(vec4d(actual[0], actual[1], actual[2], actual[3])
+ .dot(expected.coeffs())),
+ 0.99);
+}
+}
+
+// Test the extrapolation from two samples.
+TEST(LinearPosePredictorTest, Extrapolation) {
+ LinearPosePredictor predictor;
+
+ // We wil extrapolate linearly from [position|orientation] 1 -> 2.
+ const vec3d position1(0, 0, 0);
+ const vec3d position2(1, 2, 3);
+ const double angle1 = M_PI * 0.3;
+ const double angle2 = M_PI * 0.5;
+ const quatd orientation1(Eigen::AngleAxis<double>(angle1, kRotationAxis));
+ const quatd orientation2(Eigen::AngleAxis<double>(angle2, kRotationAxis));
+ const int64_t t1_ns = 0; //< First sample time stamp
+ const int64_t t2_ns = 10; //< The second sample time stamp
+ const int64_t eval_left_ns = 23; //< The eval time for left
+ const int64_t eval_right_ns = 31; //< The eval time for right
+ DvrPoseAsync start_pose, end_pose, extrapolated_pose;
+
+ predictor.Add(
+ PosePredictor::Sample{
+ .position = position1, .orientation = orientation1, .time_ns = t1_ns},
+ &start_pose);
+
+ // The start pose is passthough.
+ TestPosition(position1, start_pose.translation);
+ TestPosition(position1, start_pose.right_translation);
+ TestOrientation(orientation1, start_pose.orientation);
+ TestOrientation(orientation1, start_pose.right_orientation);
+ EXPECT_EQ(t1_ns, start_pose.timestamp_ns);
+
+ predictor.Add(
+ PosePredictor::Sample{
+ .position = position2, .orientation = orientation2, .time_ns = t2_ns},
+ &end_pose);
+
+ TestPosition(position2, end_pose.translation);
+ TestPosition(position2, end_pose.right_translation);
+ TestOrientation(orientation2, end_pose.orientation);
+ TestOrientation(orientation2, end_pose.right_orientation);
+ EXPECT_EQ(t2_ns, end_pose.timestamp_ns);
+
+ // Extrapolate from t1 - t2 to eval_[left/right].
+ predictor.Predict(eval_left_ns, eval_right_ns, &extrapolated_pose);
+
+ // The interpolation factors for left and right.
+ const auto left_t =
+ (eval_left_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+ EXPECT_EQ(2.3, left_t);
+
+ const auto right_t =
+ (eval_right_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+ EXPECT_EQ(3.1, right_t);
+
+ TestPosition(lerp(position1, position2, left_t),
+ extrapolated_pose.translation);
+ TestPosition(lerp(position1, position2, right_t),
+ extrapolated_pose.right_translation);
+ TestOrientation(qlerp(angle1, angle2, left_t), extrapolated_pose.orientation);
+ TestOrientation(qlerp(angle1, angle2, right_t),
+ extrapolated_pose.right_orientation);
+}
+
+// Test three samples, where the last two samples have the same timestamp.
+TEST(LinearPosePredictorTest, DuplicateSamples) {
+ LinearPosePredictor predictor;
+
+ const vec3d position1(0, 0, 0);
+ const vec3d position2(1, 2, 3);
+ const vec3d position3(2, 2, 3);
+ const double angle1 = M_PI * 0.3;
+ const double angle2 = M_PI * 0.5;
+ const double angle3 = M_PI * 0.65;
+ const quatd orientation1(Eigen::AngleAxis<double>(angle1, kRotationAxis));
+ const quatd orientation2(Eigen::AngleAxis<double>(angle2, kRotationAxis));
+ const quatd orientation3(Eigen::AngleAxis<double>(angle3, kRotationAxis));
+ const int64_t t1_ns = 0;
+ const int64_t t2_ns = 10;
+ const int64_t eval_left_ns = 27;
+ const int64_t eval_right_ns = 31;
+ DvrPoseAsync start_pose, end_pose, extrapolated_pose;
+
+ predictor.Add(
+ PosePredictor::Sample{
+ .position = position1, .orientation = orientation1, .time_ns = t1_ns},
+ &start_pose);
+
+ predictor.Add(
+ PosePredictor::Sample{
+ .position = position2, .orientation = orientation2, .time_ns = t2_ns},
+ &end_pose);
+
+ {
+ // Extrapolate from t1 - t2 to eval_[left/right].
+ predictor.Predict(eval_left_ns, eval_right_ns, &extrapolated_pose);
+
+ // The interpolation factors for left and right.
+ const auto left_t =
+ (eval_left_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+ const auto right_t =
+ (eval_right_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+
+ // Test the result.
+ TestPosition(lerp(position1, position2, left_t),
+ extrapolated_pose.translation);
+ TestPosition(lerp(position1, position2, right_t),
+ extrapolated_pose.right_translation);
+ TestOrientation(qlerp(angle1, angle2, left_t),
+ extrapolated_pose.orientation);
+ TestOrientation(qlerp(angle1, angle2, right_t),
+ extrapolated_pose.right_orientation);
+ }
+
+ // Sending a duplicate sample here.
+ predictor.Add(
+ PosePredictor::Sample{
+ .position = position3, .orientation = orientation3, .time_ns = t2_ns},
+ &end_pose);
+
+ {
+ // Extrapolate from t1 - t2 to eval_[left/right].
+ predictor.Predict(eval_left_ns, eval_right_ns, &extrapolated_pose);
+
+ // The interpolation factors for left and right.
+ const auto left_t =
+ (eval_left_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+ const auto right_t =
+ (eval_right_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+
+ // Test the result.
+ TestPosition(lerp(position1, position3, left_t),
+ extrapolated_pose.translation);
+ TestPosition(lerp(position1, position3, right_t),
+ extrapolated_pose.right_translation);
+ TestOrientation(qlerp(angle1, angle3, left_t),
+ extrapolated_pose.orientation);
+ TestOrientation(qlerp(angle1, angle3, right_t),
+ extrapolated_pose.right_orientation);
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libsensor/Android.mk b/libs/vr/libsensor/Android.mk
new file mode 100644
index 0000000..db1514d
--- /dev/null
+++ b/libs/vr/libsensor/Android.mk
@@ -0,0 +1,78 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ pose_client.cpp \
+ sensor_client.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libbufferhub \
+ libchrome \
+ libdvrcommon \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ libhardware \
+ liblog \
+ libutils \
+ libevent
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libsensor
+include $(BUILD_STATIC_LIBRARY)
+
+
+testFiles := \
+ tests/sensor_app_tests.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := sensor_app_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+ $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv1_CM \
+ libGLESv2 \
+ libvulkan \
+ libsync \
+ $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgmock_main \
+ libgmock \
+ libdisplay \
+ libeds \
+ libsensor \
+ libdvrgraphics \
+ $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libsensor/include/CPPLINT.cfg b/libs/vr/libsensor/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libsensor/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libsensor/include/dvr/pose_client.h b/libs/vr/libsensor/include/dvr/pose_client.h
new file mode 100644
index 0000000..ed75f84
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/include/private/dvr/pose-ipc.h b/libs/vr/libsensor/include/private/dvr/pose-ipc.h
new file mode 100644
index 0000000..908030e
--- /dev/null
+++ b/libs/vr/libsensor/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/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/libsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000..66c4c7c
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libsensor/include/private/dvr/sensor-ipc.h
new file mode 100644
index 0000000..7e8b9e5
--- /dev/null
+++ b/libs/vr/libsensor/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/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/libsensor/include/private/dvr/sensor_client.h b/libs/vr/libsensor/include/private/dvr/sensor_client.h
new file mode 100644
index 0000000..15a9b8f
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/include/private/dvr/sensor_constants.h b/libs/vr/libsensor/include/private/dvr/sensor_constants.h
new file mode 100644
index 0000000..8fa87b3
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/pose_client.cpp b/libs/vr/libsensor/pose_client.cpp
new file mode 100644
index 0000000..13e127d
--- /dev/null
+++ b/libs/vr/libsensor/pose_client.cpp
@@ -0,0 +1,341 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <base/logging.h>
+#include <cutils/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;
+
+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 >= static_cast<int32_t>(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);
+ LOG(INFO) << "Mapped pose data translation "
+ << mapped_pose_buffer_->ring[0].translation[0] << ','
+ << mapped_pose_buffer_->ring[0].translation[1] << ','
+ << mapped_pose_buffer_->ring[0].translation[2] << ", quat "
+ << 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 >= static_cast<int32_t>(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) {
+ ALOGE("Pose GetControllerRingBuffer() 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;
+ }
+ 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);
+ LOG(INFO) << "Mapped controller " << controller_id
+ << " pose data translation "
+ << client_state.mapped_pose_buffer[0].translation[0] << ','
+ << client_state.mapped_pose_buffer[0].translation[1] << ','
+ << client_state.mapped_pose_buffer[0].translation[2] << ", quat "
+ << 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/libsensor/sensor_client.cpp b/libs/vr/libsensor/sensor_client.cpp
new file mode 100644
index 0000000..1c240f5
--- /dev/null
+++ b/libs/vr/libsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <cutils/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/libsensor/tests/sensor_app_tests.cpp b/libs/vr/libsensor/tests/sensor_app_tests.cpp
new file mode 100644
index 0000000..8cd6f79
--- /dev/null
+++ b/libs/vr/libsensor/tests/sensor_app_tests.cpp
@@ -0,0 +1,131 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <math.h>
+
+#include <base/logging.h>
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.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)
+ LOG(ERROR) << "vsync did not increment: " << schedule.vsync_count;
+ if (pose.timestamp_ns == last_pose.timestamp_ns)
+ LOG(ERROR) << "timestamp did not change: " << 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)
+ LOG(ERROR) << "vsync did not increment: " << schedule.vsync_count;
+ if (pose.timestamp_ns == last_pose.timestamp_ns)
+ LOG(ERROR) << "timestamp did not change: " << 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);
+}
diff --git a/services/vr/.clang-format b/services/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/services/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/services/vr/Android.bp b/services/vr/Android.bp
index 80df479..af8212a 100644
--- a/services/vr/Android.bp
+++ b/services/vr/Android.bp
@@ -1,3 +1,3 @@
subdirs = [
- "*",
+ "*/*",
]
diff --git a/services/vr/bufferhubd/Android.mk b/services/vr/bufferhubd/Android.mk
new file mode 100644
index 0000000..492acb2
--- /dev/null
+++ b/services/vr/bufferhubd/Android.mk
@@ -0,0 +1,51 @@
+# 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 := \
+ buffer_hub.cpp \
+ bufferhubd.cpp \
+ consumer_channel.cpp \
+ producer_channel.cpp \
+ consumer_queue_channel.cpp \
+ producer_queue_channel.cpp \
+
+staticLibraries := \
+ libchrome \
+ libperformance \
+ libpdx_default_transport \
+ libbufferhub
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ libhardware \
+ liblog \
+ libsync \
+ libutils
+
+include $(CLEAR_VARS)
+# Don't strip symbols so we see stack traces in logcat.
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"bufferhubd\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhubd
+LOCAL_INIT_RC := bufferhubd.rc
+include $(BUILD_EXECUTABLE)
+
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
new file mode 100644
index 0000000..a0c7439
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -0,0 +1,474 @@
+#include "buffer_hub.h"
+
+#include <cutils/log.h>
+#include <poll.h>
+#include <utils/Trace.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+#include "producer_channel.h"
+#include "producer_queue_channel.h"
+
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::default_transport::Endpoint;
+
+namespace android {
+namespace dvr {
+
+BufferHubService::BufferHubService()
+ : BASE("BufferHub", Endpoint::Create(BufferHubRPC::kClientPath)) {}
+
+BufferHubService::~BufferHubService() {}
+
+bool BufferHubService::IsInitialized() const {
+ return BASE::IsInitialized() && IonBuffer::GetGrallocModule();
+}
+
+std::string BufferHubService::DumpState(size_t /*max_length*/) {
+ std::ostringstream stream;
+ auto channels = GetChannels<BufferHubChannel>();
+
+ std::sort(channels.begin(), channels.end(),
+ [](const std::shared_ptr<BufferHubChannel>& a,
+ const std::shared_ptr<BufferHubChannel>& b) {
+ return a->buffer_id() < b->buffer_id();
+ });
+
+ stream << "Active Producer Buffers:\n";
+ stream << std::right;
+ stream << std::setw(6) << "Id";
+ stream << " ";
+ stream << std::setw(9) << "Consumers";
+ stream << " ";
+ stream << std::setw(14) << "Geometry";
+ stream << " ";
+ stream << std::setw(6) << "Format";
+ stream << " ";
+ stream << std::setw(10) << "Usage";
+ stream << " ";
+ stream << "Name";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kProducerType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right;
+ stream << std::setw(6) << info.id;
+ stream << " ";
+ stream << std::setw(9) << info.consumer_count;
+ stream << " ";
+ if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+ std::string size = std::to_string(info.width) + " B";
+ stream << std::setw(14) << size;
+ } else {
+ std::string dimensions = std::to_string(info.width) + "x" +
+ std::to_string(info.height) + "x" +
+ std::to_string(info.slice_count);
+ stream << std::setw(14) << dimensions;
+ }
+ stream << " ";
+ stream << std::setw(6) << info.format;
+ stream << " ";
+ stream << "0x" << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage;
+ stream << std::dec << std::setfill(' ');
+ stream << " ";
+ stream << info.name;
+ stream << std::endl;
+ }
+ }
+
+ stream << "Active Consumer Buffers:\n";
+ stream << std::right;
+ stream << std::setw(6) << "Id";
+ stream << " ";
+ stream << std::setw(14) << "Geometry";
+ stream << " ";
+ stream << "Name";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kConsumerType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right;
+ stream << std::setw(6) << info.id;
+ stream << " ";
+
+ if (info.consumer_count == 0) {
+ // consumer_count is tracked by producer. When it's zero, producer must
+ // have already hung up and the consumer is orphaned.
+ stream << std::setw(14) << "Orphaned.";
+ stream << std::endl;
+ continue;
+ }
+
+ if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+ std::string size = std::to_string(info.width) + " B";
+ stream << std::setw(14) << size;
+ } else {
+ std::string dimensions = std::to_string(info.width) + "x" +
+ std::to_string(info.height) + "x" +
+ std::to_string(info.slice_count);
+ stream << std::setw(14) << dimensions;
+ }
+ stream << " ";
+ stream << info.name;
+ stream << std::endl;
+ }
+ }
+
+ stream << std::endl;
+ stream << "Active Producer Queues:\n";
+ stream << std::right << std::setw(6) << "Id";
+ stream << std::right << std::setw(12) << " Allocated";
+ stream << std::right << std::setw(12) << " Consumers";
+ stream << " UsageSetMask";
+ stream << " UsageClearMask";
+ stream << " UsageDenySetMask";
+ stream << " UsageDenyClearMask";
+ stream << " ";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kProducerQueueType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::dec << std::setfill(' ');
+ stream << std::right << std::setw(6) << info.id;
+ stream << std::right << std::setw(12) << info.capacity;
+ stream << std::right << std::setw(12) << info.consumer_count;
+ stream << std::setw(5) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_set_mask;
+ stream << std::setw(7) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_clear_mask;
+ stream << std::setw(9) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_deny_set_mask;
+ stream << std::setw(11) << std::setfill(' ') << "0x";
+ stream << std::hex << std::setfill('0');
+ stream << std::setw(8) << info.usage_deny_clear_mask;
+ }
+ }
+
+ stream << std::endl;
+ stream << "Active Consumer Queues:\n";
+ stream << std::dec << std::setfill(' ');
+ stream << std::right << std::setw(6) << "Id";
+ stream << std::right << std::setw(12) << " Imported";
+ stream << " ";
+ stream << std::endl;
+
+ for (const auto& channel : channels) {
+ if (channel->channel_type() == BufferHubChannel::kConsumerQueueType) {
+ BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+ stream << std::right << std::setw(6) << info.id;
+ stream << std::right << std::setw(12) << info.capacity;
+ }
+ }
+
+ return stream.str();
+}
+
+void BufferHubService::HandleImpulse(Message& message) {
+ ATRACE_NAME("BufferHubService::HandleImpulse");
+ if (auto channel = message.GetChannel<BufferHubChannel>())
+ channel->HandleImpulse(message);
+}
+
+int BufferHubService::HandleMessage(Message& message) {
+ ATRACE_NAME("BufferHubService::HandleMessage");
+ auto channel = message.GetChannel<BufferHubChannel>();
+
+ ALOGD_IF(
+ TRACE,
+ "BufferHubService::HandleMessage: channel=%p channel_id=%d opcode=%d",
+ channel.get(), message.GetChannelId(), message.GetOp());
+
+ // If the channel is already set up, let it handle the message.
+ if (channel && !channel->HandleMessage(message))
+ return DefaultHandleMessage(message);
+
+ // This channel has not been set up yet, the following are valid operations.
+ switch (message.GetOp()) {
+ case BufferHubRPC::CreateBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateBuffer>(
+ *this, &BufferHubService::OnCreateBuffer, message);
+ return 0;
+
+ case BufferHubRPC::CreatePersistentBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ *this, &BufferHubService::OnCreatePersistentBuffer, message);
+ return 0;
+
+ case BufferHubRPC::GetPersistentBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetPersistentBuffer>(
+ *this, &BufferHubService::OnGetPersistentBuffer, message);
+ return 0;
+
+ case BufferHubRPC::CreateProducerQueue::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+ *this, &BufferHubService::OnCreateProducerQueue, message);
+ return 0;
+
+ default:
+ return DefaultHandleMessage(message);
+ }
+}
+
+void BufferHubService::OnChannelClose(Message&,
+ const std::shared_ptr<Channel>& channel) {
+ if (auto buffer = std::static_pointer_cast<BufferHubChannel>(channel))
+ buffer->Detach();
+}
+
+int BufferHubService::OnCreateBuffer(Message& message, int width, int height,
+ int format, int usage,
+ size_t meta_size_bytes,
+ size_t slice_count) {
+ // Use the producer channel id as the global buffer id.
+ const int buffer_id = message.GetChannelId();
+ ALOGD_IF(TRACE,
+ "BufferHubService::OnCreateBuffer: buffer_id=%d width=%d height=%d "
+ "format=%d usage=%d meta_size_bytes=%zu slice_count=%zu",
+ buffer_id, width, height, format, usage, meta_size_bytes,
+ slice_count);
+
+ // See if this channel is already attached to a buffer.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE("BufferHubService::OnCreateBuffer: Buffer already created: buffer=%d",
+ buffer_id);
+ return -EALREADY;
+ }
+
+ int error;
+ if (const auto producer_channel =
+ ProducerChannel::Create(this, buffer_id, width, height, format, usage,
+ meta_size_bytes, slice_count, &error)) {
+ message.SetChannel(producer_channel);
+ return 0;
+ } else {
+ ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+ return error;
+ }
+}
+
+int BufferHubService::OnCreatePersistentBuffer(
+ Message& message, const std::string& name, int user_id, int group_id,
+ int width, int height, int format, int usage, size_t meta_size_bytes,
+ size_t slice_count) {
+ const int channel_id = message.GetChannelId();
+ ALOGD_IF(TRACE,
+ "BufferHubService::OnCreatePersistentBuffer: channel_id=%d name=%s "
+ "user_id=%d group_id=%d width=%d height=%d format=%d usage=%d "
+ "meta_size_bytes=%zu slice_count=%zu",
+ channel_id, name.c_str(), user_id, group_id, width, height, format,
+ usage, meta_size_bytes, slice_count);
+
+ // See if this channel is already attached to a buffer.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Channel already attached "
+ "to buffer: channel_id=%d buffer_id=%d",
+ channel_id, channel->buffer_id());
+ return -EALREADY;
+ }
+
+ const int euid = message.GetEffectiveUserId();
+ const int egid = message.GetEffectiveGroupId();
+ int error;
+
+ if (auto buffer = GetNamedBuffer(name)) {
+ if (!buffer->CheckAccess(euid, egid)) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Requesting process does "
+ "not have permission to access named buffer: name=%s euid=%d egid=%d",
+ name.c_str(), euid, euid);
+ return -EPERM;
+ } else if (!buffer->CheckParameters(width, height, format, usage,
+ meta_size_bytes, slice_count)) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Requested an existing "
+ "buffer with different parameters: name=%s",
+ name.c_str());
+ return -EINVAL;
+ } else if (!buffer->IsDetached()) {
+ ALOGE(
+ "BufferHubService::OnCreatePersistentBuffer: Requesting a persistent "
+ "buffer that is already attached to a channel: name=%s",
+ name.c_str());
+ return -EINVAL;
+ } else {
+ buffer->Attach(channel_id);
+ message.SetChannel(buffer);
+ return 0;
+ }
+ } else if (auto buffer = ProducerChannel::Create(
+ this, channel_id, width, height, format, usage,
+ meta_size_bytes, slice_count, &error)) {
+ const int ret =
+ buffer->OnProducerMakePersistent(message, name, user_id, group_id);
+ if (!ret)
+ message.SetChannel(buffer);
+ return ret;
+ } else {
+ ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+ return error;
+ }
+}
+
+int BufferHubService::OnGetPersistentBuffer(Message& message,
+ const std::string& name) {
+ const int channel_id = message.GetChannelId();
+ ALOGD_IF(TRACE,
+ "BufferHubService::OnGetPersistentBuffer: channel_id=%d name=%s",
+ channel_id, name.c_str());
+
+ // See if this channel is already attached to a buffer.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE(
+ "BufferHubService::OnGetPersistentBuffer: Channel already attached to "
+ "buffer: channel_id=%d buffer_id=%d",
+ channel_id, channel->buffer_id());
+ return -EALREADY;
+ }
+
+ const int euid = message.GetEffectiveUserId();
+ const int egid = message.GetEffectiveGroupId();
+
+ if (auto buffer = GetNamedBuffer(name)) {
+ if (!buffer->CheckAccess(euid, egid)) {
+ ALOGE(
+ "BufferHubService::OnGetPersistentBuffer: Requesting process does "
+ "not have permission to access named buffer: name=%s euid=%d egid=%d",
+ name.c_str(), euid, egid);
+ return -EPERM;
+ } else if (!buffer->IsDetached()) {
+ ALOGE(
+ "BufferHubService::OnGetPersistentBuffer: Requesting a persistent "
+ "buffer that is already attached to a channel: name=%s",
+ name.c_str());
+ return -EINVAL;
+ } else {
+ buffer->Attach(channel_id);
+ message.SetChannel(buffer);
+ return 0;
+ }
+ } else {
+ ALOGE("BufferHubService::OnGetPersistentBuffer: Buffer \"%s\" not found!",
+ name.c_str());
+ return -ENOENT;
+ }
+}
+
+int BufferHubService::OnCreateProducerQueue(
+ pdx::Message& message, size_t meta_size_bytes, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask, int usage_deny_clear_mask) {
+ // Use the producer channel id as the global queue id.
+ const int queue_id = message.GetChannelId();
+ ALOGD_IF(TRACE, "BufferHubService::OnCreateProducerQueue: queue_id=%d",
+ queue_id);
+
+ // See if this channel is already attached to another object.
+ if (const auto channel = message.GetChannel<BufferHubChannel>()) {
+ ALOGE("BufferHubService::OnCreateProducerQueue: already created: queue=%d",
+ queue_id);
+ return -EALREADY;
+ }
+
+ int error;
+ if (const auto producer_channel = ProducerQueueChannel::Create(
+ this, queue_id, meta_size_bytes, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask, &error)) {
+ message.SetChannel(producer_channel);
+ return 0;
+ } else {
+ ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+ return error;
+ }
+}
+
+bool BufferHubService::AddNamedBuffer(
+ const std::string& name, const std::shared_ptr<ProducerChannel>& buffer) {
+ auto search = named_buffers_.find(name);
+ if (search == named_buffers_.end()) {
+ named_buffers_.emplace(name, buffer);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+std::shared_ptr<ProducerChannel> BufferHubService::GetNamedBuffer(
+ const std::string& name) {
+ auto search = named_buffers_.find(name);
+ if (search != named_buffers_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+bool BufferHubService::RemoveNamedBuffer(const ProducerChannel& buffer) {
+ for (auto it = named_buffers_.begin(); it != named_buffers_.end();) {
+ if (it->second.get() == &buffer) {
+ named_buffers_.erase(it);
+ return true;
+ }
+ ++it;
+ }
+ return false;
+}
+
+void BufferHubChannel::SignalAvailable() {
+ ATRACE_NAME("BufferHubChannel::SignalAvailable");
+ if (!IsDetached()) {
+ const int ret = service_->ModifyChannelEvents(channel_id_, 0, POLLIN);
+ ALOGE_IF(ret < 0,
+ "BufferHubChannel::SignalAvailable: failed to signal availability "
+ "channel_id=%d: %s",
+ channel_id_, strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE, "BufferHubChannel::SignalAvailable: detached buffer.");
+ }
+}
+
+void BufferHubChannel::ClearAvailable() {
+ ATRACE_NAME("BufferHubChannel::ClearAvailable");
+ if (!IsDetached()) {
+ const int ret = service_->ModifyChannelEvents(channel_id_, POLLIN, 0);
+ ALOGE_IF(ret < 0,
+ "BufferHubChannel::ClearAvailable: failed to clear availability "
+ "channel_id=%d: %s",
+ channel_id_, strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE, "BufferHubChannel::ClearAvailable: detached buffer.");
+ }
+}
+
+void BufferHubChannel::Hangup() {
+ ATRACE_NAME("BufferHubChannel::Hangup");
+ if (!IsDetached()) {
+ const int ret = service_->ModifyChannelEvents(channel_id_, 0, POLLHUP);
+ ALOGE_IF(
+ ret < 0,
+ "BufferHubChannel::Hangup: failed to signal hangup channel_id=%d: %s",
+ channel_id_, strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE, "BufferHubChannel::Hangup: detached buffer.");
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h
new file mode 100644
index 0000000..28cb468
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -0,0 +1,182 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
+#define ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <hardware/gralloc.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubService;
+class ConsumerChannel;
+class ProducerChannel;
+class ConsumerQueueChannel;
+class ProducerQueueChannel;
+
+class BufferHubChannel : public pdx::Channel {
+ public:
+ enum ChannelType {
+ kProducerType,
+ kConsumerType,
+ kProducerQueueType,
+ kConsumerQueueType,
+ };
+
+ enum : int { kDetachedId = -1 };
+
+ BufferHubChannel(BufferHubService* service, int buffer_id, int channel_id,
+ ChannelType channel_type)
+ : service_(service),
+ buffer_id_(buffer_id),
+ channel_id_(channel_id),
+ channel_type_(channel_type) {}
+ virtual ~BufferHubChannel() {}
+
+ virtual bool HandleMessage(pdx::Message& message) = 0;
+ virtual void HandleImpulse(pdx::Message& message) = 0;
+
+ // Captures buffer info for use by BufferHubService::DumpState().
+ struct BufferInfo {
+ // Common data field shared by BufferProducer and ProducerQueue.
+ int id = -1;
+ int type = -1;
+ size_t consumer_count = 0;
+
+ // Data field for buffer producer.
+ int width = 0;
+ int height = 0;
+ int format = 0;
+ int usage = 0;
+ size_t slice_count = 0;
+ std::string name;
+
+ // Data filed for producer queue.
+ size_t capacity = 0;
+ int usage_set_mask = 0;
+ int usage_clear_mask = 0;
+ int usage_deny_set_mask = 0;
+ int usage_deny_clear_mask = 0;
+
+ BufferInfo(int id, size_t consumer_count, int width, int height, int format,
+ int usage, size_t slice_count, const std::string& name)
+ : id(id),
+ type(kProducerType),
+ consumer_count(consumer_count),
+ width(width),
+ height(height),
+ format(format),
+ usage(usage),
+ slice_count(slice_count),
+ name(name) {}
+
+ BufferInfo(int id, size_t consumer_count, size_t capacity, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask)
+ : id(id),
+ type(kProducerQueueType),
+ consumer_count(consumer_count),
+ capacity(capacity),
+ usage_set_mask(usage_set_mask),
+ usage_clear_mask(usage_clear_mask),
+ usage_deny_set_mask(usage_deny_set_mask),
+ usage_deny_clear_mask(usage_deny_clear_mask) {}
+
+ BufferInfo() {}
+ };
+
+ // Returns the buffer info for this buffer.
+ virtual BufferInfo GetBufferInfo() const = 0;
+
+ // Signal the client fd that an ownership change occurred using POLLIN.
+ void SignalAvailable();
+
+ // Clear the ownership change event.
+ void ClearAvailable();
+
+ // Signal hangup event.
+ void Hangup();
+
+ BufferHubService* service() const { return service_; }
+ ChannelType channel_type() const { return channel_type_; }
+ int buffer_id() const { return buffer_id_; }
+
+ int channel_id() const { return channel_id_; }
+ bool IsDetached() const { return channel_id_ == kDetachedId; }
+
+ void Detach() {
+ if (channel_type_ == kProducerType)
+ channel_id_ = kDetachedId;
+ }
+ void Attach(int channel_id) {
+ if (channel_type_ == kProducerType && channel_id_ == kDetachedId)
+ channel_id_ = channel_id;
+ }
+
+ private:
+ BufferHubService* service_;
+
+ // Static id of the buffer for logging and informational purposes. This id
+ // does not change for the life of the buffer.
+ // TODO(eieio): Consider using an id allocator instead of the originating
+ // channel id; channel ids wrap after 2^31 ids, but this is not a problem in
+ // general because channel ids are not used for any lookup in this service.
+ int buffer_id_;
+
+ // The channel id of the buffer. This may change for a persistent producer
+ // buffer if it is detached and re-attached to another channel.
+ int channel_id_;
+
+ ChannelType channel_type_;
+
+ BufferHubChannel(const BufferHubChannel&) = delete;
+ void operator=(const BufferHubChannel&) = delete;
+};
+
+class BufferHubService : public pdx::ServiceBase<BufferHubService> {
+ public:
+ BufferHubService();
+ ~BufferHubService() override;
+
+ int HandleMessage(pdx::Message& message) override;
+ void HandleImpulse(pdx::Message& message) override;
+
+ void OnChannelClose(pdx::Message& message,
+ const std::shared_ptr<pdx::Channel>& channel) override;
+
+ bool IsInitialized() const override;
+ std::string DumpState(size_t max_length) override;
+
+ bool AddNamedBuffer(const std::string& name,
+ const std::shared_ptr<ProducerChannel>& buffer);
+ std::shared_ptr<ProducerChannel> GetNamedBuffer(const std::string& name);
+ bool RemoveNamedBuffer(const ProducerChannel& buffer);
+
+ private:
+ friend BASE;
+
+ std::unordered_map<std::string, std::shared_ptr<ProducerChannel>>
+ named_buffers_;
+
+ int OnCreateBuffer(pdx::Message& message, int width, int height, int format,
+ int usage, size_t meta_size_bytes, size_t slice_count);
+ int OnCreatePersistentBuffer(pdx::Message& message, const std::string& name,
+ int user_id, int group_id, int width, int height,
+ int format, int usage, size_t meta_size_bytes,
+ size_t slice_count);
+ int OnGetPersistentBuffer(pdx::Message& message, const std::string& name);
+ int OnCreateProducerQueue(pdx::Message& message, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask,
+ int usage_deny_set_mask, int usage_deny_clear_mask);
+
+ BufferHubService(const BufferHubService&) = delete;
+ void operator=(const BufferHubService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_HUB_H_
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
new file mode 100644
index 0000000..a8e2ddf
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -0,0 +1,37 @@
+#include <sched.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+
+#include <dvr/performance_client_api.h>
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include "buffer_hub.h"
+
+int main(int, char**) {
+ int ret = -1;
+ std::shared_ptr<android::pdx::Service> service;
+ std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+ // We need to be able to create endpoints with full perms.
+ umask(0000);
+
+ dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+ CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
+
+ service = android::dvr::BufferHubService::Create();
+ CHECK_ERROR(!service, error, "Failed to create buffer hub service\n");
+ dispatcher->AddService(service);
+
+ ret = dvrSetSchedulerClass(0, "graphics");
+ CHECK_ERROR(ret < 0, error, "Failed to set thread priority");
+
+ ALOGI("Entering message loop.");
+
+ ret = dispatcher->EnterDispatchLoop();
+ CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+ strerror(-ret));
+
+error:
+ return -ret;
+}
diff --git a/services/vr/bufferhubd/bufferhubd.rc b/services/vr/bufferhubd/bufferhubd.rc
new file mode 100644
index 0000000..ceedf1a
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.rc
@@ -0,0 +1,6 @@
+service bufferhubd /system/bin/bufferhubd
+ class core
+ user system
+ group system
+ cpuset /
+
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
new file mode 100644
index 0000000..8db92a3
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -0,0 +1,182 @@
+#include "consumer_channel.h"
+
+#include <cutils/log.h>
+#include <utils/Trace.h>
+
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "producer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id,
+ int channel_id,
+ const std::shared_ptr<Channel> producer)
+ : BufferHubChannel(service, buffer_id, channel_id, kConsumerType),
+ handled_(true),
+ ignored_(false),
+ producer_(producer) {
+ GetProducer()->AddConsumer(this);
+}
+
+ConsumerChannel::~ConsumerChannel() {
+ ALOGD_IF(TRACE, "ConsumerChannel::~ConsumerChannel: channel_id=%d",
+ channel_id());
+
+ if (auto producer = GetProducer()) {
+ if (!handled_) // Producer is waiting for our Release.
+ producer->OnConsumerIgnored();
+ producer->RemoveConsumer(this);
+ }
+}
+
+BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const {
+ BufferHubChannel::BufferInfo info;
+ if (auto producer = GetProducer()) {
+ // If producer has not hung up, copy most buffer info from the producer.
+ info = producer->GetBufferInfo();
+ }
+ info.id = buffer_id();
+ return info;
+}
+
+std::shared_ptr<ProducerChannel> ConsumerChannel::GetProducer() const {
+ return std::static_pointer_cast<ProducerChannel>(producer_.lock());
+}
+
+void ConsumerChannel::HandleImpulse(Message& message) {
+ ATRACE_NAME("ConsumerChannel::HandleImpulse");
+ switch (message.GetOp()) {
+ case BufferHubRPC::ConsumerRelease::Opcode:
+ OnConsumerRelease(message, {});
+ break;
+ }
+}
+
+bool ConsumerChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ConsumerChannel::HandleMessage");
+ auto producer = GetProducer();
+ if (!producer)
+ REPLY_ERROR_RETURN(message, EPIPE, true);
+
+ switch (message.GetOp()) {
+ case BufferHubRPC::GetBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+ *producer, &ProducerChannel::OnGetBuffer, message);
+ return true;
+
+ case BufferHubRPC::GetBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+ *producer, &ProducerChannel::OnGetBuffers, message);
+ return true;
+
+ case BufferHubRPC::NewConsumer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+ *producer, &ProducerChannel::OnNewConsumer, message);
+ return true;
+
+ case BufferHubRPC::ConsumerAcquire::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerAcquire>(
+ *this, &ConsumerChannel::OnConsumerAcquire, message);
+ return true;
+
+ case BufferHubRPC::ConsumerRelease::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerRelease>(
+ *this, &ConsumerChannel::OnConsumerRelease, message);
+ return true;
+
+ case BufferHubRPC::ConsumerSetIgnore::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(
+ *this, &ConsumerChannel::OnConsumerSetIgnore, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+std::pair<BorrowedFence, ConsumerChannel::MetaData>
+ConsumerChannel::OnConsumerAcquire(Message& message,
+ std::size_t metadata_size) {
+ ATRACE_NAME("ConsumerChannel::OnConsumerAcquire");
+ auto producer = GetProducer();
+ if (!producer)
+ REPLY_ERROR_RETURN(message, EPIPE, {});
+
+ if (ignored_ || handled_) {
+ ALOGE(
+ "ConsumerChannel::OnConsumerAcquire: Acquire when not posted: "
+ "ignored=%d handled=%d channel_id=%d buffer_id=%d",
+ ignored_, handled_, message.GetChannelId(), producer->buffer_id());
+ REPLY_ERROR_RETURN(message, EBUSY, {});
+ } else {
+ ClearAvailable();
+ return producer->OnConsumerAcquire(message, metadata_size);
+ }
+}
+
+int ConsumerChannel::OnConsumerRelease(Message& message,
+ LocalFence release_fence) {
+ ATRACE_NAME("ConsumerChannel::OnConsumerRelease");
+ auto producer = GetProducer();
+ if (!producer)
+ return -EPIPE;
+
+ if (ignored_ || handled_) {
+ ALOGE(
+ "ConsumerChannel::OnConsumerRelease: Release when not acquired: "
+ "ignored=%d handled=%d channel_id=%d buffer_id=%d",
+ ignored_, handled_, message.GetChannelId(), producer->buffer_id());
+ return -EBUSY;
+ } else {
+ ClearAvailable();
+ const int ret =
+ producer->OnConsumerRelease(message, std::move(release_fence));
+ handled_ = ret == 0;
+ return ret;
+ }
+}
+
+int ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) {
+ ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore");
+ auto producer = GetProducer();
+ if (!producer)
+ return -EPIPE;
+
+ ignored_ = ignored;
+ if (ignored_ && !handled_) {
+ // Update the producer if ignore is set after the consumer acquires the
+ // buffer.
+ ClearAvailable();
+ producer->OnConsumerIgnored();
+ handled_ = false;
+ }
+
+ return 0;
+}
+
+bool ConsumerChannel::OnProducerPosted() {
+ if (ignored_) {
+ handled_ = true;
+ return false;
+ } else {
+ handled_ = false;
+ SignalAvailable();
+ return true;
+ }
+}
+
+void ConsumerChannel::OnProducerClosed() {
+ producer_.reset();
+ Hangup();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/consumer_channel.h
new file mode 100644
index 0000000..d2a078f
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+// Consumer channels are attached to a Producer channel
+class ConsumerChannel : public BufferHubChannel {
+ public:
+ using Channel = pdx::Channel;
+ using Message = pdx::Message;
+
+ ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id,
+ const std::shared_ptr<Channel> producer);
+ ~ConsumerChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ bool OnProducerPosted();
+ void OnProducerClosed();
+
+ private:
+ using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+
+ std::shared_ptr<ProducerChannel> GetProducer() const;
+
+ std::pair<BorrowedFence, MetaData> OnConsumerAcquire(
+ Message& message, std::size_t metadata_size);
+ int OnConsumerRelease(Message& message, LocalFence release_fence);
+ int OnConsumerSetIgnore(Message& message, bool ignore);
+
+ bool handled_; // True if we have processed RELEASE.
+ bool ignored_; // True if we are ignoring events.
+ std::weak_ptr<Channel> producer_;
+
+ ConsumerChannel(const ConsumerChannel&) = delete;
+ void operator=(const ConsumerChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
new file mode 100644
index 0000000..39d6bc8
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -0,0 +1,122 @@
+#include "consumer_queue_channel.h"
+
+#include <pdx/channel_handle.h>
+
+#include "producer_channel.h"
+
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ConsumerQueueChannel::ConsumerQueueChannel(
+ BufferHubService* service, int buffer_id, int channel_id,
+ const std::shared_ptr<Channel>& producer)
+ : BufferHubChannel(service, buffer_id, channel_id, kConsumerQueueType),
+ producer_(producer),
+ capacity_(0) {
+ GetProducer()->AddConsumer(this);
+}
+
+ConsumerQueueChannel::~ConsumerQueueChannel() {
+ ALOGD_IF(TRACE, "ConsumerQueueChannel::~ConsumerQueueChannel: channel_id=%d",
+ channel_id());
+
+ if (auto producer = GetProducer()) {
+ producer->RemoveConsumer(this);
+ }
+}
+
+bool ConsumerQueueChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ConsumerQueueChannel::HandleMessage");
+ auto producer = GetProducer();
+ if (!producer)
+ REPLY_ERROR_RETURN(message, EPIPE, true);
+
+ switch (message.GetOp()) {
+ case BufferHubRPC::CreateConsumerQueue::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateConsumerQueue>(
+ *producer, &ProducerQueueChannel::OnCreateConsumerQueue, message);
+ return true;
+
+ case BufferHubRPC::ConsumerQueueImportBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(
+ *this, &ConsumerQueueChannel::OnConsumerQueueImportBuffers, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+std::shared_ptr<ProducerQueueChannel> ConsumerQueueChannel::GetProducer()
+ const {
+ return std::static_pointer_cast<ProducerQueueChannel>(producer_.lock());
+}
+
+void ConsumerQueueChannel::HandleImpulse(Message& /* message */) {
+ ATRACE_NAME("ConsumerQueueChannel::HandleImpulse");
+}
+
+BufferHubChannel::BufferInfo ConsumerQueueChannel::GetBufferInfo() const {
+ BufferHubChannel::BufferInfo info;
+ if (auto producer = GetProducer()) {
+ // If producer has not hung up, copy most buffer info from the producer.
+ info = producer->GetBufferInfo();
+ }
+ info.id = buffer_id();
+ info.capacity = capacity_;
+ return info;
+}
+
+void ConsumerQueueChannel::RegisterNewBuffer(
+ const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
+ pending_buffer_slots_.emplace(producer_channel, slot);
+
+ // Signal the client that there is new buffer available throught POLLIN.
+ SignalAvailable();
+}
+
+std::vector<std::pair<RemoteChannelHandle, size_t>>
+ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
+ std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
+ ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
+ ALOGD(
+ "ConsumerQueueChannel::OnConsumerQueueImportBuffers number of buffers to "
+ "import: %zu",
+ pending_buffer_slots_.size());
+
+ while (!pending_buffer_slots_.empty()) {
+ auto producer_channel = pending_buffer_slots_.front().first.lock();
+ size_t producer_slot = pending_buffer_slots_.front().second;
+ pending_buffer_slots_.pop();
+
+ // It's possible that the producer channel has expired.
+ if (producer_channel == nullptr) {
+ ALOGE(
+ "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
+ "channel has already been expired.");
+ REPLY_ERROR_RETURN(message, ENOENT, {});
+ }
+
+ RemoteChannelHandle consumer_handle(
+ producer_channel->CreateConsumer(message));
+
+ // All buffer imports should succeed together.
+ if (!consumer_handle.valid()) {
+ ALOGE(
+ "ConsumerQueueChannel::OnConsumerQueueImportBuffers: imported "
+ "consumer handle is invalid.");
+ REPLY_ERROR_RETURN(message, EIO, {});
+ }
+
+ // Move consumer_handle into buffer_handles.
+ buffer_handles.emplace_back(std::move(consumer_handle), producer_slot);
+ }
+
+ return buffer_handles;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/consumer_queue_channel.h
new file mode 100644
index 0000000..b345595
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.h
@@ -0,0 +1,62 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <private/dvr/bufferhub_rpc.h>
+
+#include <queue>
+
+#include "consumer_channel.h"
+#include "producer_queue_channel.h"
+
+namespace android {
+namespace dvr {
+
+class ConsumerQueueChannel : public BufferHubChannel {
+ public:
+ using Message = pdx::Message;
+ using RemoteChannelHandle = pdx::RemoteChannelHandle;
+
+ ConsumerQueueChannel(BufferHubService* service, int buffer_id, int channel_id,
+ const std::shared_ptr<Channel>& producer);
+ ~ConsumerQueueChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ // Called by ProdcuerQueueChannel to notify consumer queue that a new
+ // buffer has been allocated.
+ void RegisterNewBuffer(
+ const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot);
+
+ // Called after clients been signaled by service that new buffer has been
+ // allocated. Clients uses kOpConsumerQueueImportBuffers to import new
+ // consumer buffers and this handler returns a vector of fd representing
+ // BufferConsumers that clients can import.
+ std::vector<std::pair<RemoteChannelHandle, size_t>>
+ OnConsumerQueueImportBuffers(Message& message);
+
+ private:
+ std::shared_ptr<ProducerQueueChannel> GetProducer() const;
+
+ // Pointer to the prodcuer channel
+ std::weak_ptr<Channel> producer_;
+
+ // Tracks newly allocated buffer producers along with it's slot number.
+ std::queue<std::pair<std::weak_ptr<ProducerChannel>, size_t>>
+ pending_buffer_slots_;
+
+ // Tracks how many buffers have this queue imported.
+ size_t capacity_;
+
+ ConsumerQueueChannel(const ConsumerQueueChannel&) = delete;
+ void operator=(const ConsumerQueueChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
new file mode 100644
index 0000000..b87b709
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -0,0 +1,377 @@
+#include "producer_channel.h"
+
+#include <cutils/log.h>
+#include <sync/sync.h>
+#include <sys/poll.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <atomic>
+#include <thread>
+
+#include <base/logging.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::BufferWrapper;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::WrapBuffer;
+
+namespace android {
+namespace dvr {
+
+ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id,
+ int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count,
+ int* error)
+ : BufferHubChannel(service, channel_id, channel_id, kProducerType),
+ pending_consumers_(0),
+ slices_(std::max(static_cast<size_t>(1), slice_count)),
+ producer_owns_(true),
+ meta_size_bytes_(meta_size_bytes),
+ meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) {
+ for (auto& ion_buffer : slices_) {
+ const int ret = ion_buffer.Alloc(width, height, format, usage);
+ if (ret < 0) {
+ ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s",
+ strerror(-ret));
+ *error = ret;
+ return;
+ }
+ }
+
+ // Success.
+ *error = 0;
+}
+
+std::shared_ptr<ProducerChannel> ProducerChannel::Create(
+ BufferHubService* service, int channel_id, int width, int height,
+ int format, int usage, size_t meta_size_bytes, size_t slice_count,
+ int* error) {
+ std::shared_ptr<ProducerChannel> producer(
+ new ProducerChannel(service, channel_id, width, height, format, usage,
+ meta_size_bytes, slice_count, error));
+ if (*error < 0)
+ return nullptr;
+ else
+ return producer;
+}
+
+ProducerChannel::~ProducerChannel() {
+ ALOGD_IF(TRACE, "ProducerChannel::~ProducerChannel: channel_id=%d",
+ channel_id());
+ for (auto consumer : consumer_channels_)
+ consumer->OnProducerClosed();
+}
+
+BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
+ return BufferInfo(buffer_id(), consumer_channels_.size(), slices_[0].width(),
+ slices_[0].height(), slices_[0].format(),
+ slices_[0].usage(), slices_.size(), name_);
+}
+
+void ProducerChannel::HandleImpulse(Message& message) {
+ ATRACE_NAME("ProducerChannel::HandleImpulse");
+ switch (message.GetOp()) {
+ case BufferHubRPC::ProducerGain::Opcode:
+ OnProducerGain(message);
+ break;
+ }
+}
+
+bool ProducerChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ProducerChannel::HandleMessage");
+ switch (message.GetOp()) {
+ case BufferHubRPC::GetBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+ *this, &ProducerChannel::OnGetBuffer, message);
+ return true;
+
+ case BufferHubRPC::GetBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+ *this, &ProducerChannel::OnGetBuffers, message);
+ return true;
+
+ case BufferHubRPC::NewConsumer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+ *this, &ProducerChannel::OnNewConsumer, message);
+ return true;
+
+ case BufferHubRPC::ProducerPost::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerPost>(
+ *this, &ProducerChannel::OnProducerPost, message);
+ return true;
+
+ case BufferHubRPC::ProducerGain::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerGain>(
+ *this, &ProducerChannel::OnProducerGain, message);
+ return true;
+
+ case BufferHubRPC::ProducerMakePersistent::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerMakePersistent>(
+ *this, &ProducerChannel::OnProducerMakePersistent, message);
+ return true;
+
+ case BufferHubRPC::ProducerRemovePersistence::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerRemovePersistence>(
+ *this, &ProducerChannel::OnRemovePersistence, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+NativeBufferHandle<BorrowedHandle> ProducerChannel::OnGetBuffer(
+ Message& message, unsigned index) {
+ ATRACE_NAME("ProducerChannel::OnGetBuffer");
+ ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id());
+ if (index < slices_.size()) {
+ return NativeBufferHandle<BorrowedHandle>(slices_[index], buffer_id());
+ } else {
+ REPLY_ERROR_RETURN(message, EINVAL, NativeBufferHandle<BorrowedHandle>());
+ }
+}
+
+std::vector<NativeBufferHandle<BorrowedHandle>> ProducerChannel::OnGetBuffers(
+ Message&) {
+ ATRACE_NAME("ProducerChannel::OnGetBuffers");
+ ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffers: buffer_id=%d", buffer_id());
+ std::vector<NativeBufferHandle<BorrowedHandle>> buffer_handles;
+ for (const auto& buffer : slices_)
+ buffer_handles.emplace_back(buffer, buffer_id());
+ return buffer_handles;
+}
+
+RemoteChannelHandle ProducerChannel::CreateConsumer(Message& message) {
+ ATRACE_NAME("ProducerChannel::CreateConsumer");
+ ALOGD_IF(TRACE, "ProducerChannel::CreateConsumer: buffer_id=%d", buffer_id());
+
+ int channel_id;
+ auto status = message.PushChannel(0, nullptr, &channel_id);
+ if (!status) {
+ ALOGE(
+ "ProducerChannel::CreateConsumer: failed to push consumer channel: %s",
+ status.GetErrorMessage().c_str());
+ return RemoteChannelHandle();
+ }
+
+ auto consumer = std::make_shared<ConsumerChannel>(
+ service(), buffer_id(), channel_id, shared_from_this());
+ const int ret = service()->SetChannel(channel_id, consumer);
+ if (ret < 0) {
+ ALOGE(
+ "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
+ "%s",
+ strerror(-ret));
+ return RemoteChannelHandle();
+ }
+
+ if (!producer_owns_) {
+ // Signal the new consumer when adding it to a posted producer.
+ if (consumer->OnProducerPosted())
+ pending_consumers_++;
+ }
+
+ return status.take();
+}
+
+RemoteChannelHandle ProducerChannel::OnNewConsumer(Message& message) {
+ ATRACE_NAME("ProducerChannel::OnNewConsumer");
+ ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
+
+ RemoteChannelHandle consumer_handle(CreateConsumer(message));
+
+ if (consumer_handle.valid())
+ return consumer_handle;
+ else
+ REPLY_ERROR_RETURN(message, ENOMEM, RemoteChannelHandle());
+}
+
+int ProducerChannel::OnProducerPost(
+ Message&, LocalFence acquire_fence,
+ BufferWrapper<std::vector<std::uint8_t>> metadata) {
+ ATRACE_NAME("ProducerChannel::OnProducerPost");
+ ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
+ if (!producer_owns_) {
+ ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
+ return -EBUSY;
+ }
+
+ if (meta_size_bytes_ != metadata.size())
+ return -EINVAL;
+ std::copy(metadata.begin(), metadata.end(), meta_.get());
+
+ post_fence_ = std::move(acquire_fence);
+ producer_owns_ = false;
+
+ // Signal any interested consumers. If there are none, automatically release
+ // the buffer.
+ pending_consumers_ = 0;
+ for (auto consumer : consumer_channels_) {
+ if (consumer->OnProducerPosted())
+ pending_consumers_++;
+ }
+ if (pending_consumers_ == 0)
+ SignalAvailable();
+ ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
+ pending_consumers_);
+
+ return 0;
+}
+
+LocalFence ProducerChannel::OnProducerGain(Message& message) {
+ ATRACE_NAME("ProducerChannel::OnGain");
+ ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
+ if (producer_owns_) {
+ ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
+ channel_id());
+ REPLY_ERROR_RETURN(message, EALREADY, {});
+ }
+
+ // There are still pending consumers, return busy.
+ if (pending_consumers_ > 0)
+ REPLY_ERROR_RETURN(message, EBUSY, {});
+
+ ClearAvailable();
+ producer_owns_ = true;
+ post_fence_.get_fd();
+ return std::move(returned_fence_);
+}
+
+std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>>
+ProducerChannel::OnConsumerAcquire(Message& message,
+ std::size_t metadata_size) {
+ ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
+ ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
+ buffer_id());
+ if (producer_owns_) {
+ ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
+ REPLY_ERROR_RETURN(message, EBUSY, {});
+ }
+
+ // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
+ // Serialization just needs to read the handle.
+ if (metadata_size == 0)
+ return std::make_pair(post_fence_.borrow(),
+ WrapBuffer<std::uint8_t>(nullptr, 0));
+ else
+ return std::make_pair(post_fence_.borrow(),
+ WrapBuffer(meta_.get(), meta_size_bytes_));
+}
+
+int ProducerChannel::OnConsumerRelease(Message&, LocalFence release_fence) {
+ ATRACE_NAME("ProducerChannel::OnConsumerRelease");
+ ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
+ buffer_id());
+ if (producer_owns_) {
+ ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
+ return -EBUSY;
+ }
+
+ // Attempt to merge the fences if necessary.
+ if (release_fence) {
+ if (returned_fence_) {
+ LocalFence merged_fence(sync_merge(
+ "bufferhub_merged", returned_fence_.get_fd(), release_fence.get_fd()));
+ const int error = errno;
+ if (!merged_fence) {
+ ALOGE("ProducerChannel::OnConsumerRelease: Failed to merge fences: %s",
+ strerror(error));
+ return -error;
+ }
+ returned_fence_ = std::move(merged_fence);
+ } else {
+ returned_fence_ = std::move(release_fence);
+ }
+ }
+
+ OnConsumerIgnored();
+ return 0;
+}
+
+void ProducerChannel::OnConsumerIgnored() {
+ if (!--pending_consumers_)
+ SignalAvailable();
+ ALOGD_IF(TRACE,
+ "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
+ buffer_id(), pending_consumers_);
+}
+
+int ProducerChannel::OnProducerMakePersistent(Message& message,
+ const std::string& name,
+ int user_id, int group_id) {
+ ATRACE_NAME("ProducerChannel::OnProducerMakePersistent");
+ ALOGD_IF(TRACE,
+ "ProducerChannel::OnProducerMakePersistent: buffer_id=%d name=%s "
+ "user_id=%d group_id=%d",
+ buffer_id(), name.c_str(), user_id, group_id);
+
+ if (name.empty() || (user_id < 0 && user_id != kNoCheckId) ||
+ (group_id < 0 && group_id != kNoCheckId)) {
+ return -EINVAL;
+ }
+
+ // Try to add this buffer with the requested name.
+ if (service()->AddNamedBuffer(name, std::static_pointer_cast<ProducerChannel>(
+ shared_from_this()))) {
+ // If successful, set the requested permissions.
+
+ // A value of zero indicates that the ids from the sending process should be
+ // used.
+ if (user_id == kUseCallerId)
+ user_id = message.GetEffectiveUserId();
+ if (group_id == kUseCallerId)
+ group_id = message.GetEffectiveGroupId();
+
+ owner_user_id_ = user_id;
+ owner_group_id_ = group_id;
+ name_ = name;
+ return 0;
+ } else {
+ // Otherwise a buffer with that name already exists.
+ return -EALREADY;
+ }
+}
+
+int ProducerChannel::OnRemovePersistence(Message&) {
+ if (service()->RemoveNamedBuffer(*this))
+ return 0;
+ else
+ return -ENOENT;
+}
+
+void ProducerChannel::AddConsumer(ConsumerChannel* channel) {
+ consumer_channels_.push_back(channel);
+}
+
+void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
+ consumer_channels_.erase(
+ std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+}
+
+// Returns true if either the user or group ids match the owning ids or both
+// owning ids are not set, in which case access control does not apply.
+bool ProducerChannel::CheckAccess(int euid, int egid) {
+ const bool no_check =
+ owner_user_id_ == kNoCheckId && owner_group_id_ == kNoCheckId;
+ const bool euid_check = euid == owner_user_id_ || euid == kRootId;
+ const bool egid_check = egid == owner_group_id_ || egid == kRootId;
+ return no_check || euid_check || egid_check;
+}
+
+// Returns true if the given parameters match the underlying buffer parameters.
+bool ProducerChannel::CheckParameters(int width, int height, int format,
+ int usage, size_t meta_size_bytes,
+ size_t slice_count) {
+ return slices_.size() == slice_count &&
+ meta_size_bytes == meta_size_bytes_ && slices_[0].width() == width &&
+ slices_[0].height() == height && slices_[0].format() == format &&
+ slices_[0].usage() == usage;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
new file mode 100644
index 0000000..e7ca459
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -0,0 +1,109 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// The buffer changes ownership according to the following sequence:
+// POST -> ACQUIRE/RELEASE (all consumers) -> GAIN (producer acquires) -> POST
+
+// The producer channel is owned by a single app that writes into buffers and
+// calls POST when drawing is complete. This channel has a set of consumer
+// channels associated with it that are waiting for notifications.
+class ProducerChannel : public BufferHubChannel {
+ public:
+ using Message = pdx::Message;
+ using BorrowedHandle = pdx::BorrowedHandle;
+ using RemoteChannelHandle = pdx::RemoteChannelHandle;
+ template <typename T>
+ using BufferWrapper = pdx::rpc::BufferWrapper<T>;
+
+ static std::shared_ptr<ProducerChannel> Create(
+ BufferHubService* service, int channel_id, int width, int height,
+ int format, int usage, size_t meta_size_bytes, size_t slice_count,
+ int* error);
+
+ ~ProducerChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ NativeBufferHandle<BorrowedHandle> OnGetBuffer(Message& message,
+ unsigned index);
+ std::vector<NativeBufferHandle<BorrowedHandle>> OnGetBuffers(
+ Message& message);
+
+ RemoteChannelHandle CreateConsumer(Message& message);
+ RemoteChannelHandle OnNewConsumer(Message& message);
+
+ std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>> OnConsumerAcquire(
+ Message& message, std::size_t metadata_size);
+ int OnConsumerRelease(Message& message, LocalFence release_fence);
+
+ void OnConsumerIgnored();
+
+ void AddConsumer(ConsumerChannel* channel);
+ void RemoveConsumer(ConsumerChannel* channel);
+
+ bool CheckAccess(int euid, int egid);
+ bool CheckParameters(int width, int height, int format, int usage,
+ size_t meta_size_bytes, size_t slice_count);
+
+ int OnProducerMakePersistent(Message& message, const std::string& name,
+ int user_id, int group_id);
+ int OnRemovePersistence(Message& message);
+
+ private:
+ std::vector<ConsumerChannel*> consumer_channels_;
+ // This counts the number of consumers left to process this buffer. If this is
+ // zero then the producer can re-acquire ownership.
+ int pending_consumers_;
+
+ std::vector<IonBuffer> slices_;
+
+ bool producer_owns_;
+ LocalFence post_fence_;
+ LocalFence returned_fence_;
+ size_t meta_size_bytes_;
+ std::unique_ptr<uint8_t[]> meta_;
+
+ static constexpr int kNoCheckId = -1;
+ static constexpr int kUseCallerId = 0;
+ static constexpr int kRootId = 0;
+
+ // User and group id to check when obtaining a persistent buffer.
+ int owner_user_id_ = kNoCheckId;
+ int owner_group_id_ = kNoCheckId;
+
+ std::string name_;
+
+ ProducerChannel(BufferHubService* service, int channel, int width, int height,
+ int format, int usage, size_t meta_size_bytes,
+ size_t slice_count, int* error);
+
+ int OnProducerPost(Message& message, LocalFence acquire_fence,
+ BufferWrapper<std::vector<std::uint8_t>> metadata);
+ LocalFence OnProducerGain(Message& message);
+
+ ProducerChannel(const ProducerChannel&) = delete;
+ void operator=(const ProducerChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
new file mode 100644
index 0000000..08f1e9d
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -0,0 +1,287 @@
+#include "producer_queue_channel.h"
+
+#include "consumer_queue_channel.h"
+#include "producer_channel.h"
+
+using android::pdx::RemoteChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+ProducerQueueChannel::ProducerQueueChannel(
+ BufferHubService* service, int channel_id, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error)
+ : BufferHubChannel(service, channel_id, channel_id, kProducerQueueType),
+ meta_size_bytes_(meta_size_bytes),
+ usage_set_mask_(usage_set_mask),
+ usage_clear_mask_(usage_clear_mask),
+ usage_deny_set_mask_(usage_deny_set_mask),
+ usage_deny_clear_mask_(usage_deny_clear_mask),
+ capacity_(0) {
+ *error = 0;
+}
+
+ProducerQueueChannel::~ProducerQueueChannel() {}
+
+/* static */
+std::shared_ptr<ProducerQueueChannel> ProducerQueueChannel::Create(
+ BufferHubService* service, int channel_id, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error) {
+ // Configuration between |usage_deny_set_mask| and |usage_deny_clear_mask|
+ // should be mutually exclusive.
+ if (usage_deny_set_mask & usage_deny_clear_mask) {
+ ALOGE(
+ "BufferHubService::OnCreateProducerQueue: illegal usage mask "
+ "configuration: usage_deny_set_mask=%d, usage_deny_clear_mask=%d",
+ usage_deny_set_mask, usage_deny_clear_mask);
+ *error = -EINVAL;
+ return nullptr;
+ }
+
+ std::shared_ptr<ProducerQueueChannel> producer(new ProducerQueueChannel(
+ service, channel_id, meta_size_bytes, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask, error));
+ if (*error < 0)
+ return nullptr;
+ else
+ return producer;
+}
+
+bool ProducerQueueChannel::HandleMessage(Message& message) {
+ ATRACE_NAME("ProducerQueueChannel::HandleMessage");
+ switch (message.GetOp()) {
+ case BufferHubRPC::CreateConsumerQueue::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::CreateConsumerQueue>(
+ *this, &ProducerQueueChannel::OnCreateConsumerQueue, message);
+ return true;
+
+ case BufferHubRPC::ProducerQueueAllocateBuffers::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+ *this, &ProducerQueueChannel::OnProducerQueueAllocateBuffers,
+ message);
+ return true;
+
+ case BufferHubRPC::ProducerQueueDetachBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(
+ *this, &ProducerQueueChannel::OnProducerQueueDetachBuffer, message);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void ProducerQueueChannel::HandleImpulse(Message& /* message */) {
+ ATRACE_NAME("ProducerQueueChannel::HandleImpulse");
+}
+
+BufferHubChannel::BufferInfo ProducerQueueChannel::GetBufferInfo() const {
+ return BufferInfo(channel_id(), consumer_channels_.size(), capacity_,
+ usage_set_mask_, usage_clear_mask_, usage_deny_set_mask_,
+ usage_deny_clear_mask_);
+}
+
+std::pair<RemoteChannelHandle, size_t>
+ProducerQueueChannel::OnCreateConsumerQueue(Message& message) {
+ ATRACE_NAME("ProducerQueueChannel::OnCreateConsumerQueue");
+ ALOGD_IF(TRACE, "ProducerQueueChannel::OnCreateConsumerQueue: channel_id=%d",
+ channel_id());
+
+ int channel_id;
+ auto status = message.PushChannel(0, nullptr, &channel_id);
+ if (!status) {
+ ALOGE(
+ "ProducerQueueChannel::OnCreateConsumerQueue: failed to push consumer "
+ "channel: %s",
+ status.GetErrorMessage().c_str());
+ REPLY_ERROR_RETURN(message, ENOMEM, {});
+ }
+
+ const int ret = service()->SetChannel(
+ channel_id, std::make_shared<ConsumerQueueChannel>(
+ service(), buffer_id(), channel_id, shared_from_this()));
+ if (ret < 0) {
+ ALOGE(
+ "ProducerQueueChannel::OnCreateConsumerQueue: failed to set new "
+ "consumer channel: %s",
+ strerror(-ret));
+ REPLY_ERROR_RETURN(message, ENOMEM, {});
+ }
+
+ return std::make_pair(status.take(), meta_size_bytes_);
+}
+
+std::vector<std::pair<RemoteChannelHandle, size_t>>
+ProducerQueueChannel::OnProducerQueueAllocateBuffers(Message& message,
+ int width, int height,
+ int format, int usage,
+ size_t slice_count,
+ size_t buffer_count) {
+ ATRACE_NAME("ProducerQueueChannel::OnProducerQueueAllocateBuffers");
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: "
+ "producer_channel_id=%d",
+ channel_id());
+
+ std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
+
+ // Deny buffer allocation violating preset rules.
+ if (usage & usage_deny_set_mask_) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %d is "
+ "not permitted. Violating usage_deny_set_mask, the following bits "
+ "shall not be set: %d.",
+ usage, usage_deny_set_mask_);
+ REPLY_ERROR_RETURN(message, EINVAL, buffer_handles);
+ }
+
+ if (~usage & usage_deny_clear_mask_) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %d is "
+ "not permitted. Violating usage_deny_clear_mask, the following bits "
+ "must be set: %d.",
+ usage, usage_deny_clear_mask_);
+ REPLY_ERROR_RETURN(message, EINVAL, buffer_handles);
+ }
+
+ // Force set mask and clear mask. Note that |usage_set_mask_| takes precedence
+ // and will overwrite |usage_clear_mask_|.
+ int effective_usage = (usage & ~usage_clear_mask_) | usage_set_mask_;
+
+ for (size_t i = 0; i < buffer_count; i++) {
+ auto buffer_handle_slot = AllocateBuffer(message, width, height, format,
+ effective_usage, slice_count);
+ if (!buffer_handle_slot.first) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueAllocateBuffers: failed to "
+ "allocate new buffer.");
+ REPLY_ERROR_RETURN(message, ENOMEM, buffer_handles);
+ }
+ buffer_handles.emplace_back(std::move(buffer_handle_slot.first),
+ buffer_handle_slot.second);
+ }
+
+ return buffer_handles;
+}
+
+std::pair<RemoteChannelHandle, size_t> ProducerQueueChannel::AllocateBuffer(
+ Message& message, int width, int height, int format, int usage,
+ size_t slice_count) {
+ ATRACE_NAME("ProducerQueueChannel::AllocateBuffer");
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::AllocateBuffer: producer_channel_id=%d",
+ channel_id());
+
+ if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) {
+ ALOGE("ProducerQueueChannel::AllocateBuffer: reaches kMaxQueueCapacity.");
+ return {};
+ }
+
+ // Here we are creating a new BufferHubBuffer, initialize the producer
+ // channel, and returning its file handle back to the client.
+ // buffer_id is the id of the producer channel of BufferHubBuffer.
+ int buffer_id;
+ auto status = message.PushChannel(0, nullptr, &buffer_id);
+
+ if (!status) {
+ ALOGE("ProducerQueueChannel::AllocateBuffer: failed to push channel: %s",
+ status.GetErrorMessage().c_str());
+ return {};
+ }
+
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::AllocateBuffer: buffer_id=%d width=%d "
+ "height=%d format=%d usage=%d slice_count=%zu",
+ buffer_id, width, height, format, usage, slice_count);
+ auto buffer_handle = status.take();
+
+ int error;
+ const auto producer_channel = ProducerChannel::Create(
+ service(), buffer_id, width, height, format, usage,
+ meta_size_bytes_, slice_count, &error);
+ if (!producer_channel) {
+ ALOGE(
+ "ProducerQueueChannel::AllocateBuffer: Failed to create "
+ "BufferHubBuffer producer!!");
+ return {};
+ }
+
+ ALOGD_IF(
+ TRACE,
+ "ProducerQueueChannel::AllocateBuffer: buffer_id=%d, buffer_handle=%d",
+ buffer_id, buffer_handle.value());
+
+ const int ret = service()->SetChannel(buffer_id, producer_channel);
+ if (ret < 0) {
+ ALOGE(
+ "ProducerQueueChannel::AllocateBuffer: failed to set prodcuer channel "
+ "for new BufferHubBuffer: %s",
+ strerror(-ret));
+ return {};
+ }
+
+ // Register the newly allocated buffer's channel_id into the first empty
+ // buffer slot.
+ size_t slot = 0;
+ for (; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
+ if (buffers_[slot].expired())
+ break;
+ }
+ if (slot == BufferHubRPC::kMaxQueueCapacity) {
+ ALOGE(
+ "ProducerQueueChannel::AllocateBuffer: Cannot find empty slot for new "
+ "buffer allocation.");
+ return {};
+ }
+
+ buffers_[slot] = producer_channel;
+ capacity_++;
+
+ // Notify each consumer channel about the new buffer.
+ for (auto consumer_channel : consumer_channels_) {
+ ALOGD(
+ "ProducerQueueChannel::AllocateBuffer: Notified consumer with new "
+ "buffer, buffer_id=%d",
+ buffer_id);
+ consumer_channel->RegisterNewBuffer(producer_channel, slot);
+ }
+
+ return {std::move(buffer_handle), slot};
+}
+
+int ProducerQueueChannel::OnProducerQueueDetachBuffer(Message& message,
+ size_t slot) {
+ if (buffers_[slot].expired()) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach "
+ "an invalid buffer producer at slot %zu",
+ slot);
+ return -EINVAL;
+ }
+
+ if (capacity_ == 0) {
+ ALOGE(
+ "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach a "
+ "buffer producer while the queue's capacity is already zero.");
+ return -EINVAL;
+ }
+
+ buffers_[slot].reset();
+ capacity_--;
+ return 0;
+}
+
+void ProducerQueueChannel::AddConsumer(ConsumerQueueChannel* channel) {
+ consumer_channels_.push_back(channel);
+}
+
+void ProducerQueueChannel::RemoveConsumer(ConsumerQueueChannel* channel) {
+ consumer_channels_.erase(
+ std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/producer_queue_channel.h
new file mode 100644
index 0000000..49611d4
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -0,0 +1,96 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class ProducerQueueChannel : public BufferHubChannel {
+ public:
+ using Message = pdx::Message;
+ using RemoteChannelHandle = pdx::RemoteChannelHandle;
+
+ static std::shared_ptr<ProducerQueueChannel> Create(
+ BufferHubService* service, int channel_id, size_t meta_size_bytes,
+ int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error);
+ ~ProducerQueueChannel() override;
+
+ bool HandleMessage(Message& message) override;
+ void HandleImpulse(Message& message) override;
+
+ BufferInfo GetBufferInfo() const override;
+
+ // Handles client request to create a new consumer queue attached to current
+ // producer queue.
+ // Returns a handle for the service channel, as well as the size of the
+ // metadata associated with the queue.
+ std::pair<RemoteChannelHandle, size_t> OnCreateConsumerQueue(
+ Message& message);
+
+ // Allocate a new BufferHubProducer according to the input spec. Client may
+ // handle this as if a new producer is created through kOpCreateBuffer.
+ std::vector<std::pair<RemoteChannelHandle, size_t>>
+ OnProducerQueueAllocateBuffers(Message& message, int width, int height,
+ int format, int usage, size_t slice_count,
+ size_t buffer_count);
+
+ // Detach a BufferHubProducer indicated by |slot|. Note that the buffer must
+ // be in Gain'ed state for the producer queue to detach.
+ int OnProducerQueueDetachBuffer(Message& message, size_t slot);
+
+ void AddConsumer(ConsumerQueueChannel* channel);
+ void RemoveConsumer(ConsumerQueueChannel* channel);
+
+ private:
+ ProducerQueueChannel(BufferHubService* service, int channel_id,
+ size_t meta_size_bytes, int usage_set_mask,
+ int usage_clear_mask, int usage_deny_set_mask,
+ int usage_deny_clear_mask, int* error);
+
+ // Allocate one single producer buffer by |OnProducerQueueAllocateBuffers|.
+ // Note that the newly created buffer's file handle will be pushed to client
+ // and our return type is a RemoteChannelHandle.
+ // Returns the remote channdel handle and the slot number for the newly
+ // allocated buffer.
+ std::pair<RemoteChannelHandle, size_t> AllocateBuffer(Message& message,
+ int width, int height,
+ int format, int usage,
+ size_t slice_count);
+
+ // Size of the meta data associated with all the buffers allocated from the
+ // queue. Now we assume the metadata size is immutable once the queue is
+ // created.
+ size_t meta_size_bytes_;
+
+ // A set of variables to control what |usage| bits can this ProducerQueue
+ // allocate.
+ int usage_set_mask_;
+ int usage_clear_mask_;
+ int usage_deny_set_mask_;
+ int usage_deny_clear_mask_;
+
+ // Provides access to the |channel_id| of all consumer channels associated
+ // with this producer.
+ std::vector<ConsumerQueueChannel*> consumer_channels_;
+
+ // Tracks how many buffers have this queue allocated.
+ size_t capacity_;
+
+ // Tracks of all buffer producer allocated through this buffer queue. Once
+ // a buffer get allocated, it will take a logical slot in the |buffers_| array
+ // and the slot number will stay unchanged during the entire life cycle of the
+ // queue.
+ std::weak_ptr<ProducerChannel> buffers_[BufferHubRPC::kMaxQueueCapacity];
+
+ ProducerQueueChannel(const ProducerQueueChannel&) = delete;
+ void operator=(const ProducerQueueChannel&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
diff --git a/services/vr/performanced/Android.mk b/services/vr/performanced/Android.mk
new file mode 100644
index 0000000..6256e90
--- /dev/null
+++ b/services/vr/performanced/Android.mk
@@ -0,0 +1,49 @@
+# 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 := \
+ cpu_set.cpp \
+ main.cpp \
+ performance_service.cpp \
+ task.cpp
+
+staticLibraries := \
+ libperformance \
+ libpdx_default_transport \
+
+sharedLibraries := \
+ libbase \
+ libcutils \
+ liblog \
+ libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"performanced\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := performanced
+LOCAL_INIT_RC := performanced.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := performance_service_tests.cpp
+LOCAL_STATIC_LIBRARIES := $(staticLibraries) libgtest_main
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := performance_service_tests
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_TEST)
diff --git a/services/vr/performanced/CPPLINT.cfg b/services/vr/performanced/CPPLINT.cfg
new file mode 100644
index 0000000..fd379da
--- /dev/null
+++ b/services/vr/performanced/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-runtime/int
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
new file mode 100644
index 0000000..916226e
--- /dev/null
+++ b/services/vr/performanced/cpu_set.cpp
@@ -0,0 +1,287 @@
+#include "cpu_set.h"
+
+#include <cutils/log.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
+
+#include "directory_reader.h"
+#include "stdio_filebuf.h"
+#include "task.h"
+#include "unique_file.h"
+
+namespace {
+
+constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
+constexpr pid_t kKernelThreadDaemonPid = 2;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+bool CpuSet::prefix_enabled_ = false;
+
+void CpuSetManager::Load(const std::string& cpuset_root) {
+ if (!root_set_)
+ root_set_ = Create(cpuset_root);
+}
+
+std::unique_ptr<CpuSet> CpuSetManager::Create(const std::string& path) {
+ base::unique_fd root_cpuset_fd(open(path.c_str(), kDirectoryFlags));
+ if (root_cpuset_fd.get() < 0) {
+ ALOGE("CpuSet::Create: Failed to open \"%s\": %s", path.c_str(),
+ strerror(errno));
+ return nullptr;
+ }
+
+ return Create(std::move(root_cpuset_fd), "/", nullptr);
+}
+
+std::unique_ptr<CpuSet> CpuSetManager::Create(base::unique_fd base_fd,
+ const std::string& name,
+ CpuSet* parent) {
+ DirectoryReader directory(base::unique_fd(dup(base_fd)));
+ if (!directory) {
+ ALOGE("CpuSet::Create: Failed to opendir %s cpuset: %s", name.c_str(),
+ strerror(directory.GetError()));
+ return nullptr;
+ }
+
+ std::unique_ptr<CpuSet> group(
+ new CpuSet(parent, name, base::unique_fd(dup(base_fd))));
+ path_map_.insert(std::make_pair(group->path(), group.get()));
+
+ while (dirent* entry = directory.Next()) {
+ if (entry->d_type == DT_DIR) {
+ std::string directory_name(entry->d_name);
+
+ if (directory_name == "." || directory_name == "..")
+ continue;
+
+ base::unique_fd entry_fd(
+ openat(base_fd.get(), directory_name.c_str(), kDirectoryFlags));
+ if (entry_fd.get() >= 0) {
+ auto child =
+ Create(std::move(entry_fd), directory_name.c_str(), group.get());
+
+ if (child)
+ group->AddChild(std::move(child));
+ else
+ return nullptr;
+ } else {
+ ALOGE("CpuSet::Create: Failed to openat \"%s\": %s", entry->d_name,
+ strerror(errno));
+ return nullptr;
+ }
+ }
+ }
+
+ return group;
+}
+
+CpuSet* CpuSetManager::Lookup(const std::string& path) {
+ auto search = path_map_.find(path);
+ if (search != path_map_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+std::vector<CpuSet*> CpuSetManager::GetCpuSets() {
+ std::vector<CpuSet*> sets(path_map_.size());
+
+ for (const auto& pair : path_map_) {
+ sets.push_back(pair.second);
+ }
+
+ return sets;
+}
+
+std::string CpuSetManager::DumpState() const {
+ size_t max_path = 0;
+ std::vector<CpuSet*> sets;
+
+ for (const auto& pair : path_map_) {
+ max_path = std::max(max_path, pair.second->path().length());
+ sets.push_back(pair.second);
+ }
+
+ std::sort(sets.begin(), sets.end(), [](const CpuSet* a, const CpuSet* b) {
+ return a->path() < b->path();
+ });
+
+ std::ostringstream stream;
+
+ stream << std::left;
+ stream << std::setw(max_path) << "Path";
+ stream << " ";
+ stream << std::setw(6) << "CPUs";
+ stream << " ";
+ stream << std::setw(6) << "Tasks";
+ stream << std::endl;
+
+ stream << std::string(max_path, '_');
+ stream << " ";
+ stream << std::string(6, '_');
+ stream << " ";
+ stream << std::string(6, '_');
+ stream << std::endl;
+
+ for (const auto set : sets) {
+ stream << std::left;
+ stream << std::setw(max_path) << set->path();
+ stream << " ";
+ stream << std::right;
+ stream << std::setw(6) << set->GetCpuList();
+ stream << " ";
+ stream << std::setw(6) << set->GetTasks().size();
+ stream << std::endl;
+ }
+
+ return stream.str();
+}
+
+void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
+ auto root = Lookup("/");
+ if (!root) {
+ ALOGE("CpuSetManager::MoveUnboundTasks: Failed to find root cpuset!");
+ return;
+ }
+
+ auto target = Lookup(target_set);
+ if (!target) {
+ ALOGE(
+ "CpuSetManager::MoveUnboundTasks: Failed to find target cpuset \"%s\"!",
+ target_set.c_str());
+ return;
+ }
+
+ auto cpu_list = root->GetCpuList();
+
+ for (auto task_id : root->GetTasks()) {
+ Task task(task_id);
+
+ // Move only unbound kernel threads to the target cpuset.
+ if (task.cpus_allowed_list() == cpu_list &&
+ task.parent_process_id() == kKernelThreadDaemonPid) {
+ ALOGD_IF(TRACE,
+ "CpuSetManager::MoveUnboundTasks: Moving task_id=%d name=%s to "
+ "target_set=%s tgid=%d ppid=%d.",
+ task_id, task.name().c_str(), target_set.c_str(),
+ task.thread_group_id(), task.parent_process_id());
+
+ const int ret = target->AttachTask(task_id);
+ ALOGW_IF(ret < 0 && ret != -EINVAL,
+ "CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
+ "to cpuset=%s: %s",
+ task_id, target_set.c_str(), strerror(-ret));
+ } else {
+ ALOGD_IF(TRACE,
+ "CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
+ task_id, task.name().c_str(), task.cpus_allowed_list().c_str());
+ }
+ }
+}
+
+CpuSet::CpuSet(CpuSet* parent, const std::string& name,
+ base::unique_fd&& cpuset_fd)
+ : parent_(parent), name_(name), cpuset_fd_(std::move(cpuset_fd)) {
+ if (parent_ == nullptr)
+ path_ = name_;
+ else if (parent_->IsRoot())
+ path_ = parent_->name() + name_;
+ else
+ path_ = parent_->path() + "/" + name_;
+
+ ALOGI("CpuSet::CpuSet: path=%s", path().c_str());
+}
+
+base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
+ return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
+}
+
+UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
+ return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
+}
+
+base::unique_fd CpuSet::OpenFile(const std::string& name, int flags) const {
+ const std::string relative_path = "./" + name;
+ return base::unique_fd(
+ openat(cpuset_fd_.get(), relative_path.c_str(), flags));
+}
+
+UniqueFile CpuSet::OpenFilePointer(const std::string& name, int flags) const {
+ const std::string relative_path = "./" + name;
+ base::unique_fd fd(openat(cpuset_fd_.get(), relative_path.c_str(), flags));
+ if (fd.get() < 0) {
+ ALOGE("CpuSet::OpenPropertyFilePointer: Failed to open %s/%s: %s",
+ path_.c_str(), name.c_str(), strerror(errno));
+ return nullptr;
+ }
+
+ UniqueFile fp(fdopen(fd.release(), "r"));
+ if (!fp)
+ ALOGE("CpuSet::OpenPropertyFilePointer: Failed to fdopen %s/%s: %s",
+ path_.c_str(), name.c_str(), strerror(errno));
+
+ return fp;
+}
+
+int CpuSet::AttachTask(pid_t task_id) const {
+ auto file = OpenFile("tasks", O_RDWR);
+ if (file.get() >= 0) {
+ std::ostringstream stream;
+ stream << task_id;
+ std::string value = stream.str();
+
+ const bool ret = base::WriteStringToFd(value, file.get());
+ return !ret ? -errno : 0;
+ } else {
+ ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
+ strerror(errno));
+ return -errno;
+ }
+}
+
+std::vector<pid_t> CpuSet::GetTasks() const {
+ std::vector<pid_t> tasks;
+
+ if (auto file = OpenFilePointer("tasks")) {
+ stdio_filebuf<char> filebuf(file.get());
+ std::istream file_stream(&filebuf);
+
+ for (std::string line; std::getline(file_stream, line);) {
+ pid_t task_id = std::strtol(line.c_str(), nullptr, 10);
+ tasks.push_back(task_id);
+ }
+ }
+
+ return tasks;
+}
+
+std::string CpuSet::GetCpuList() const {
+ if (auto file = OpenPropertyFilePointer("cpus")) {
+ stdio_filebuf<char> filebuf(file.get());
+ std::istream file_stream(&filebuf);
+
+ std::string line;
+ if (std::getline(file_stream, line))
+ return line;
+ }
+
+ ALOGE("CpuSet::GetCpuList: Failed to read cpu list!!!");
+ return "";
+}
+
+void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
+ children_.push_back(std::move(child));
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
new file mode 100644
index 0000000..fcf280a
--- /dev/null
+++ b/services/vr/performanced/cpu_set.h
@@ -0,0 +1,105 @@
+#ifndef ANDROID_DVR_PERFORMANCED_CPU_SET_H_
+#define ANDROID_DVR_PERFORMANCED_CPU_SET_H_
+
+#include <fcntl.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "unique_file.h"
+
+namespace android {
+namespace dvr {
+
+class CpuSet {
+ public:
+ // Returns the parent group for this group, if any. This pointer is owned by
+ // the group hierarchy and is only valid as long as the hierarchy is valid.
+ CpuSet* parent() const { return parent_; }
+ std::string name() const { return name_; }
+ std::string path() const { return path_; }
+
+ bool IsRoot() const { return parent_ == nullptr; }
+
+ std::string GetCpuList() const;
+
+ int AttachTask(pid_t task_id) const;
+ std::vector<pid_t> GetTasks() const;
+
+ private:
+ friend class CpuSetManager;
+
+ CpuSet(CpuSet* parent, const std::string& name, base::unique_fd&& cpuset_fd);
+
+ void AddChild(std::unique_ptr<CpuSet> child);
+
+ base::unique_fd OpenPropertyFile(const std::string& name) const;
+ UniqueFile OpenPropertyFilePointer(const std::string& name) const;
+
+ base::unique_fd OpenFile(const std::string& name, int flags = O_RDONLY) const;
+ UniqueFile OpenFilePointer(const std::string& name,
+ int flags = O_RDONLY) const;
+
+ CpuSet* parent_;
+ std::string name_;
+ std::string path_;
+ base::unique_fd cpuset_fd_;
+ std::vector<std::unique_ptr<CpuSet>> children_;
+
+ static void SetPrefixEnabled(bool enabled) { prefix_enabled_ = enabled; }
+ static bool prefix_enabled_;
+
+ CpuSet(const CpuSet&) = delete;
+ void operator=(const CpuSet&) = delete;
+};
+
+class CpuSetManager {
+ public:
+ CpuSetManager() {}
+
+ // Creats a CpuSet hierarchy by walking the directory tree starting at
+ // |cpuset_root|. This argument must be the path to the root cpuset for the
+ // system, which is usually /dev/cpuset.
+ void Load(const std::string& cpuset_root);
+
+ // Lookup and return a CpuSet from a cpuset path. Ownership of the pointer
+ // DOES NOT pass to the caller; the pointer remains valid as long as the
+ // CpuSet hierarchy is valid.
+ CpuSet* Lookup(const std::string& path);
+
+ // Returns a vector of all the cpusets found at initializaiton. Ownership of
+ // the pointers to CpuSets DOES NOT pass to the caller; the pointers remain
+ // valid as long as the CpuSet hierarchy is valid.
+ std::vector<CpuSet*> GetCpuSets();
+
+ // Moves all unbound tasks from the root set into the target set. This is used
+ // to shield the system from interference from unbound kernel threads.
+ void MoveUnboundTasks(const std::string& target_set);
+
+ std::string DumpState() const;
+
+ operator bool() const { return root_set_ != nullptr; }
+
+ private:
+ // Creates a CpuSet from a path to a cpuset cgroup directory. Recursively
+ // creates child groups for each directory found under |path|.
+ std::unique_ptr<CpuSet> Create(const std::string& path);
+ std::unique_ptr<CpuSet> Create(base::unique_fd base_fd,
+ const std::string& name, CpuSet* parent);
+
+ std::unique_ptr<CpuSet> root_set_;
+ std::unordered_map<std::string, CpuSet*> path_map_;
+
+ CpuSetManager(const CpuSetManager&) = delete;
+ void operator=(const CpuSetManager&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_CPU_SET_H_
diff --git a/services/vr/performanced/directory_reader.h b/services/vr/performanced/directory_reader.h
new file mode 100644
index 0000000..7d7ecc5
--- /dev/null
+++ b/services/vr/performanced/directory_reader.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
+#define ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dvr {
+
+// Utility class around readdir() that handles automatic cleanup.
+class DirectoryReader {
+ public:
+ explicit DirectoryReader(base::unique_fd directory_fd) {
+ directory_ = fdopendir(directory_fd.get());
+ error_ = errno;
+ if (directory_ != nullptr)
+ directory_fd.release();
+ }
+
+ ~DirectoryReader() {
+ if (directory_)
+ closedir(directory_);
+ }
+
+ bool IsValid() const { return directory_ != nullptr; }
+ explicit operator bool() const { return IsValid(); }
+ int GetError() const { return error_; }
+
+ // Returns a pointer to a dirent describing the next directory entry. The
+ // pointer is only valid unitl the next call to Next() or the DirectoryReader
+ // is destroyed. Returns nullptr when the end of the directory is reached.
+ dirent* Next() {
+ if (directory_)
+ return readdir(directory_);
+ else
+ return nullptr;
+ }
+
+ private:
+ DIR* directory_;
+ int error_;
+
+ DirectoryReader(const DirectoryReader&) = delete;
+ void operator=(const DirectoryReader&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
diff --git a/services/vr/performanced/main.cpp b/services/vr/performanced/main.cpp
new file mode 100644
index 0000000..114413d
--- /dev/null
+++ b/services/vr/performanced/main.cpp
@@ -0,0 +1,76 @@
+#include <errno.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <sys/resource.h>
+#include <utils/threads.h>
+
+#include <pdx/default_transport/service_dispatcher.h>
+#include <private/android_filesystem_config.h>
+
+#include "performance_service.h"
+
+namespace {
+
+// Annoying that sys/capability.h doesn't define this directly.
+constexpr int kMaxCapNumber = (CAP_TO_INDEX(CAP_LAST_CAP) + 1);
+
+} // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+ int ret = -1;
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[kMaxCapNumber];
+
+ std::shared_ptr<android::pdx::Service> service;
+ std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+ ALOGI("Starting up...");
+
+ // We need to be able to create endpoints with full perms.
+ umask(0000);
+
+ // Keep capabilities when switching UID to AID_SYSTEM.
+ ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+ CHECK_ERROR(ret < 0, error, "Failed to set KEEPCAPS: %s", strerror(errno));
+
+ // Set UID and GID to system.
+ ret = setresgid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM);
+ CHECK_ERROR(ret < 0, error, "Failed to set GID: %s", strerror(errno));
+ ret = setresuid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM);
+ CHECK_ERROR(ret < 0, error, "Failed to set UID: %s", strerror(errno));
+
+ // Keep CAP_SYS_NICE, allowing control of scheduler class, priority, and
+ // cpuset for other tasks in the system.
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capdata[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
+ capdata[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
+
+ // Drop all caps but the ones configured above.
+ ret = capset(&capheader, capdata);
+ CHECK_ERROR(ret < 0, error, "Could not set capabilities: %s",
+ strerror(errno));
+
+ dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+ CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher.");
+
+ service = android::dvr::PerformanceService::Create();
+ CHECK_ERROR(!service, error, "Failed to create performance service service.");
+ dispatcher->AddService(service);
+
+ ALOGI("Entering message loop.");
+
+ ret = dispatcher->EnterDispatchLoop();
+ CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+ strerror(-ret));
+
+error:
+ return ret;
+}
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
new file mode 100644
index 0000000..c99c8d4
--- /dev/null
+++ b/services/vr/performanced/performance_service.cpp
@@ -0,0 +1,196 @@
+#include "performance_service.h"
+
+#include <sched.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/remote_method.h>
+#include <private/dvr/performance_rpc.h>
+
+#include "task.h"
+
+// This prctl is only available in Android kernels.
+#define PR_SET_TIMERSLACK_PID 41
+
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::default_transport::Endpoint;
+
+namespace {
+
+const char kCpuSetBasePath[] = "/dev/cpuset";
+
+constexpr unsigned long kTimerSlackForegroundNs = 50000;
+constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+PerformanceService::PerformanceService()
+ : BASE("PerformanceService",
+ Endpoint::Create(PerformanceRPC::kClientPath)) {
+ cpuset_.Load(kCpuSetBasePath);
+
+ Task task(getpid());
+ ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(),
+ task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]);
+
+ // Errors here are checked in IsInitialized().
+ sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO);
+ sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO);
+
+ const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_;
+ const int fifo_low = sched_fifo_min_priority_;
+ const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
+
+ // TODO(eieio): Make this configurable on the command line.
+ cpuset_.MoveUnboundTasks("/kernel");
+
+ // Setup the scheduler classes.
+ scheduler_classes_ = {
+ {"audio:low",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium}},
+ {"audio:high",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium + 3}},
+ {"graphics",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium}},
+ {"graphics:low",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium}},
+ {"graphics:high",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium + 2}},
+ {"sensors",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_low}},
+ {"sensors:low",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_low}},
+ {"sensors:high",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_low + 1}},
+ {"normal",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_NORMAL,
+ .priority = 0}},
+ {"foreground",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_NORMAL,
+ .priority = 0}},
+ {"background",
+ {.timer_slack = kTimerSlackBackgroundNs,
+ .scheduler_policy = SCHED_BATCH,
+ .priority = 0}},
+ {"batch",
+ {.timer_slack = kTimerSlackBackgroundNs,
+ .scheduler_policy = SCHED_BATCH,
+ .priority = 0}},
+ };
+}
+
+bool PerformanceService::IsInitialized() const {
+ return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
+ sched_fifo_max_priority_ >= 0;
+}
+
+std::string PerformanceService::DumpState(size_t /*max_length*/) {
+ return cpuset_.DumpState();
+}
+
+int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id,
+ const std::string& partition) {
+ Task task(task_id);
+ if (!task || task.thread_group_id() != message.GetProcessId())
+ return -EINVAL;
+
+ auto target_set = cpuset_.Lookup(partition);
+ if (!target_set)
+ return -ENOENT;
+
+ const auto attach_error = target_set->AttachTask(task_id);
+ if (attach_error)
+ return attach_error;
+
+ return 0;
+}
+
+int PerformanceService::OnSetSchedulerClass(
+ Message& message, pid_t task_id, const std::string& scheduler_class) {
+ // Make sure the task id is valid and belongs to the sending process.
+ Task task(task_id);
+ if (!task || task.thread_group_id() != message.GetProcessId())
+ return -EINVAL;
+
+ struct sched_param param;
+
+ // TODO(eieio): Apply rules based on the requesting process. Applications are
+ // only allowed one audio thread that runs at SCHED_FIFO. System services can
+ // have more than one.
+ auto search = scheduler_classes_.find(scheduler_class);
+ if (search != scheduler_classes_.end()) {
+ auto config = search->second;
+ param.sched_priority = config.priority;
+ sched_setscheduler(task_id, config.scheduler_policy, ¶m);
+ prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
+ ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
+ task_id, scheduler_class.c_str());
+ return 0;
+ } else {
+ ALOGE(
+ "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
+ "by task=%d.",
+ scheduler_class.c_str(), task_id);
+ return -EINVAL;
+ }
+}
+
+std::string PerformanceService::OnGetCpuPartition(Message& message,
+ pid_t task_id) {
+ // Make sure the task id is valid and belongs to the sending process.
+ Task task(task_id);
+ if (!task || task.thread_group_id() != message.GetProcessId())
+ REPLY_ERROR_RETURN(message, EINVAL, "");
+
+ return task.GetCpuSetPath();
+}
+
+int PerformanceService::HandleMessage(Message& message) {
+ switch (message.GetOp()) {
+ case PerformanceRPC::SetCpuPartition::Opcode:
+ DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+ *this, &PerformanceService::OnSetCpuPartition, message);
+ return 0;
+
+ case PerformanceRPC::SetSchedulerClass::Opcode:
+ DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+ *this, &PerformanceService::OnSetSchedulerClass, message);
+ return 0;
+
+ case PerformanceRPC::GetCpuPartition::Opcode:
+ DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
+ *this, &PerformanceService::OnGetCpuPartition, message);
+ return 0;
+
+ default:
+ return Service::HandleMessage(message);
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
new file mode 100644
index 0000000..e32d834
--- /dev/null
+++ b/services/vr/performanced/performance_service.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
+#define ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
+
+#include <string>
+#include <unordered_map>
+
+#include <pdx/service.h>
+
+#include "cpu_set.h"
+
+namespace android {
+namespace dvr {
+
+// PerformanceService manages compute partitions usings cpusets. Different
+// cpusets are assigned specific purposes and performance characteristics;
+// clients may request for threads to be moved into these cpusets to help
+// achieve system performance goals.
+class PerformanceService : public pdx::ServiceBase<PerformanceService> {
+ public:
+ int HandleMessage(pdx::Message& message) override;
+ bool IsInitialized() const override;
+
+ std::string DumpState(size_t max_length) override;
+
+ private:
+ friend BASE;
+
+ PerformanceService();
+
+ int OnSetCpuPartition(pdx::Message& message, pid_t task_id,
+ const std::string& partition);
+ int OnSetSchedulerClass(pdx::Message& message, pid_t task_id,
+ const std::string& scheduler_class);
+ std::string OnGetCpuPartition(pdx::Message& message, pid_t task_id);
+
+ CpuSetManager cpuset_;
+
+ int sched_fifo_min_priority_;
+ int sched_fifo_max_priority_;
+
+ // Scheduler class config type.
+ struct SchedulerClassConfig {
+ unsigned long timer_slack;
+ int scheduler_policy;
+ int priority;
+ };
+
+ std::unordered_map<std::string, SchedulerClassConfig> scheduler_classes_;
+
+ PerformanceService(const PerformanceService&) = delete;
+ void operator=(const PerformanceService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
diff --git a/services/vr/performanced/performance_service_tests.cpp b/services/vr/performanced/performance_service_tests.cpp
new file mode 100644
index 0000000..b526082
--- /dev/null
+++ b/services/vr/performanced/performance_service_tests.cpp
@@ -0,0 +1,137 @@
+#include <errno.h>
+#include <sched.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <dvr/performance_client_api.h>
+#include <gtest/gtest.h>
+
+TEST(DISABLED_PerformanceTest, SetCpuPartition) {
+ int error;
+
+ // Test setting the the partition for the current task.
+ error = dvrSetCpuPartition(0, "/application/background");
+ EXPECT_EQ(0, error);
+
+ error = dvrSetCpuPartition(0, "/application/performance");
+ EXPECT_EQ(0, error);
+
+ // Test setting the partition for one of our tasks.
+ bool done = false;
+ pid_t task_id = 0;
+ std::mutex mutex;
+ std::condition_variable done_condition, id_condition;
+
+ std::thread thread([&] {
+ std::unique_lock<std::mutex> lock(mutex);
+
+ task_id = gettid();
+ id_condition.notify_one();
+
+ done_condition.wait(lock, [&done] { return done; });
+ });
+
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ id_condition.wait(lock, [&task_id] { return task_id != 0; });
+ }
+ EXPECT_NE(0, task_id);
+
+ error = dvrSetCpuPartition(task_id, "/application");
+ EXPECT_EQ(0, error);
+
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ done = true;
+ done_condition.notify_one();
+ }
+ thread.join();
+
+ // Test setting the partition for a task that isn't valid using
+ // the task id of the thread that we just joined. Technically the
+ // id could wrap around by the time we get here, but this is
+ // extremely unlikely.
+ error = dvrSetCpuPartition(task_id, "/application");
+ EXPECT_EQ(-EINVAL, error);
+
+ // Test setting the partition for a task that doesn't belong to us.
+ error = dvrSetCpuPartition(1, "/application");
+ EXPECT_EQ(-EINVAL, error);
+
+ // Test setting the partition to one that doesn't exist.
+ error = dvrSetCpuPartition(0, "/foobar");
+ EXPECT_EQ(-ENOENT, error);
+}
+
+TEST(PerformanceTest, SetSchedulerClass) {
+ int error;
+
+ // TODO(eieio): Test all supported scheduler classes and priority levels.
+
+ error = dvrSetSchedulerClass(0, "background");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_BATCH, sched_getscheduler(0));
+
+ error = dvrSetSchedulerClass(0, "audio:low");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));
+
+ error = dvrSetSchedulerClass(0, "normal");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_NORMAL, sched_getscheduler(0));
+
+ error = dvrSetSchedulerClass(0, "foobar");
+ EXPECT_EQ(-EINVAL, error);
+}
+
+TEST(PerformanceTest, SchedulerClassResetOnFork) {
+ int error;
+
+ error = dvrSetSchedulerClass(0, "graphics:high");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));
+
+ int scheduler = -1;
+ std::thread thread([&]() { scheduler = sched_getscheduler(0); });
+ thread.join();
+
+ EXPECT_EQ(SCHED_NORMAL, scheduler);
+
+ // Return to SCHED_NORMAL.
+ error = dvrSetSchedulerClass(0, "normal");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_NORMAL, sched_getscheduler(0));
+}
+
+TEST(PerformanceTest, GetCpuPartition) {
+ int error;
+ char partition[PATH_MAX + 1];
+
+ error = dvrSetCpuPartition(0, "/");
+ ASSERT_EQ(0, error);
+
+ error = dvrGetCpuPartition(0, partition, sizeof(partition));
+ EXPECT_EQ(0, error);
+ EXPECT_EQ("/", std::string(partition));
+
+ error = dvrSetCpuPartition(0, "/application");
+ EXPECT_EQ(0, error);
+
+ error = dvrGetCpuPartition(0, partition, sizeof(partition));
+ EXPECT_EQ(0, error);
+ EXPECT_EQ("/application", std::string(partition));
+
+ // Test passing a buffer that is too short.
+ error = dvrGetCpuPartition(0, partition, 5);
+ EXPECT_EQ(-ENOBUFS, error);
+
+ // Test getting the partition for a task that doesn't belong to us.
+ error = dvrGetCpuPartition(1, partition, sizeof(partition));
+ EXPECT_EQ(-EINVAL, error);
+
+ // Test passing a nullptr value for partition buffer.
+ error = dvrGetCpuPartition(0, nullptr, sizeof(partition));
+ EXPECT_EQ(-EINVAL, error);
+}
diff --git a/services/vr/performanced/performanced.rc b/services/vr/performanced/performanced.rc
new file mode 100644
index 0000000..754c97f
--- /dev/null
+++ b/services/vr/performanced/performanced.rc
@@ -0,0 +1,5 @@
+service performanced /system/bin/performanced
+ class core
+ user root
+ group system readproc
+ cpuset /
diff --git a/services/vr/performanced/stdio_filebuf.h b/services/vr/performanced/stdio_filebuf.h
new file mode 100644
index 0000000..5988aa8
--- /dev/null
+++ b/services/vr/performanced/stdio_filebuf.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
+// Copyright (c) 2016 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
+#define ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
+
+#include <cstdio>
+#include <istream>
+#include <locale>
+#include <streambuf>
+
+namespace android {
+namespace dvr {
+
+// An implementation of std::basic_streambuf backed by a FILE pointer. This is
+// ported from the internal llvm-libc++ support for std::cin. It's really
+// unfortunate that we have to do this, but the C++11 standard is too pendantic
+// to support creating streams from file descriptors or FILE pointers. This
+// implementation uses all standard interfaces, except for the call to
+// std::__throw_runtime_error(), which is only needed to deal with exceeding
+// locale encoding limits. This class is meant to be used for reading system
+// files, which don't require exotic locale support, so this call could be
+// removed in the future, if necessary.
+//
+// Original source file: llvm-libcxx/llvm-libc++/include/__std_stream
+// Original class name: __stdinbuf
+//
+template <class _CharT>
+class stdio_filebuf
+ : public std::basic_streambuf<_CharT, std::char_traits<_CharT> > {
+ public:
+ typedef _CharT char_type;
+ typedef std::char_traits<char_type> traits_type;
+ typedef typename traits_type::int_type int_type;
+ typedef typename traits_type::pos_type pos_type;
+ typedef typename traits_type::off_type off_type;
+ typedef typename traits_type::state_type state_type;
+
+ explicit stdio_filebuf(FILE* __fp);
+ ~stdio_filebuf() override;
+
+ protected:
+ virtual int_type underflow() override;
+ virtual int_type uflow() override;
+ virtual int_type pbackfail(int_type __c = traits_type::eof()) override;
+ virtual void imbue(const std::locale& __loc) override;
+
+ private:
+ FILE* __file_;
+ const std::codecvt<char_type, char, state_type>* __cv_;
+ state_type __st_;
+ int __encoding_;
+ int_type __last_consumed_;
+ bool __last_consumed_is_next_;
+ bool __always_noconv_;
+
+ stdio_filebuf(const stdio_filebuf&);
+ stdio_filebuf& operator=(const stdio_filebuf&);
+
+ int_type __getchar(bool __consume);
+
+ static const int __limit = 8;
+};
+
+template <class _CharT>
+stdio_filebuf<_CharT>::stdio_filebuf(FILE* __fp)
+ : __file_(__fp),
+ __last_consumed_(traits_type::eof()),
+ __last_consumed_is_next_(false) {
+ imbue(this->getloc());
+}
+
+template <class _CharT>
+stdio_filebuf<_CharT>::~stdio_filebuf() {
+ if (__file_)
+ fclose(__file_);
+}
+
+template <class _CharT>
+void stdio_filebuf<_CharT>::imbue(const std::locale& __loc) {
+ __cv_ = &std::use_facet<std::codecvt<char_type, char, state_type> >(__loc);
+ __encoding_ = __cv_->encoding();
+ __always_noconv_ = __cv_->always_noconv();
+ if (__encoding_ > __limit)
+ std::__throw_runtime_error("unsupported locale for standard io");
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::underflow() {
+ return __getchar(false);
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::uflow() {
+ return __getchar(true);
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::__getchar(
+ bool __consume) {
+ if (__last_consumed_is_next_) {
+ int_type __result = __last_consumed_;
+ if (__consume) {
+ __last_consumed_ = traits_type::eof();
+ __last_consumed_is_next_ = false;
+ }
+ return __result;
+ }
+ char __extbuf[__limit];
+ int __nread = std::max(1, __encoding_);
+ for (int __i = 0; __i < __nread; ++__i) {
+ int __c = getc(__file_);
+ if (__c == EOF)
+ return traits_type::eof();
+ __extbuf[__i] = static_cast<char>(__c);
+ }
+ char_type __1buf;
+ if (__always_noconv_)
+ __1buf = static_cast<char_type>(__extbuf[0]);
+ else {
+ const char* __enxt;
+ char_type* __inxt;
+ std::codecvt_base::result __r;
+ do {
+ state_type __sv_st = __st_;
+ __r = __cv_->in(__st_, __extbuf, __extbuf + __nread, __enxt, &__1buf,
+ &__1buf + 1, __inxt);
+ switch (__r) {
+ case std::codecvt_base::ok:
+ break;
+ case std::codecvt_base::partial:
+ __st_ = __sv_st;
+ if (__nread == sizeof(__extbuf))
+ return traits_type::eof();
+ {
+ int __c = getc(__file_);
+ if (__c == EOF)
+ return traits_type::eof();
+ __extbuf[__nread] = static_cast<char>(__c);
+ }
+ ++__nread;
+ break;
+ case std::codecvt_base::error:
+ return traits_type::eof();
+ case std::codecvt_base::noconv:
+ __1buf = static_cast<char_type>(__extbuf[0]);
+ break;
+ }
+ } while (__r == std::codecvt_base::partial);
+ }
+ if (!__consume) {
+ for (int __i = __nread; __i > 0;) {
+ if (ungetc(traits_type::to_int_type(__extbuf[--__i]), __file_) == EOF)
+ return traits_type::eof();
+ }
+ } else
+ __last_consumed_ = traits_type::to_int_type(__1buf);
+ return traits_type::to_int_type(__1buf);
+}
+
+template <class _CharT>
+typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::pbackfail(
+ int_type __c) {
+ if (traits_type::eq_int_type(__c, traits_type::eof())) {
+ if (!__last_consumed_is_next_) {
+ __c = __last_consumed_;
+ __last_consumed_is_next_ =
+ !traits_type::eq_int_type(__last_consumed_, traits_type::eof());
+ }
+ return __c;
+ }
+ if (__last_consumed_is_next_) {
+ char __extbuf[__limit];
+ char* __enxt;
+ const char_type __ci = traits_type::to_char_type(__last_consumed_);
+ const char_type* __inxt;
+ switch (__cv_->out(__st_, &__ci, &__ci + 1, __inxt, __extbuf,
+ __extbuf + sizeof(__extbuf), __enxt)) {
+ case std::codecvt_base::ok:
+ break;
+ case std::codecvt_base::noconv:
+ __extbuf[0] = static_cast<char>(__last_consumed_);
+ __enxt = __extbuf + 1;
+ break;
+ case std::codecvt_base::partial:
+ case std::codecvt_base::error:
+ return traits_type::eof();
+ }
+ while (__enxt > __extbuf)
+ if (ungetc(*--__enxt, __file_) == EOF)
+ return traits_type::eof();
+ }
+ __last_consumed_ = __c;
+ __last_consumed_is_next_ = true;
+ return __c;
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
diff --git a/services/vr/performanced/string_trim.h b/services/vr/performanced/string_trim.h
new file mode 100644
index 0000000..7094e9f
--- /dev/null
+++ b/services/vr/performanced/string_trim.h
@@ -0,0 +1,46 @@
+#ifndef ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
+#define ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
+
+#include <functional>
+#include <locale>
+#include <string>
+
+namespace android {
+namespace dvr {
+
+// Trims whitespace from the left side of |subject| and returns the result as a
+// new string.
+inline std::string LeftTrim(std::string subject) {
+ subject.erase(subject.begin(),
+ std::find_if(subject.begin(), subject.end(),
+ std::not1(std::ptr_fun<int, int>(std::isspace))));
+ return subject;
+}
+
+// Trims whitespace from the right side of |subject| and returns the result as a
+// new string.
+inline std::string RightTrim(std::string subject) {
+ subject.erase(std::find_if(subject.rbegin(), subject.rend(),
+ std::not1(std::ptr_fun<int, int>(std::isspace)))
+ .base(),
+ subject.end());
+ return subject;
+}
+
+// Trims whitespace from the both sides of |subject| and returns the result as a
+// new string.
+inline std::string Trim(std::string subject) {
+ subject.erase(subject.begin(),
+ std::find_if(subject.begin(), subject.end(),
+ std::not1(std::ptr_fun<int, int>(std::isspace))));
+ subject.erase(std::find_if(subject.rbegin(), subject.rend(),
+ std::not1(std::ptr_fun<int, int>(std::isspace)))
+ .base(),
+ subject.end());
+ return subject;
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
diff --git a/services/vr/performanced/task.cpp b/services/vr/performanced/task.cpp
new file mode 100644
index 0000000..ad12858
--- /dev/null
+++ b/services/vr/performanced/task.cpp
@@ -0,0 +1,163 @@
+#include "task.h"
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <cctype>
+#include <cstdlib>
+#include <memory>
+#include <sstream>
+
+#include <android-base/unique_fd.h>
+
+#include "stdio_filebuf.h"
+#include "string_trim.h"
+
+namespace {
+
+const char kProcBase[] = "/proc";
+
+android::base::unique_fd OpenTaskDirectory(pid_t task_id) {
+ std::ostringstream stream;
+ stream << kProcBase << "/" << task_id;
+
+ return android::base::unique_fd(
+ open(stream.str().c_str(), O_RDONLY | O_DIRECTORY));
+}
+
+void ParseUidStatusField(const std::string& value, std::array<int, 4>& ids) {
+ const char* start = value.c_str();
+
+ ids[0] = std::strtol(start, const_cast<char**>(&start), 10);
+ ids[1] = std::strtol(start, const_cast<char**>(&start), 10);
+ ids[2] = std::strtol(start, const_cast<char**>(&start), 10);
+ ids[3] = std::strtol(start, const_cast<char**>(&start), 10);
+}
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+Task::Task(pid_t task_id)
+ : task_id_(task_id),
+ thread_group_id_(-1),
+ parent_process_id_(-1),
+ thread_count_(0),
+ cpus_allowed_mask_(0) {
+ task_fd_ = OpenTaskDirectory(task_id_);
+ ALOGE_IF(task_fd_.get() < 0,
+ "Task::Task: Failed to open task directory for task_id=%d: %s",
+ task_id, strerror(errno));
+
+ ReadStatusFields();
+
+ ALOGD_IF(TRACE, "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x",
+ task_id_, name_.c_str(), thread_group_id_, parent_process_id_,
+ cpus_allowed_mask_);
+}
+
+base::unique_fd Task::OpenTaskFile(const std::string& name) const {
+ const std::string relative_path = "./" + name;
+ return base::unique_fd(
+ openat(task_fd_.get(), relative_path.c_str(), O_RDONLY));
+}
+
+UniqueFile Task::OpenTaskFilePointer(const std::string& name) const {
+ const std::string relative_path = "./" + name;
+ base::unique_fd fd(openat(task_fd_.get(), relative_path.c_str(), O_RDONLY));
+ if (fd.get() < 0) {
+ ALOGE("Task::OpenTaskFilePointer: Failed to open /proc/%d/%s: %s", task_id_,
+ name.c_str(), strerror(errno));
+ return nullptr;
+ }
+
+ UniqueFile fp(fdopen(fd.release(), "r"));
+ if (!fp)
+ ALOGE("Task::OpenTaskFilePointer: Failed to fdopen /proc/%d/%s: %s",
+ task_id_, name.c_str(), strerror(errno));
+
+ return fp;
+}
+
+std::string Task::GetStatusField(const std::string& field) const {
+ if (auto file = OpenTaskFilePointer("status")) {
+ stdio_filebuf<char> filebuf(file.get());
+ std::istream file_stream(&filebuf);
+
+ for (std::string line; std::getline(file_stream, line);) {
+ auto offset = line.find(field);
+
+ ALOGD_IF(TRACE,
+ "Task::GetStatusField: field=\"%s\" line=\"%s\" offset=%zd",
+ field.c_str(), line.c_str(), offset);
+
+ if (offset == std::string::npos)
+ continue;
+
+ // The status file has lines with the format <field>:<value>. Extract the
+ // value after the colon.
+ return Trim(line.substr(offset + field.size() + 1));
+ }
+ }
+
+ return "[unknown]";
+}
+
+void Task::ReadStatusFields() {
+ if (auto file = OpenTaskFilePointer("status")) {
+ stdio_filebuf<char> filebuf(file.get());
+ std::istream file_stream(&filebuf);
+
+ for (std::string line; std::getline(file_stream, line);) {
+ auto offset = line.find(":");
+ if (offset == std::string::npos) {
+ ALOGW("ReadStatusFields: Failed to find delimiter \":\" in line=\"%s\"",
+ line.c_str());
+ continue;
+ }
+
+ std::string key = line.substr(0, offset);
+ std::string value = Trim(line.substr(offset + 1));
+
+ ALOGD_IF(TRACE, "Task::ReadStatusFields: key=\"%s\" value=\"%s\"",
+ key.c_str(), value.c_str());
+
+ if (key == "Name")
+ name_ = value;
+ else if (key == "Tgid")
+ thread_group_id_ = std::strtol(value.c_str(), nullptr, 10);
+ else if (key == "PPid")
+ parent_process_id_ = std::strtol(value.c_str(), nullptr, 10);
+ else if (key == "Uid")
+ ParseUidStatusField(value, user_id_);
+ else if (key == "Gid")
+ ParseUidStatusField(value, group_id_);
+ else if (key == "Threads")
+ thread_count_ = std::strtoul(value.c_str(), nullptr, 10);
+ else if (key == "Cpus_allowed")
+ cpus_allowed_mask_ = std::strtoul(value.c_str(), nullptr, 16);
+ else if (key == "Cpus_allowed_list")
+ cpus_allowed_list_ = value;
+ }
+ }
+}
+
+std::string Task::GetCpuSetPath() const {
+ if (auto file = OpenTaskFilePointer("cpuset")) {
+ stdio_filebuf<char> filebuf(file.get());
+ std::istream file_stream(&filebuf);
+
+ std::string line = "";
+ std::getline(file_stream, line);
+
+ return Trim(line);
+ } else {
+ return "";
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/performanced/task.h b/services/vr/performanced/task.h
new file mode 100644
index 0000000..4a3b7f2
--- /dev/null
+++ b/services/vr/performanced/task.h
@@ -0,0 +1,83 @@
+#ifndef ANDROID_DVR_PERFORMANCED_TASK_H_
+#define ANDROID_DVR_PERFORMANCED_TASK_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "unique_file.h"
+
+namespace android {
+namespace dvr {
+
+// Task provides access to task-related information from the procfs
+// pseudo-filesystem.
+class Task {
+ public:
+ explicit Task(pid_t task_id);
+
+ bool IsValid() const { return task_fd_.get() >= 0; }
+ explicit operator bool() const { return IsValid(); }
+
+ pid_t task_id() const { return task_id_; }
+ std::string name() const { return name_; }
+ pid_t thread_group_id() const { return thread_group_id_; }
+ pid_t parent_process_id() const { return parent_process_id_; }
+ size_t thread_count() const { return thread_count_; }
+ uint32_t cpus_allowed_mask() const { return cpus_allowed_mask_; }
+ const std::string& cpus_allowed_list() const { return cpus_allowed_list_; }
+ const std::array<int, 4>& user_id() const { return user_id_; }
+ const std::array<int, 4>& group_id() const { return group_id_; }
+
+ // Indices into user and group id arrays.
+ enum {
+ kUidReal = 0,
+ kUidEffective,
+ kUidSavedSet,
+ kUidFilesystem,
+ };
+
+ std::string GetCpuSetPath() const;
+
+ private:
+ pid_t task_id_;
+ base::unique_fd task_fd_;
+
+ // Fields read from /proc/<task_id_>/status.
+ std::string name_;
+ pid_t thread_group_id_;
+ pid_t parent_process_id_;
+ std::array<int, 4> user_id_;
+ std::array<int, 4> group_id_;
+ size_t thread_count_;
+ uint32_t cpus_allowed_mask_;
+ std::string cpus_allowed_list_;
+
+ // Opens the file /proc/<task_id_>/|name| and returns the open file
+ // descriptor.
+ base::unique_fd OpenTaskFile(const std::string& name) const;
+
+ // Similar to OpenTaskFile() but returns a file pointer.
+ UniqueFile OpenTaskFilePointer(const std::string& name) const;
+
+ // Reads the field named |field| from /proc/<task_id_>/status.
+ std::string GetStatusField(const std::string& field) const;
+
+ // Reads a subset of the fields in /proc/<task_id_>/status.
+ void ReadStatusFields();
+
+ Task(const Task&) = delete;
+ void operator=(const Task&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_TASK_H_
diff --git a/services/vr/performanced/unique_file.h b/services/vr/performanced/unique_file.h
new file mode 100644
index 0000000..86e487a
--- /dev/null
+++ b/services/vr/performanced/unique_file.h
@@ -0,0 +1,20 @@
+#ifndef ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
+#define ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
+
+#include <stdio.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+// Utility to manage the lifetime of a file pointer.
+struct FileDeleter {
+ void operator()(FILE* fp) { fclose(fp); }
+};
+using UniqueFile = std::unique_ptr<FILE, FileDeleter>;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
diff --git a/services/vr/sensord/Android.mk b/services/vr/sensord/Android.mk
new file mode 100644
index 0000000..907c3d6
--- /dev/null
+++ b/services/vr/sensord/Android.mk
@@ -0,0 +1,81 @@
+# Copyright (C) 2008 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 := \
+ pose_service.cpp \
+ sensord.cpp \
+ sensor_fusion.cpp \
+ sensor_hal_thread.cpp \
+ sensor_ndk_thread.cpp \
+ sensor_service.cpp \
+ sensor_thread.cpp \
+
+includeFiles += \
+ $(LOCAL_PATH)/include
+
+staticLibraries := \
+ libdvrcommon \
+ libsensor \
+ libperformance \
+ libbufferhub \
+ libpdx_default_transport \
+ libchrome \
+ libposepredictor \
+
+sharedLibraries := \
+ libandroid \
+ libbase \
+ libbinder \
+ libcutils \
+ liblog \
+ libhardware \
+ libutils \
+
+cFlags := -DLOG_TAG=\"sensord\" \
+ -DTRACE=0
+
+ifeq ($(TARGET_USES_QCOM_BSP), true)
+ifneq ($(TARGET_QCOM_DISPLAY_VARIANT),)
+ platform := .
+else
+ platform := $(TARGET_BOARD_PLATFORM)
+endif
+ cFlags += -DQCOM_B_FAMILY \
+ -DQCOM_BSP
+endif
+
+include $(CLEAR_VARS)
+# Don't strip symbols so we see stack traces in logcat.
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := $(sourceFiles)
+PLATFORM := $(platform)
+LOCAL_CFLAGS := $(cFlags)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := sensord
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_C_INCLUDES += \
+ $(call local-generated-sources-dir)/proto/frameworks/native/services/vr/sensord
+LOCAL_INIT_RC := sensord.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_SRC_FILES := test/poselatencytest.cpp
+LOCAL_MODULE := poselatencytest
+include $(BUILD_EXECUTABLE)
diff --git a/services/vr/sensord/pose_service.cpp b/services/vr/sensord/pose_service.cpp
new file mode 100644
index 0000000..c2863ee
--- /dev/null
+++ b/services/vr/sensord/pose_service.cpp
@@ -0,0 +1,677 @@
+#define ATRACE_TAG ATRACE_TAG_INPUT
+#include "pose_service.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <time.h>
+
+#include <array>
+#include <cmath>
+#include <cstdint>
+#include <sstream>
+#include <type_traits>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <hardware/sensors.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/benchmark.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/platform_defines.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/sensor_constants.h>
+#include <utils/Trace.h>
+
+#define arraysize(x) (static_cast<ssize_t>(std::extent<decltype(x)>::value))
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+using Vector3d = vec3d;
+using Vector3f = vec3f;
+using Rotationd = quatd;
+using Rotationf = quatf;
+using AngleAxisd = Eigen::AngleAxis<double>;
+using AngleAxisf = Eigen::AngleAxis<float>;
+
+namespace {
+// Wait a few seconds before checking if we need to disable sensors.
+static constexpr int64_t kSensorTimeoutNs = 5000000000ll;
+
+static constexpr float kTwoPi = 2.0 * M_PI;
+static constexpr float kDegToRad = M_PI / 180.f;
+
+// Head model code data.
+static constexpr float kDefaultNeckHorizontalOffset = 0.080f; // meters
+static constexpr float kDefaultNeckVerticalOffset = 0.075f; // meters
+
+static constexpr char kDisablePosePredictionProp[] =
+ "persist.dreamos.disable_predict";
+
+// Device type property for controlling classes of behavior that differ
+// between devices. If unset, defaults to kOrientationTypeSmartphone.
+static constexpr char kOrientationTypeProp[] = "dvr.orientation_type";
+
+static constexpr char kEnableSensorRecordProp[] = "dvr.enable_6dof_recording";
+static constexpr char kEnableSensorPlayProp[] = "dvr.enable_6dof_playback";
+static constexpr char kEnableSensorPlayIdProp[] = "dvr.6dof_playback_id";
+static constexpr char kEnablePoseRecordProp[] = "dvr.enable_pose_recording";
+
+// Persistent buffer names.
+static constexpr char kPoseRingBufferName[] = "PoseService:RingBuffer";
+
+static constexpr int kDatasetIdLength = 36;
+static constexpr char kDatasetIdChars[] = "0123456789abcdef-";
+static constexpr char kDatasetLocation[] = "/data/sdcard/datasets/";
+
+// These are the flags used by BufferProducer::CreatePersistentUncachedBlob,
+// plus PRIVATE_ADSP_HEAP to allow access from the DSP.
+static constexpr int kPoseRingBufferFlags =
+ GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY |
+ GRALLOC_USAGE_PRIVATE_UNCACHED | GRALLOC_USAGE_PRIVATE_ADSP_HEAP;
+
+// Extract yaw angle from a given quaternion rotation.
+// Y-axis is considered to be vertical. Result is in rad.
+template <typename T>
+T ExtractYaw(Eigen::Quaternion<T> rotation) {
+ const Eigen::Vector3<T> yaw_axis = rotation * vec3::UnitZ();
+ return std::atan2(yaw_axis.z(), yaw_axis.x());
+}
+
+std::string GetPoseModeString(DvrPoseMode mode) {
+ switch (mode) {
+ case DVR_POSE_MODE_6DOF:
+ return "DVR_POSE_MODE_6DOF";
+ case DVR_POSE_MODE_3DOF:
+ return "DVR_POSE_MODE_3DOF";
+ case DVR_POSE_MODE_MOCK_FROZEN:
+ return "DVR_POSE_MODE_MOCK_FROZEN";
+ case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+ return "DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW";
+ case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+ return "DVR_POSE_MODE_MOCK_HEAD_TURN_FAST";
+ case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+ return "DVR_POSE_MODE_MOCK_ROTATE_SLOW";
+ case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+ return "DVR_POSE_MODE_MOCK_ROTATE_MEDIUM";
+ case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+ return "DVR_POSE_MODE_MOCK_ROTATE_FAST";
+ case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE:
+ return "DVR_POSE_MODE_MOCK_CIRCLE_STRAFE";
+ default:
+ return "Unknown pose mode";
+ }
+}
+
+inline std::string GetVector3dString(const Vector3d& vector) {
+ std::ostringstream stream;
+ stream << "[" << vector[0] << "," << vector[1] << "," << vector[2] << "]";
+ return stream.str();
+}
+
+inline std::string GetRotationdString(const Rotationd& rotation) {
+ std::ostringstream stream;
+ stream << "[" << rotation.w() << ", " << GetVector3dString(rotation.vec())
+ << "]";
+ return stream.str();
+}
+
+} // namespace
+
+PoseService::PoseService(SensorThread* sensor_thread)
+ : BASE("PoseService", Endpoint::Create(DVR_POSE_SERVICE_CLIENT)),
+ sensor_thread_(sensor_thread),
+ last_sensor_usage_time_ns_(0),
+ watchdog_shutdown_(false),
+ sensors_on_(false),
+ accelerometer_index_(-1),
+ gyroscope_index_(-1),
+ pose_mode_(DVR_POSE_MODE_6DOF),
+ mapped_pose_buffer_(nullptr),
+ vsync_count_(0),
+ photon_timestamp_(0),
+ // Will be updated by external service, but start with a non-zero value:
+ display_period_ns_(16000000) {
+ last_known_pose_ = {
+ .orientation = {1.0f, 0.0f, 0.0f, 0.0f},
+ .translation = {0.0f, 0.0f, 0.0f, 0.0f},
+ .angular_velocity = {0.0f, 0.0f, 0.0f, 0.0f},
+ .velocity = {0.0f, 0.0f, 0.0f, 0.0f},
+ .timestamp_ns = 0,
+ .flags = DVR_POSE_FLAG_HEAD,
+ .pad = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ };
+
+ switch (property_get_int32(kOrientationTypeProp, kOrientationTypePortrait)) {
+ case kOrientationTypeLandscape:
+ device_orientation_type_ = kOrientationTypeLandscape;
+ break;
+ default:
+ device_orientation_type_ = kOrientationTypePortrait;
+ break;
+ }
+
+ ring_buffer_ =
+ BufferProducer::Create(kPoseRingBufferName, 0, 0, kPoseRingBufferFlags,
+ sizeof(DvrPoseRingBuffer));
+ if (!ring_buffer_) {
+ ALOGE("PoseService::PoseService: Failed to create/get pose ring buffer!");
+ return;
+ }
+
+ void* addr = nullptr;
+ int ret =
+ ring_buffer_->GetBlobReadWritePointer(sizeof(DvrPoseRingBuffer), &addr);
+ if (ret < 0) {
+ ALOGE("PoseService::PoseService: Failed to map pose ring buffer: %s",
+ strerror(-ret));
+ return;
+ }
+ memset(addr, 0, sizeof(DvrPoseRingBuffer));
+ mapped_pose_buffer_ = static_cast<DvrPoseRingBuffer*>(addr);
+ addr = nullptr;
+
+ for (int i = 0; i < sensor_thread->GetSensorCount(); ++i) {
+ if (sensor_thread->GetSensorType(i) == SENSOR_TYPE_ACCELEROMETER)
+ accelerometer_index_ = i;
+ if (sensor_thread->GetSensorType(i) == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED)
+ gyroscope_index_ = i;
+ }
+ // If we failed to find the uncalibrated gyroscope, use the regular one.
+ if (gyroscope_index_ < 0) {
+ ALOGW("PoseService was unable to find uncalibrated gyroscope");
+ for (int i = 0; i < sensor_thread->GetSensorCount(); ++i) {
+ ALOGI("Type %d", sensor_thread->GetSensorType(i));
+ if (sensor_thread->GetSensorType(i) == SENSOR_TYPE_GYROSCOPE)
+ gyroscope_index_ = i;
+ }
+ }
+
+ if (accelerometer_index_ < 0) {
+ ALOGE("PoseService was unable to find accelerometer");
+ }
+ if (gyroscope_index_ < 0) {
+ ALOGE("PoseService was unable to find gyroscope");
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ KickSensorWatchDogThread();
+ }
+
+ // Read the persistent dreamos flags before using them in SetPoseMode.
+ enable_pose_prediction_ =
+ property_get_bool(kDisablePosePredictionProp, 0) == 0;
+
+ enable_sensor_recording_ = property_get_bool(kEnableSensorRecordProp, 0) == 1;
+
+ enable_sensor_playback_ = property_get_bool(kEnableSensorPlayProp, 0) == 1;
+
+ if (enable_sensor_playback_) {
+ char dataset_id[PROPERTY_VALUE_MAX];
+ property_get(kEnableSensorPlayIdProp, dataset_id, "");
+ sensor_playback_id_ = std::string(dataset_id);
+
+ if (sensor_playback_id_.length() != kDatasetIdLength ||
+ sensor_playback_id_.find_first_not_of(kDatasetIdChars) !=
+ std::string::npos) {
+ ALOGE("Error: invalid playback id %s", sensor_playback_id_.c_str());
+ sensor_playback_id_ = "";
+ enable_sensor_playback_ = false;
+ } else {
+ ALOGI("Playback id %s", sensor_playback_id_.c_str());
+ }
+ }
+
+ enable_pose_recording_ = property_get_bool(kEnablePoseRecordProp, 0) == 1;
+
+ SetPoseMode(DVR_POSE_MODE_6DOF);
+}
+
+PoseService::~PoseService() {
+ if (watchdog_thread_.get_id() != std::thread::id()) {
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ watchdog_shutdown_ = true;
+ watchdog_condition_.notify_one();
+ }
+ watchdog_thread_.join();
+ }
+}
+
+void PoseService::KickSensorWatchDogThread() {
+ // This method is called every frame while rendering so we want to make sure
+ // it is very light weight with synchronization.
+ // TODO(jbates) For better performance, we can consider a lock-free atomic
+ // solution instead of locking this mutex.
+
+ // Update the usage time. The watchdog thread will poll this value to know
+ // when to disable sensors.
+ last_sensor_usage_time_ns_ = GetSystemClockNs();
+
+ // If sensors are still on, there's nothing else to do.
+ if (sensors_on_)
+ return;
+
+ // Enable sensors.
+ ALOGI("Start using sensors.");
+ sensors_on_ = true;
+ if (accelerometer_index_ >= 0) {
+ sensor_thread_->StartUsingSensor(accelerometer_index_);
+ }
+ if (gyroscope_index_ >= 0) {
+ sensor_thread_->StartUsingSensor(gyroscope_index_);
+ }
+
+ // Tell the thread to wake up to disable the sensors when no longer needed.
+ watchdog_condition_.notify_one();
+
+ if (watchdog_thread_.get_id() == std::thread::id()) {
+ // The sensor watchdog thread runs while sensors are in use. When no APIs
+ // have requested sensors beyond a threshold (5 seconds), sensors are
+ // disabled.
+ watchdog_thread_ = std::thread([this] {
+ std::unique_lock<std::mutex> lock(mutex_);
+ while (!watchdog_shutdown_) {
+ int64_t remaining_sensor_time_ns =
+ last_sensor_usage_time_ns_ + kSensorTimeoutNs - GetSystemClockNs();
+
+ if (remaining_sensor_time_ns > 0) {
+ // Wait for the remaining usage time before checking again.
+ watchdog_condition_.wait_for(
+ lock, std::chrono::nanoseconds(remaining_sensor_time_ns));
+ continue;
+ }
+
+ if (sensors_on_) {
+ // Disable sensors.
+ ALOGI("Stop using sensors.");
+ sensors_on_ = false;
+ if (accelerometer_index_ >= 0) {
+ sensor_thread_->StopUsingSensor(accelerometer_index_);
+ }
+ if (gyroscope_index_ >= 0) {
+ sensor_thread_->StopUsingSensor(gyroscope_index_);
+ }
+ }
+
+ // Wait for sensors to be enabled again.
+ watchdog_condition_.wait(lock);
+ }
+ });
+ }
+}
+
+bool PoseService::IsInitialized() const {
+ return BASE::IsInitialized() && ring_buffer_ && mapped_pose_buffer_;
+}
+
+void PoseService::WriteAsyncPoses(const Vector3d& start_t_head,
+ const Rotationd& start_q_head,
+ int64_t pose_timestamp) {
+ if (enable_external_pose_) {
+ return;
+ }
+
+ // If playing back data, the timestamps are different enough from the
+ // current time that prediction doesn't work. This hack pretends that
+ // there was one nanosecond of latency between the sensors and here.
+ if (enable_sensor_playback_)
+ pose_timestamp = GetSystemClockNs() - 1;
+
+ // Feed the sample to the predictor
+ pose_predictor_.Add(PosePredictor::Sample{.position = start_t_head,
+ .orientation = start_q_head,
+ .time_ns = pose_timestamp},
+ &last_known_pose_);
+
+ // Store one extra value, because the application is working on the next
+ // frame and expects the minimum count from that frame on.
+ for (uint32_t i = 0; i < kPoseAsyncBufferMinFutureCount + 1; ++i) {
+ int64_t target_time = photon_timestamp_ + i * display_period_ns_;
+
+ // TODO(jbates, cwolfe) For the DSP code, we may still want poses even when
+ // the vsyncs are not ticking up. But it's important not to update the pose
+ // data that's in the past so that applications have the most accurate
+ // estimate of the last frame's *actual* pose, so that they can update
+ // simulations and calculate collisions, etc.
+ if (target_time < pose_timestamp) {
+ // Already in the past, do not update this head pose slot.
+ continue;
+ }
+
+ // Write to the actual shared memory ring buffer.
+ uint32_t index = ((vsync_count_ + i) & kPoseAsyncBufferIndexMask);
+
+ // Make a pose prediction
+ if (enable_pose_prediction_) {
+ pose_predictor_.Predict(target_time,
+ target_time + right_eye_photon_offset_ns_,
+ mapped_pose_buffer_->ring + index);
+ } else {
+ mapped_pose_buffer_->ring[index] = last_known_pose_;
+ }
+ }
+}
+
+void PoseService::UpdatePoseMode() {
+ ALOGI_IF(TRACE, "UpdatePoseMode: %f %f %f", last_known_pose_.translation[0],
+ last_known_pose_.translation[1], last_known_pose_.translation[2]);
+
+ const int64_t current_time_ns = GetSystemClockNs();
+
+ const PoseState pose_state = sensor_fusion_.GetLatestPoseState();
+
+ switch (pose_mode_) {
+ case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+ case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+ case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+ case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+ case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+ case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE: {
+ // Calculate a pose based on monotic system time.
+ const Vector3d y_axis(0., 1., 0.);
+ double time_s = current_time_ns / 1e9;
+
+ // Generate fake yaw data.
+ float yaw = 0.0f;
+ Vector3d head_trans(0.0, 0.0, 0.0);
+ switch (pose_mode_) {
+ default:
+ case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+ // Pan across 120 degrees in 15 seconds.
+ yaw = std::cos(kTwoPi * time_s / 15.0) * 60.0 * kDegToRad;
+ break;
+ case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+ // Pan across 120 degrees in 4 seconds.
+ yaw = std::cos(kTwoPi * time_s / 4.0) * 60.0 * kDegToRad;
+ break;
+ case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+ // Rotate 5 degrees per second.
+ yaw = std::fmod(time_s * 5.0 * kDegToRad, kTwoPi);
+ break;
+ case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+ // Rotate 30 degrees per second.
+ yaw = std::fmod(time_s * 30.0 * kDegToRad, kTwoPi);
+ break;
+ case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+ // Rotate 90 degrees per second.
+ yaw = std::fmod(time_s * 90.0 * kDegToRad, kTwoPi);
+ break;
+ case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE:
+ // Circle strafe around origin at distance of 3 meters.
+ yaw = std::fmod(time_s * 30.0 * kDegToRad, kTwoPi);
+ head_trans += 3.0 * Vector3d(sin(yaw), 0.0, cos(yaw));
+ break;
+ }
+
+ // Calculate the simulated head rotation in an absolute "head" space.
+ // This space is not related to start space and doesn't need a
+ // reference.
+ Rotationd head_rotation_in_head_space(AngleAxisd(yaw, y_axis));
+
+ WriteAsyncPoses(head_trans, head_rotation_in_head_space, current_time_ns);
+ break;
+ }
+ case DVR_POSE_MODE_MOCK_FROZEN: {
+ // Even when frozen, we still provide a current timestamp, because
+ // consumers may rely on it being monotonic.
+
+ Rotationd start_from_head_rotation(
+ frozen_state_.head_from_start_rotation.w,
+ frozen_state_.head_from_start_rotation.x,
+ frozen_state_.head_from_start_rotation.y,
+ frozen_state_.head_from_start_rotation.z);
+ Vector3d head_from_start_translation(
+ frozen_state_.head_from_start_translation.x,
+ frozen_state_.head_from_start_translation.y,
+ frozen_state_.head_from_start_translation.z);
+
+ WriteAsyncPoses(head_from_start_translation, start_from_head_rotation,
+ current_time_ns);
+ break;
+ }
+ case DVR_POSE_MODE_3DOF: {
+ // Sensor fusion provides IMU-space data, transform to world space.
+
+ // Constants to perform IMU orientation adjustments. Note that these
+ // calculations will be optimized out in a release build.
+ constexpr double k90DegInRad = 90.0 * M_PI / 180.0;
+ const Vector3d kVecAxisX(1.0, 0.0, 0.0);
+ const Vector3d kVecAxisY(0.0, 1.0, 0.0);
+ const Vector3d kVecAxisZ(0.0, 0.0, 1.0);
+ const Rotationd kRotX90(AngleAxisd(k90DegInRad, kVecAxisX));
+
+ Rotationd start_from_head_rotation;
+ if (device_orientation_type_ == kOrientationTypeLandscape) {
+ const Rotationd kPostRotation =
+ kRotX90 * Rotationd(AngleAxisd(-k90DegInRad, kVecAxisY));
+ start_from_head_rotation =
+ (pose_state.sensor_from_start_rotation * kPostRotation).inverse();
+ } else {
+ const Rotationd kPreRotation =
+ Rotationd(AngleAxisd(k90DegInRad, kVecAxisZ));
+ const Rotationd kPostRotation = kRotX90;
+ start_from_head_rotation =
+ (kPreRotation * pose_state.sensor_from_start_rotation *
+ kPostRotation)
+ .inverse();
+ }
+ start_from_head_rotation.normalize();
+
+ // Neck / head model code procedure for when no 6dof is available.
+ // To apply the neck model, first translate the head pose to the new
+ // center of eyes, then rotate around the origin (the original head
+ // pos).
+ Vector3d position =
+ start_from_head_rotation * Vector3d(0.0, kDefaultNeckVerticalOffset,
+ -kDefaultNeckHorizontalOffset);
+
+ // IMU driver gives timestamps on its own clock, but we need monotonic
+ // clock. Subtract 5ms to account for estimated IMU sample latency.
+ WriteAsyncPoses(position, start_from_head_rotation,
+ pose_state.timestamp_ns + 5000000);
+ break;
+ }
+ default:
+ case DVR_POSE_MODE_6DOF:
+ ALOGE("ERROR: invalid pose mode");
+ break;
+ }
+}
+
+int PoseService::HandleMessage(pdx::Message& msg) {
+ int ret = 0;
+ const pdx::MessageInfo& info = msg.GetInfo();
+ switch (info.op) {
+ case DVR_POSE_NOTIFY_VSYNC: {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ // Kick the sensor thread, because we are still rendering.
+ KickSensorWatchDogThread();
+
+ const struct iovec data[] = {
+ {.iov_base = &vsync_count_, .iov_len = sizeof(vsync_count_)},
+ {.iov_base = &photon_timestamp_,
+ .iov_len = sizeof(photon_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_)},
+ };
+ constexpr int expected_size =
+ sizeof(vsync_count_) + sizeof(photon_timestamp_) +
+ sizeof(display_period_ns_) + sizeof(right_eye_photon_offset_ns_);
+ ret = msg.ReadVector(data, sizeof(data) / sizeof(data[0]));
+ if (ret < expected_size) {
+ ALOGI("error: msg.Read read too little (%d < %d)", ret, expected_size);
+ REPLY_ERROR(msg, EIO, error);
+ }
+
+ if (!enable_external_pose_) {
+ mapped_pose_buffer_->vsync_count = vsync_count_;
+ }
+
+ // TODO(jbates, eieio): make this async, no need to reply.
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_POSE_POLL: {
+ ATRACE_NAME("pose_poll");
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ DvrPoseState client_state;
+ client_state = {
+ .head_from_start_rotation = {last_known_pose_.orientation[0],
+ last_known_pose_.orientation[1],
+ last_known_pose_.orientation[2],
+ last_known_pose_.orientation[3]},
+ .head_from_start_translation = {last_known_pose_.translation[0],
+ last_known_pose_.translation[1],
+ last_known_pose_.translation[2]},
+ .timestamp_ns = static_cast<uint64_t>(last_known_pose_.timestamp_ns),
+ .sensor_from_start_rotation_velocity = {
+ last_known_pose_.angular_velocity[0],
+ last_known_pose_.angular_velocity[1],
+ last_known_pose_.angular_velocity[2]}};
+
+ Btrace("Sensor data received",
+ static_cast<int64_t>(client_state.timestamp_ns));
+
+ Btrace("Pose polled");
+
+ ret = msg.Write(&client_state, sizeof(client_state));
+ const int expected_size = sizeof(client_state);
+ if (ret < expected_size) {
+ ALOGI("error: msg.Write wrote too little (%d < %d)", ret,
+ expected_size);
+ REPLY_ERROR(msg, EIO, error);
+ }
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_POSE_FREEZE: {
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ DvrPoseState frozen_state;
+ const int expected_size = sizeof(frozen_state);
+ ret = msg.Read(&frozen_state, expected_size);
+ if (ret < expected_size) {
+ ALOGI("error: msg.Read read too little (%d < %d)", ret,
+ expected_size);
+ REPLY_ERROR(msg, EIO, error);
+ }
+ frozen_state_ = frozen_state;
+ }
+ SetPoseMode(DVR_POSE_MODE_MOCK_FROZEN);
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_POSE_SET_MODE: {
+ int mode;
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ const int expected_size = sizeof(mode);
+ ret = msg.Read(&mode, expected_size);
+ if (ret < expected_size) {
+ ALOGI("error: msg.Read read too little (%d < %d)", ret,
+ expected_size);
+ REPLY_ERROR(msg, EIO, error);
+ }
+ if (mode < 0 || mode >= DVR_POSE_MODE_COUNT) {
+ REPLY_ERROR(msg, EINVAL, error);
+ }
+ }
+ SetPoseMode(DvrPoseMode(mode));
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_POSE_GET_MODE: {
+ std::lock_guard<std::mutex> guard(mutex_);
+ int mode = pose_mode_;
+ ret = msg.Write(&mode, sizeof(mode));
+ const int expected_size = sizeof(mode);
+ if (ret < expected_size) {
+ ALOGI("error: msg.Write wrote too little (%d < %d)", ret,
+ expected_size);
+ REPLY_ERROR(msg, EIO, error);
+ }
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_POSE_GET_RING_BUFFER: {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ // Kick the sensor thread, because we have a new consumer.
+ KickSensorWatchDogThread();
+
+ Status<LocalChannelHandle> consumer_channel =
+ ring_buffer_->CreateConsumer();
+ REPLY_MESSAGE(msg, consumer_channel, error);
+ }
+ case DVR_POSE_GET_CONTROLLER_RING_BUFFER: {
+ std::lock_guard<std::mutex> guard(mutex_);
+ REPLY_ERROR(msg, EINVAL, error);
+ }
+ case DVR_POSE_LOG_CONTROLLER: {
+ std::lock_guard<std::mutex> guard(mutex_);
+ REPLY_ERROR(msg, EINVAL, error);
+ }
+ default:
+ // Do not lock mutex_ here, because this may call the on*() handlers,
+ // which will lock the mutex themselves.
+ ret = Service::HandleMessage(msg);
+ break;
+ }
+error:
+ return ret;
+}
+
+std::string PoseService::DumpState(size_t /*max_length*/) {
+ DvrPoseMode pose_mode;
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ pose_mode = pose_mode_;
+ }
+
+ std::ostringstream stream;
+ stream << "Pose mode: " << GetPoseModeString(pose_mode);
+ return stream.str();
+}
+
+void PoseService::HandleEvents(const sensors_event_t* begin_events,
+ const sensors_event_t* end_events) {
+ ATRACE_NAME("PoseService::HandleEvents");
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ for (const sensors_event_t* event = begin_events; event != end_events;
+ ++event) {
+ if (event->type == SENSOR_TYPE_ACCELEROMETER) {
+ sensor_fusion_.ProcessAccelerometerSample(
+ event->acceleration.x, event->acceleration.y, event->acceleration.z,
+ event->timestamp);
+ } else if (event->type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
+ sensor_fusion_.ProcessGyroscopeSample(event->gyro.x, event->gyro.y,
+ event->gyro.z, event->timestamp);
+ }
+ }
+
+ UpdatePoseMode();
+}
+
+void PoseService::SetPoseMode(DvrPoseMode mode) {
+ if (mode == DVR_POSE_MODE_6DOF) {
+ // Only 3DoF is currently supported.
+ mode = DVR_POSE_MODE_3DOF;
+ }
+
+ pose_mode_ = mode;
+
+ sensor_thread_->SetPaused(false);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/sensord/pose_service.h b/services/vr/sensord/pose_service.h
new file mode 100644
index 0000000..300737c
--- /dev/null
+++ b/services/vr/sensord/pose_service.h
@@ -0,0 +1,143 @@
+#ifndef ANDROID_DVR_SENSORD_POSE_SERVICE_H_
+#define ANDROID_DVR_SENSORD_POSE_SERVICE_H_
+
+#include <condition_variable>
+#include <forward_list>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <dvr/pose_client.h>
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/ring_buffer.h>
+#include <private/dvr/linear_pose_predictor.h>
+
+#include "sensor_fusion.h"
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// PoseService implements the HMD pose service over ServiceFS.
+class PoseService : public pdx::ServiceBase<PoseService> {
+ public:
+ ~PoseService() override;
+
+ bool IsInitialized() const override;
+ int HandleMessage(pdx::Message& msg) override;
+ std::string DumpState(size_t max_length) override;
+
+ // Handle events from the sensor HAL.
+ // Safe to call concurrently with any other public member functions.
+ void HandleEvents(const sensors_event_t* begin_events,
+ const sensors_event_t* end_events);
+
+ private:
+ friend BASE;
+
+ enum OrientationType {
+ // Typical smartphone device (default).
+ kOrientationTypePortrait = 1,
+ // Landscape device.
+ kOrientationTypeLandscape = 2,
+ };
+
+ // Initializes the service. Keeps a reference to sensor_thread, which must be
+ // non-null.
+ explicit PoseService(SensorThread* sensor_thread);
+
+ // Kick the sensor watch dog thread which will robustly disable IMU usage
+ // when there are no sensor data consumers.
+ // The class mutex (mutex_) must be locked while calling this method.
+ void KickSensorWatchDogThread();
+
+ void UpdatePoseMode();
+
+ // Update the async pose ring buffer with new pose data.
+ // |start_t_head| Head position in start space.
+ // |start_q_head| Head orientation quaternion in start space.
+ // |pose_timestamp| System timestamp of pose data in seconds.
+ // |pose_delta_time| Elapsed time in seconds between this pose and the last.
+ void WriteAsyncPoses(const Eigen::Vector3<double>& start_t_head,
+ const Eigen::Quaternion<double>& start_q_head,
+ int64_t pose_timestamp);
+
+ // Set the pose mode.
+ void SetPoseMode(DvrPoseMode mode);
+
+ // The abstraction around the sensor data.
+ SensorThread* sensor_thread_;
+
+ // Protects access to all member variables.
+ std::mutex mutex_;
+
+ // Watchdog thread data. The watchdog thread will ensure that sensor access
+ // is disabled when nothing has been consuming it for a while.
+ int64_t last_sensor_usage_time_ns_;
+ std::thread watchdog_thread_;
+ std::condition_variable watchdog_condition_;
+ bool watchdog_shutdown_;
+ bool sensors_on_;
+
+ // Indices for the accelerometer and gyroscope sensors, or -1 if the sensor
+ // wasn't present on construction.
+ int accelerometer_index_;
+ int gyroscope_index_;
+
+ // The sensor fusion algorithm and its state.
+ SensorFusion sensor_fusion_;
+
+ // Current pose mode.
+ DvrPoseMode pose_mode_;
+
+ // State which is sent if pose_mode_ is DVR_POSE_MODE_MOCK_FROZEN.
+ DvrPoseState frozen_state_;
+
+ // Last known pose.
+ DvrPoseAsync last_known_pose_;
+
+ // If this flag is true, the pose published includes a small prediction of
+ // where it'll be when it's consumed.
+ bool enable_pose_prediction_;
+
+ // Flag to turn on recording of raw sensor data
+ bool enable_sensor_recording_;
+
+ // Flag to log pose to a file
+ bool enable_pose_recording_;
+
+ // Flag to turn on playback from a saved dataset instead of using live data.
+ bool enable_sensor_playback_;
+
+ std::string sensor_playback_id_;
+
+ // External pose generation.
+ bool enable_external_pose_ = false;
+
+ // The predictor to extrapolate pose samples.
+ LinearPosePredictor pose_predictor_;
+
+ // Pose ring buffer.
+ std::shared_ptr<BufferProducer> ring_buffer_;
+ // Temporary mapped ring buffer.
+ DvrPoseRingBuffer* mapped_pose_buffer_;
+ // Current vsync info, updated by displayd.
+ uint32_t vsync_count_;
+ int64_t photon_timestamp_;
+ int64_t display_period_ns_;
+ int64_t right_eye_photon_offset_ns_ = 0;
+
+ // Type for controlling pose orientation calculation.
+ OrientationType device_orientation_type_;
+
+ PoseService(const PoseService&) = delete;
+ void operator=(const PoseService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_POSE_SERVICE_H_
diff --git a/services/vr/sensord/sensor_fusion.cpp b/services/vr/sensord/sensor_fusion.cpp
new file mode 100644
index 0000000..5663ae4
--- /dev/null
+++ b/services/vr/sensord/sensor_fusion.cpp
@@ -0,0 +1,348 @@
+#include "sensor_fusion.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// --- start of added bits for porting to eigen
+
+// In general, we prefer to add wrappers for things like Inverse() to minimize
+// the changes to the imported code, so that merging in upstream changes becomes
+// simpler.
+
+inline Matrix3d Inverse(const Matrix3d& matrix) { return matrix.inverse(); }
+inline Matrix3d Transpose(const Matrix3d& matrix) { return matrix.transpose(); }
+inline Matrix3d RotationMatrixNH(const Rotationd& rotation) {
+ return rotation.toRotationMatrix();
+}
+inline double Length(const Vector3d& vector) { return vector.norm(); }
+
+using uint64 = uint64_t;
+
+// --- end of added bits for porting to eigen
+
+static const double kFiniteDifferencingEpsilon = 1e-7;
+static const double kEpsilon = 1e-15;
+// Default gyroscope frequency. This corresponds to 200 Hz.
+static const double kDefaultGyroscopeTimestep_s = 0.005f;
+// Maximum time between gyroscope before we start limiting the integration.
+static const double kMaximumGyroscopeSampleDelay_s = 0.04f;
+// Compute a first-order exponential moving average of changes in accel norm per
+// frame.
+static const double kSmoothingFactor = 0.5;
+// Minimum and maximum values used for accelerometer noise covariance matrix.
+// The smaller the sigma value, the more weight is given to the accelerometer
+// signal.
+static const double kMinAccelNoiseSigma = 0.75;
+static const double kMaxAccelNoiseSigma = 7.0;
+// Initial value for the diagonal elements of the different covariance matrices.
+static const double kInitialStateCovarianceValue = 25.0;
+static const double kInitialProcessCovarianceValue = 1.0;
+// Maximum accelerometer norm change allowed before capping it covariance to a
+// large value.
+static const double kMaxAccelNormChange = 0.15;
+// Timestep IIR filtering coefficient.
+static const double kTimestepFilterCoeff = 0.95;
+// Minimum number of sample for timestep filtering.
+static const uint32_t kTimestepFilterMinSamples = 10;
+
+// Z direction in start space.
+static const Vector3d kCanonicalZDirection(0.0, 0.0, 1.0);
+
+// Computes a axis angle rotation from the input vector.
+// angle = norm(a)
+// axis = a.normalized()
+// If norm(a) == 0, it returns an identity rotation.
+static Rotationd RotationFromVector(const Vector3d& a) {
+ const double norm_a = Length(a);
+ if (norm_a < kEpsilon) {
+ return Rotationd::Identity();
+ }
+ return Rotationd(AngleAxisd(norm_a, a / norm_a));
+}
+
+// --- start of functions ported from pose_prediction.cc
+
+namespace pose_prediction {
+
+// Returns a rotation matrix based on the integration of the gyroscope_value
+// over the timestep_s in seconds.
+// TODO(pfg): Document the space better here.
+//
+// @param gyroscope_value gyroscope sensor values.
+// @param timestep_s integration period in seconds.
+// @return Integration of the gyroscope value the rotation is from Start to
+// Sensor Space.
+Rotationd GetRotationFromGyroscope(const Vector3d& gyroscope_value,
+ double timestep_s) {
+ const double velocity = Length(gyroscope_value);
+
+ // When there is no rotation data return an identity rotation.
+ if (velocity < kEpsilon) {
+ return Rotationd::Identity();
+ }
+ // Since the gyroscope_value is a start from sensor transformation we need to
+ // invert it to have a sensor from start transformation, hence the minus sign.
+ // For more info:
+ // http://developer.android.com/guide/topics/sensors/sensors_motion.html#sensors-motion-gyro
+ return Rotationd(AngleAxisd(-timestep_s * velocity,
+ gyroscope_value / velocity));
+}
+
+} // namespace pose_prediction
+
+// --- end of functions ported from pose_prediction.cc
+
+} // namespace
+
+SensorFusion::SensorFusion()
+ : execute_reset_with_next_accelerometer_sample_(false) {
+ ResetState();
+}
+
+void SensorFusion::Reset() {
+ execute_reset_with_next_accelerometer_sample_ = true;
+}
+
+void SensorFusion::ResetState() {
+ current_state_.timestamp_ns = 0;
+ current_state_.sensor_from_start_rotation = Rotationd::Identity();
+ current_state_.sensor_from_start_rotation_velocity = Vector3d::Zero();
+
+ current_accelerometer_timestamp_ns_ = 0;
+
+ state_covariance_ = Matrix3d::Identity() * kInitialStateCovarianceValue;
+ process_covariance_ = Matrix3d::Identity() * kInitialProcessCovarianceValue;
+ accelerometer_measurement_covariance_ =
+ Matrix3d::Identity() * kMinAccelNoiseSigma * kMinAccelNoiseSigma;
+ innovation_covariance_.setIdentity();
+
+ accelerometer_measurement_jacobian_ = Matrix3d::Zero();
+ kalman_gain_ = Matrix3d::Zero();
+ innovation_ = Vector3d::Zero();
+ accelerometer_measurement_ = Vector3d::Zero();
+ prediction_ = Vector3d::Zero();
+ control_input_ = Vector3d::Zero();
+ state_update_ = Vector3d::Zero();
+
+ moving_average_accelerometer_norm_change_ = 0.0;
+
+ is_timestep_filter_initialized_ = false;
+ is_gyroscope_filter_valid_ = false;
+ is_aligned_with_gravity_ = false;
+}
+
+// Here I am doing something wrong relative to time stamps. The state timestamps
+// always correspond to the gyrostamps because it would require additional
+// extrapolation if I wanted to do otherwise.
+// TODO(pfg): investigate about published an updated pose after accelerometer
+// data was used for filtering.
+PoseState SensorFusion::GetLatestPoseState() const {
+ std::unique_lock<std::mutex> lock(mutex_);
+ return current_state_;
+}
+
+void SensorFusion::ProcessGyroscopeSample(float v_x, float v_y, float v_z,
+ uint64 timestamp_ns) {
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ // Don't accept gyroscope sample when waiting for a reset.
+ if (execute_reset_with_next_accelerometer_sample_) {
+ return;
+ }
+
+ // Discard outdated samples.
+ if (current_state_.timestamp_ns >= timestamp_ns) {
+ // TODO(pfg): Investigate why this happens.
+ return;
+ }
+
+ // Checks that we received at least one gyroscope sample in the past.
+ if (current_state_.timestamp_ns != 0) {
+ // TODO(pfg): roll this in filter gyroscope timestep function.
+ double current_timestep_s =
+ static_cast<double>(timestamp_ns - current_state_.timestamp_ns) * 1e-9;
+ if (current_timestep_s > kMaximumGyroscopeSampleDelay_s) {
+ if (is_gyroscope_filter_valid_) {
+ // Replaces the delta timestamp by the filtered estimates of the delta
+ // time.
+ current_timestep_s = filtered_gyroscope_timestep_s_;
+ } else {
+ current_timestep_s = kDefaultGyroscopeTimestep_s;
+ }
+ } else {
+ FilterGyroscopeTimestep(current_timestep_s);
+ }
+
+ // Only integrate after receiving a accelerometer sample.
+ if (is_aligned_with_gravity_) {
+ const Rotationd rotation_from_gyroscope =
+ pose_prediction::GetRotationFromGyroscope(Vector3d(v_x, v_y, v_z),
+ current_timestep_s);
+ current_state_.sensor_from_start_rotation =
+ rotation_from_gyroscope * current_state_.sensor_from_start_rotation;
+ current_state_.sensor_from_start_rotation.normalize();
+ UpdateStateCovariance(RotationMatrixNH(rotation_from_gyroscope));
+ state_covariance_ =
+ state_covariance_ +
+ (process_covariance_ * (current_timestep_s * current_timestep_s));
+ }
+ }
+
+ // Saves gyroscope event for future prediction.
+ current_state_.timestamp_ns = timestamp_ns;
+ current_state_.sensor_from_start_rotation_velocity = Vector3d(v_x, v_y, v_z);
+}
+
+// TODO(pfg): move to rotation object for the input.
+Vector3d SensorFusion::ComputeInnovation(const Rotationd& pose) {
+ const Vector3d predicted_down_direction =
+ RotationMatrixNH(pose) * kCanonicalZDirection;
+
+ const Rotationd rotation = Rotationd::FromTwoVectors(
+ predicted_down_direction, accelerometer_measurement_);
+ AngleAxisd angle_axis(rotation);
+ return angle_axis.axis() * angle_axis.angle();
+}
+
+void SensorFusion::ComputeMeasurementJacobian() {
+ for (int dof = 0; dof < 3; dof++) {
+ // TODO(pfg): Create this delta rotation in the constructor and used unitX..
+ Vector3d delta = Vector3d::Zero();
+ delta[dof] = kFiniteDifferencingEpsilon;
+
+ const Rotationd epsilon_rotation = RotationFromVector(delta);
+ const Vector3d delta_rotation = ComputeInnovation(
+ epsilon_rotation * current_state_.sensor_from_start_rotation);
+
+ const Vector3d col =
+ (innovation_ - delta_rotation) / kFiniteDifferencingEpsilon;
+ accelerometer_measurement_jacobian_(0, dof) = col[0];
+ accelerometer_measurement_jacobian_(1, dof) = col[1];
+ accelerometer_measurement_jacobian_(2, dof) = col[2];
+ }
+}
+
+void SensorFusion::ProcessAccelerometerSample(float acc_x, float acc_y,
+ float acc_z,
+ uint64 timestamp_ns) {
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ // Discard outdated samples.
+ if (current_accelerometer_timestamp_ns_ >= timestamp_ns) {
+ // TODO(pfg): Investigate why this happens.
+ return;
+ }
+
+ // Call reset state if required.
+ if (execute_reset_with_next_accelerometer_sample_.exchange(false)) {
+ ResetState();
+ }
+
+ accelerometer_measurement_ = Vector3d(acc_x, acc_y, acc_z);
+ current_accelerometer_timestamp_ns_ = timestamp_ns;
+
+ if (!is_aligned_with_gravity_) {
+ // This is the first accelerometer measurement so it initializes the
+ // orientation estimate.
+ current_state_.sensor_from_start_rotation = Rotationd::FromTwoVectors(
+ kCanonicalZDirection, accelerometer_measurement_);
+ is_aligned_with_gravity_ = true;
+
+ previous_accelerometer_norm_ = Length(accelerometer_measurement_);
+ return;
+ }
+
+ UpdateMeasurementCovariance();
+
+ innovation_ = ComputeInnovation(current_state_.sensor_from_start_rotation);
+ ComputeMeasurementJacobian();
+
+ // S = H * P * H' + R
+ innovation_covariance_ = accelerometer_measurement_jacobian_ *
+ state_covariance_ *
+ Transpose(accelerometer_measurement_jacobian_) +
+ accelerometer_measurement_covariance_;
+
+ // K = P * H' * S^-1
+ kalman_gain_ = state_covariance_ *
+ Transpose(accelerometer_measurement_jacobian_) *
+ Inverse(innovation_covariance_);
+
+ // x_update = K*nu
+ state_update_ = kalman_gain_ * innovation_;
+
+ // P = (I - K * H) * P;
+ state_covariance_ = (Matrix3d::Identity() -
+ kalman_gain_ * accelerometer_measurement_jacobian_) *
+ state_covariance_;
+
+ // Updates pose and associate covariance matrix.
+ const Rotationd rotation_from_state_update =
+ RotationFromVector(state_update_);
+
+ current_state_.sensor_from_start_rotation =
+ rotation_from_state_update * current_state_.sensor_from_start_rotation;
+ UpdateStateCovariance(RotationMatrixNH(rotation_from_state_update));
+}
+
+void SensorFusion::UpdateStateCovariance(const Matrix3d& motion_update) {
+ state_covariance_ =
+ motion_update * state_covariance_ * Transpose(motion_update);
+}
+
+void SensorFusion::FilterGyroscopeTimestep(double gyroscope_timestep_s) {
+ if (!is_timestep_filter_initialized_) {
+ // Initializes the filter.
+ filtered_gyroscope_timestep_s_ = gyroscope_timestep_s;
+ num_gyroscope_timestep_samples_ = 1;
+ is_timestep_filter_initialized_ = true;
+ return;
+ }
+
+ // Computes the IIR filter response.
+ filtered_gyroscope_timestep_s_ =
+ kTimestepFilterCoeff * filtered_gyroscope_timestep_s_ +
+ (1 - kTimestepFilterCoeff) * gyroscope_timestep_s;
+ ++num_gyroscope_timestep_samples_;
+
+ if (num_gyroscope_timestep_samples_ > kTimestepFilterMinSamples) {
+ is_gyroscope_filter_valid_ = true;
+ }
+}
+
+void SensorFusion::UpdateMeasurementCovariance() {
+ const double current_accelerometer_norm = Length(accelerometer_measurement_);
+ // Norm change between current and previous accel readings.
+ const double current_accelerometer_norm_change =
+ std::abs(current_accelerometer_norm - previous_accelerometer_norm_);
+ previous_accelerometer_norm_ = current_accelerometer_norm;
+
+ moving_average_accelerometer_norm_change_ =
+ kSmoothingFactor * current_accelerometer_norm_change +
+ (1. - kSmoothingFactor) * moving_average_accelerometer_norm_change_;
+
+ // If we hit the accel norm change threshold, we use the maximum noise sigma
+ // for the accel covariance. For anything below that, we use a linear
+ // combination between min and max sigma values.
+ const double norm_change_ratio =
+ moving_average_accelerometer_norm_change_ / kMaxAccelNormChange;
+ const double accelerometer_noise_sigma = std::min(
+ kMaxAccelNoiseSigma,
+ kMinAccelNoiseSigma +
+ norm_change_ratio * (kMaxAccelNoiseSigma - kMinAccelNoiseSigma));
+
+ // Updates the accel covariance matrix with the new sigma value.
+ accelerometer_measurement_covariance_ = Matrix3d::Identity() *
+ accelerometer_noise_sigma *
+ accelerometer_noise_sigma;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/sensord/sensor_fusion.h b/services/vr/sensord/sensor_fusion.h
new file mode 100644
index 0000000..0ceae21
--- /dev/null
+++ b/services/vr/sensord/sensor_fusion.h
@@ -0,0 +1,181 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
+#define ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
+
+#include <atomic>
+#include <cstdlib>
+#include <mutex>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+using Matrix3d = Eigen::Matrix<double, 3, 3>;
+using Rotationd = quatd;
+using Vector3d = vec3d;
+using AngleAxisd = Eigen::AngleAxisd;
+
+// Ported from GVR's pose_state.h.
+// Stores a 3dof pose plus derivatives. This can be used for prediction.
+struct PoseState {
+ // Time in nanoseconds for the current pose.
+ uint64_t timestamp_ns;
+
+ // Rotation from Sensor Space to Start Space.
+ Rotationd sensor_from_start_rotation;
+
+ // First derivative of the rotation.
+ // TODO(pfg): currently storing gyro data, switch to first derivative instead.
+ Vector3d sensor_from_start_rotation_velocity;
+};
+
+// Sensor fusion class that implements an Extended Kalman Filter (EKF) to
+// estimate a 3D rotation from a gyroscope and and accelerometer.
+// This system only has one state, the pose. It does not estimate any velocity
+// or acceleration.
+//
+// To learn more about Kalman filtering one can read this article which is a
+// good introduction: http://en.wikipedia.org/wiki/Kalman_filter
+//
+// Start Space is :
+// z is up.
+// y is forward based on the first sensor data.
+// x = y \times z
+// Sensor Space follows the android specification {@link
+// http://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords}
+// See http://go/vr-coords for definitions of Start Space and Sensor Space.
+//
+// This is a port from GVR's SensorFusion code (See
+// https://cs/vr/gvr/sensors/sensor_fusion.h)
+// which in turn is a port from java of OrientationEKF (See
+// https://cs/java/com/google/vr/cardboard/vrtoolkit/vrtoolkit/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/OrientationEKF.java)
+class SensorFusion {
+ public:
+ SensorFusion();
+ SensorFusion(const SensorFusion&) = delete;
+ void operator=(const SensorFusion&) = delete;
+
+ // Resets the state of the sensor fusion. It sets the velocity for
+ // prediction to zero. The reset will happen with the next
+ // accelerometer sample. Gyroscope sample will be discarded until a new
+ // accelerometer sample arrives.
+ void Reset();
+
+ // Gets the PoseState representing the latest pose and derivatives at a
+ // particular timestamp as estimated by SensorFusion.
+ PoseState GetLatestPoseState() const;
+
+ // Processes one gyroscope sample event. This updates the pose of the system
+ // and the prediction model. The gyroscope data is assumed to be in axis angle
+ // form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T.
+ //
+ // @param v_x velocity in x.
+ // @param v_y velocity in y.
+ // @param v_z velocity in z.
+ // @param timestamp_ns gyroscope event timestamp in nanosecond.
+ void ProcessGyroscopeSample(float v_x, float v_y, float v_z,
+ uint64_t timestamp_ns);
+
+ // Processes one accelerometer sample event. This updates the pose of the
+ // system. If the Accelerometer norm changes too much between sample it is not
+ // trusted as much.
+ //
+ // @param acc_x accelerometer data in x.
+ // @param acc_y accelerometer data in y.
+ // @param acc_z accelerometer data in z.
+ // @param timestamp_ns accelerometer event timestamp in nanosecond.
+ void ProcessAccelerometerSample(float acc_x, float acc_y, float acc_z,
+ uint64_t timestamp_ns);
+
+ private:
+ // Estimates the average timestep between gyroscope event.
+ void FilterGyroscopeTimestep(double gyroscope_timestep);
+
+ // Updates the state covariance with an incremental motion. It changes the
+ // space of the quadric.
+ void UpdateStateCovariance(const Matrix3d& motion_update);
+
+ // Computes the innovation vector of the Kalman based on the input pose.
+ // It uses the latest measurement vector (i.e. accelerometer data), which must
+ // be set prior to calling this function.
+ Vector3d ComputeInnovation(const Rotationd& pose);
+
+ // This computes the measurement_jacobian_ via numerical differentiation based
+ // on the current value of sensor_from_start_rotation_.
+ void ComputeMeasurementJacobian();
+
+ // Updates the accelerometer covariance matrix.
+ //
+ // This looks at the norm of recent accelerometer readings. If it has changed
+ // significantly, it means the phone receives additional acceleration than
+ // just gravity, and so the down vector information gravity signal is noisier.
+ //
+ // TODO(dcoz,pfg): this function is very simple, we probably need something
+ // more elaborated here once we have proper regression testing.
+ void UpdateMeasurementCovariance();
+
+ // Reset all internal states. This is not thread safe. Lock should be acquired
+ // outside of it. This function is called in ProcessAccelerometerSample.
+ void ResetState();
+
+ // Current transformation from Sensor Space to Start Space.
+ // x_sensor = sensor_from_start_rotation_ * x_start;
+ PoseState current_state_;
+
+ // Filtering of the gyroscope timestep started?
+ bool is_timestep_filter_initialized_;
+ // Filtered gyroscope timestep valid?
+ bool is_gyroscope_filter_valid_;
+ // Sensor fusion currently aligned with gravity? After initialization
+ // it will requires a couple of accelerometer data for the system to get
+ // aligned.
+ bool is_aligned_with_gravity_;
+
+ // Covariance of Kalman filter state (P in common formulation).
+ Matrix3d state_covariance_;
+ // Covariance of the process noise (Q in common formulation).
+ Matrix3d process_covariance_;
+ // Covariance of the accelerometer measurement (R in common formulation).
+ Matrix3d accelerometer_measurement_covariance_;
+ // Covariance of innovation (S in common formulation).
+ Matrix3d innovation_covariance_;
+ // Jacobian of the measurements (H in common formulation).
+ Matrix3d accelerometer_measurement_jacobian_;
+ // Gain of the Kalman filter (K in common formulation).
+ Matrix3d kalman_gain_;
+ // Parameter update a.k.a. innovation vector. (\nu in common formulation).
+ Vector3d innovation_;
+ // Measurement vector (z in common formulation).
+ Vector3d accelerometer_measurement_;
+ // Current prediction vector (g in common formulation).
+ Vector3d prediction_;
+ // Control input, currently this is only the gyroscope data (\mu in common
+ // formulation).
+ Vector3d control_input_;
+ // Update of the state vector. (x in common formulation).
+ Vector3d state_update_;
+
+ // Time of the last accelerometer processed event.
+ uint64_t current_accelerometer_timestamp_ns_;
+
+ // Estimates of the timestep between gyroscope event in seconds.
+ double filtered_gyroscope_timestep_s_;
+ // Number of timestep samples processed so far by the filter.
+ uint32_t num_gyroscope_timestep_samples_;
+ // Norm of the accelerometer for the previous measurement.
+ double previous_accelerometer_norm_;
+ // Moving average of the accelerometer norm changes. It is computed for every
+ // sensor datum.
+ double moving_average_accelerometer_norm_change_;
+
+ // Flag indicating if a state reset should be executed with the next
+ // accelerometer sample.
+ std::atomic<bool> execute_reset_with_next_accelerometer_sample_;
+
+ mutable std::mutex mutex_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
diff --git a/services/vr/sensord/sensor_hal_thread.cpp b/services/vr/sensord/sensor_hal_thread.cpp
new file mode 100644
index 0000000..59b433f
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.cpp
@@ -0,0 +1,158 @@
+#include "sensor_hal_thread.h"
+
+#include <cutils/log.h>
+#include <dvr/performance_client_api.h>
+
+namespace android {
+namespace dvr {
+
+SensorHalThread::SensorHalThread(bool* out_success)
+ : shutting_down_(false),
+ paused_(false),
+ sensor_module_(nullptr),
+ sensor_device_(nullptr),
+ sensor_list_(nullptr) {
+ // Assume failure; we will change this to true on success.
+ *out_success = false;
+
+ // TODO(segal): module & device should be singletons.
+ int32_t err = hw_get_module_by_class(SENSORS_HARDWARE_MODULE_ID, "platform",
+ (hw_module_t const**)&sensor_module_);
+
+ if (err) {
+ ALOGE("couldn't load %s module (%s)", SENSORS_HARDWARE_MODULE_ID,
+ strerror(-err));
+ return;
+ }
+
+ err = sensors_open_1(&sensor_module_->common, &sensor_device_);
+ if (err) {
+ ALOGE("couldn't open device for module %s (%s)", SENSORS_HARDWARE_MODULE_ID,
+ strerror(-err));
+ return;
+ }
+
+ const int sensor_count =
+ sensor_module_->get_sensors_list(sensor_module_, &sensor_list_);
+
+ // Deactivate all of the sensors initially.
+ sensor_user_count_.resize(sensor_count, 0);
+ for (int i = 0; i < sensor_count; ++i) {
+ err = sensor_device_->activate(
+ reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+ sensor_list_[i].handle, 0);
+
+ if (err) {
+ ALOGE("failed to deactivate sensor %d (%s)", i, strerror(-err));
+ return;
+ }
+ }
+
+ // At this point, we've successfully initialized everything.
+ *out_success = true;
+}
+
+SensorHalThread::~SensorHalThread() {
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ shutting_down_ = true;
+ condition_.notify_one();
+ }
+
+ // Implicitly joins *thread_ if it's running.
+}
+
+void SensorHalThread::StartPolling(const EventConsumer& consumer) {
+ if (thread_) {
+ ALOGE("SensorHalThread::Start() called but thread is already running!");
+ return;
+ }
+
+ thread_.reset(new std::thread([this, consumer] {
+ const int priority_error = dvrSetSchedulerClass(0, "sensors:high");
+ LOG_ALWAYS_FATAL_IF(
+ priority_error < 0,
+ "SensorHalTread::StartPolling: Failed to set scheduler class: %s",
+ strerror(-priority_error));
+
+ for (;;) {
+ for (;;) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (shutting_down_)
+ return;
+ if (!paused_)
+ break;
+ condition_.wait(lock);
+ }
+ const int kMaxEvents = 100;
+ sensors_event_t events[kMaxEvents];
+ ssize_t event_count = 0;
+ do {
+ if (sensor_device_) {
+ event_count = sensor_device_->poll(
+ reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+ events, kMaxEvents);
+ } else {
+ // When there is no sensor_device_, we still call the consumer at
+ // regular intervals in case mock poses are in use. Note that this
+ // will never be the case for production devices, but this helps
+ // during bringup.
+ usleep(5000);
+ }
+ } while (event_count == -EINTR);
+ if (event_count == kMaxEvents)
+ ALOGI("max events (%d) reached", kMaxEvents);
+
+ if (event_count >= 0) {
+ consumer(events, events + event_count);
+ } else {
+ ALOGE(
+ "SensorHalThread::StartPolling: Error while polling sensor: %s "
+ "(%zd)",
+ strerror(-event_count), -event_count);
+ }
+ }
+ }));
+}
+
+void SensorHalThread::SetPaused(bool is_paused) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ paused_ = is_paused;
+ condition_.notify_one();
+}
+
+void SensorHalThread::StartUsingSensor(const int sensor_index) {
+ if (sensor_index < 0 || sensor_index >= GetSensorCount()) {
+ ALOGE("StartUsingSensor(): sensor index %d out of range [0, %d)",
+ sensor_index, GetSensorCount());
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(user_count_mutex_);
+ if (sensor_user_count_[sensor_index]++ == 0) {
+ sensor_device_->activate(
+ reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+ sensor_list_[sensor_index].handle, 1);
+ sensor_device_->setDelay(
+ reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+ sensor_list_[sensor_index].handle, 0);
+ }
+}
+
+void SensorHalThread::StopUsingSensor(const int sensor_index) {
+ if (sensor_index < 0 || sensor_index >= GetSensorCount()) {
+ ALOGE("StopUsingSensor(): sensor index %d out of range [0, %d)",
+ sensor_index, GetSensorCount());
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(user_count_mutex_);
+ if (--sensor_user_count_[sensor_index] == 0) {
+ sensor_device_->activate(
+ reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+ sensor_list_[sensor_index].handle, 0);
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/sensord/sensor_hal_thread.h b/services/vr/sensord/sensor_hal_thread.h
new file mode 100644
index 0000000..9220757
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
+
+#include <hardware/sensors.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor HAL. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorHalThread : public SensorThread {
+ public:
+ // Initializes the sensor HAL, but does not yet start polling (see Start()
+ // below). Sets *out_success to true on success; otherwise, sets *out_success
+ // to false and logs an error.
+ explicit SensorHalThread(bool* out_success);
+
+ // Tells the polling thread to shut down if it's running, and waits for it to
+ // complete its polling loop.
+ ~SensorHalThread() override;
+
+ // Begins polling on the thread. The provided consumer will be notified of
+ // events. Event notification occurs on the polling thread.
+ // Calling Start() more than once on an instance of SensorHalThread is
+ // invalid.
+ void StartPolling(const EventConsumer& consumer) override;
+
+ // Set whether the sensor polling thread is paused or not. This is useful
+ // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+ // must be paused while the 6DoF codepath is using the IMU event stream.
+ void SetPaused(bool is_paused) override;
+
+ // Increase the number of users of the given sensor by one. Activates the
+ // sensor if it wasn't already active.
+ // Safe to call concurrently with any other functions in this class.
+ void StartUsingSensor(int sensor_index) override;
+
+ // Decrease the number of users of the given sensor by one. Deactivates the
+ // sensor if its usage count has dropped to zero.
+ // Safe to call concurrently with any other functions in this class.
+ void StopUsingSensor(int sensor_index) override;
+
+ // The number of sensors that are available. Returns a negative number if
+ // initialization failed.
+ int GetSensorCount() const override {
+ return static_cast<int>(sensor_user_count_.size());
+ }
+
+ // The underlying sensor HAL data structure for the sensor at the given index.
+ int GetSensorType(int index) const override {
+ return sensor_list_[index].type;
+ }
+
+ private:
+ // The actual thread on which we consume events.
+ std::unique_ptr<std::thread> thread_;
+
+ // Mutex for access to shutting_down_ and paused_ members.
+ std::mutex mutex_;
+
+ // Condition for signaling pause/unpause to the thread.
+ std::condition_variable condition_;
+
+ // If this member is set to true, the thread will stop running at its next
+ // iteration. Only set with the mutex held and signal condition_ when changed.
+ bool shutting_down_;
+
+ // If this member is set to true, the thread will pause at its next
+ // iteration. Only set with the mutex held and signal condition_ when changed.
+ bool paused_;
+
+ // HAL access
+ struct sensors_module_t* sensor_module_;
+ sensors_poll_device_1_t* sensor_device_;
+
+ // Contiguous array of available sensors, owned by the sensor HAL.
+ const sensor_t* sensor_list_;
+
+ // Mutex that protects access to sensor_user_count_.data().
+ std::mutex user_count_mutex_;
+
+ // A count of how many users each sensor has. Protected by user_count_mutex.
+ std::vector<int> sensor_user_count_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
diff --git a/services/vr/sensord/sensor_ndk_thread.cpp b/services/vr/sensord/sensor_ndk_thread.cpp
new file mode 100644
index 0000000..b5e16e7
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.cpp
@@ -0,0 +1,257 @@
+#include "sensor_ndk_thread.h"
+
+#include <cutils/log.h>
+#include <dvr/performance_client_api.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+static constexpr int kLooperIdUser = 5;
+} // namespace
+
+SensorNdkThread::SensorNdkThread(bool* out_success)
+ : shutting_down_(false),
+ paused_(true),
+ thread_started_(false),
+ initialization_result_(false),
+ looper_(nullptr),
+ sensor_manager_(nullptr),
+ event_queue_(nullptr),
+ sensor_list_(nullptr),
+ sensor_count_(0) {
+ // Assume failure; we will change this to true on success.
+ *out_success = false;
+
+ // These structs are the same, but sanity check the sizes.
+ static_assert(sizeof(sensors_event_t) == sizeof(ASensorEvent),
+ "Error: sizeof(sensors_event_t) != sizeof(ASensorEvent)");
+
+ thread_.reset(new std::thread([this] {
+ const int priority_error = dvrSetSchedulerClass(0, "sensors:high");
+ LOG_ALWAYS_FATAL_IF(
+ priority_error < 0,
+ "SensorHalTread::StartPolling: Failed to set scheduler class: %s",
+ strerror(-priority_error));
+
+ // Start ALooper and initialize sensor access.
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ initialization_result_ = InitializeSensors();
+ thread_started_ = true;
+ init_condition_.notify_one();
+ if (!initialization_result_)
+ return;
+ }
+
+ EventConsumer consumer;
+ for (;;) {
+ for (;;) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ UpdateSensorUse();
+ if (!consumer)
+ consumer = consumer_;
+ if (shutting_down_)
+ return;
+ if (!paused_)
+ break;
+ condition_.wait(lock);
+ }
+
+ constexpr int kMaxEvents = 100;
+ sensors_event_t events[kMaxEvents];
+ ssize_t event_count = 0;
+ if (looper_ && sensor_manager_) {
+ int poll_fd, poll_events;
+ void* poll_source;
+ // Poll for events.
+ int ident = ALooper_pollAll(-1, &poll_fd, &poll_events, &poll_source);
+
+ if (ident != kLooperIdUser)
+ continue;
+
+ ASensorEvent* event = reinterpret_cast<ASensorEvent*>(&events[0]);
+ event_count =
+ ASensorEventQueue_getEvents(event_queue_, event, kMaxEvents);
+
+ if (event_count == 0) {
+ ALOGE("Detected sensor service failure, restarting sensors");
+ // This happens when sensorservice has died and restarted. To avoid
+ // spinning we need to restart the sensor access.
+ DestroySensors();
+ InitializeSensors();
+ }
+ } else {
+ // When there is no sensor_device_, we still call the consumer at
+ // regular intervals in case mock poses are in use. Note that this
+ // will never be the case for production devices, but this helps
+ // during bringup.
+ usleep(5000);
+ }
+ if (event_count == kMaxEvents)
+ ALOGI("max events (%d) reached", kMaxEvents);
+
+ if (event_count >= 0) {
+ consumer(events, events + event_count);
+ } else {
+ ALOGE(
+ "SensorNdkThread::StartPolling: Error while polling sensor: %s "
+ "(%zd)",
+ strerror(-event_count), -event_count);
+ }
+ }
+
+ // About to exit sensor thread, destroy sensor objects.
+ DestroySensors();
+ }));
+
+ // Wait for thread to startup and initialize sensors so that we know whether
+ // it succeeded.
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ while (!thread_started_)
+ init_condition_.wait(lock);
+ }
+
+ // At this point, we've successfully initialized everything.
+ *out_success = initialization_result_;
+}
+
+SensorNdkThread::~SensorNdkThread() {
+ {
+ if (looper_)
+ ALooper_wake(looper_);
+ std::unique_lock<std::mutex> lock(mutex_);
+ shutting_down_ = true;
+ condition_.notify_one();
+ }
+
+ thread_->join();
+}
+
+bool SensorNdkThread::InitializeSensors() {
+ looper_ = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+ if (!looper_) {
+ ALOGE("Failed to create ALooper.");
+ return false;
+ }
+
+ // Prepare to monitor accelerometer
+ sensor_manager_ = ASensorManager_getInstanceForPackage(nullptr);
+ if (!sensor_manager_) {
+ ALOGE("Failed to create ASensorManager.");
+ return false;
+ }
+
+ event_queue_ = ASensorManager_createEventQueue(
+ sensor_manager_, looper_, kLooperIdUser, nullptr, nullptr);
+ if (!event_queue_) {
+ ALOGE("Failed to create sensor EventQueue.");
+ return false;
+ }
+
+ sensor_count_ = ASensorManager_getSensorList(sensor_manager_, &sensor_list_);
+ ALOGI("Sensor count %d", sensor_count_);
+
+ sensor_user_count_.resize(sensor_count_, 0);
+
+ // To recover from sensorservice restart, enable the sensors that are already
+ // requested.
+ for (size_t sensor_index = 0; sensor_index < sensor_user_count_.size();
+ ++sensor_index) {
+ if (sensor_user_count_[sensor_index] > 0) {
+ int result = ASensorEventQueue_registerSensor(
+ event_queue_, sensor_list_[sensor_index], 0, 0);
+ ALOGE_IF(result < 0, "ASensorEventQueue_registerSensor failed: %d",
+ result);
+ }
+ }
+
+ return true;
+}
+
+void SensorNdkThread::DestroySensors() {
+ for (size_t sensor_index = 0; sensor_index < sensor_user_count_.size();
+ ++sensor_index) {
+ if (sensor_user_count_[sensor_index] > 0) {
+ ASensorEventQueue_disableSensor(event_queue_, sensor_list_[sensor_index]);
+ }
+ }
+ ASensorManager_destroyEventQueue(sensor_manager_, event_queue_);
+}
+
+void SensorNdkThread::UpdateSensorUse() {
+ if (!enable_sensors_.empty()) {
+ for (int sensor_index : enable_sensors_) {
+ if (sensor_user_count_[sensor_index]++ == 0) {
+ int result = ASensorEventQueue_registerSensor(
+ event_queue_, sensor_list_[sensor_index], 0, 0);
+ ALOGE_IF(result < 0, "ASensorEventQueue_registerSensor failed: %d",
+ result);
+ }
+ }
+ enable_sensors_.clear();
+ }
+
+ if (!disable_sensors_.empty()) {
+ for (int sensor_index : disable_sensors_) {
+ if (--sensor_user_count_[sensor_index] == 0) {
+ int result = ASensorEventQueue_disableSensor(
+ event_queue_, sensor_list_[sensor_index]);
+ ALOGE_IF(result < 0, "ASensorEventQueue_disableSensor failed: %d",
+ result);
+ }
+ }
+ disable_sensors_.clear();
+ }
+}
+
+void SensorNdkThread::StartPolling(const EventConsumer& consumer) {
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (consumer_) {
+ ALOGE("Already started sensor thread.");
+ return;
+ }
+ consumer_ = consumer;
+ }
+ SetPaused(false);
+}
+
+void SensorNdkThread::SetPaused(bool is_paused) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ // SetPaused may be called before we have StartPolling, make sure we have
+ // an event consumer. Otherwise we defer until StartPolling is called.
+ if (!consumer_)
+ return;
+ paused_ = is_paused;
+ condition_.notify_one();
+ ALooper_wake(looper_);
+}
+
+void SensorNdkThread::StartUsingSensor(const int sensor_index) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (sensor_index < 0 || sensor_index >= sensor_count_) {
+ ALOGE("StartUsingSensor(): sensor index %d out of range [0, %d)",
+ sensor_index, sensor_count_);
+ return;
+ }
+
+ enable_sensors_.push_back(sensor_index);
+ ALooper_wake(looper_);
+}
+
+void SensorNdkThread::StopUsingSensor(const int sensor_index) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (sensor_index < 0 || sensor_index >= sensor_count_) {
+ ALOGE("StopUsingSensor(): sensor index %d out of range [0, %d)",
+ sensor_index, sensor_count_);
+ return;
+ }
+
+ disable_sensors_.push_back(sensor_index);
+ ALooper_wake(looper_);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/sensord/sensor_ndk_thread.h b/services/vr/sensord/sensor_ndk_thread.h
new file mode 100644
index 0000000..eb3cf9d
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.h
@@ -0,0 +1,124 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
+
+#include <android/sensor.h>
+#include <hardware/sensors.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor data. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorNdkThread : public SensorThread {
+ public:
+ // Initializes the sensor access, but does not yet start polling (see Start()
+ // below). Sets *out_success to true on success; otherwise, sets *out_success
+ // to false and logs an error.
+ explicit SensorNdkThread(bool* out_success);
+
+ // Tells the polling thread to shut down if it's running, and waits for it to
+ // complete its polling loop.
+ ~SensorNdkThread() override;
+
+ // Begins polling on the thread. The provided consumer will be notified of
+ // events. Event notification occurs on the polling thread.
+ // Calling Start() more than once on an instance of SensorNdkThread is
+ // invalid.
+ void StartPolling(const EventConsumer& consumer) override;
+
+ // Set whether the sensor polling thread is paused or not. This is useful
+ // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+ // must be paused while the 6DoF codepath is using the IMU event stream.
+ void SetPaused(bool is_paused) override;
+
+ // Increase the number of users of the given sensor by one. Activates the
+ // sensor if it wasn't already active.
+ // Safe to call concurrently with any other functions in this class.
+ void StartUsingSensor(int sensor_index) override;
+
+ // Decrease the number of users of the given sensor by one. Deactivates the
+ // sensor if its usage count has dropped to zero.
+ // Safe to call concurrently with any other functions in this class.
+ void StopUsingSensor(int sensor_index) override;
+
+ // The number of sensors that are available. Returns a negative number if
+ // initialization failed.
+ int GetSensorCount() const override { return sensor_count_; }
+
+ // The underlying sensor HAL data structure for the sensor at the given index.
+ int GetSensorType(int index) const override {
+ return ASensor_getType(sensor_list_[index]);
+ }
+
+ private:
+ // Initialize ALooper and sensor access on the thread.
+ // Returns true on success, false on failure.
+ bool InitializeSensors();
+
+ // Destroy sensor access.
+ void DestroySensors();
+
+ // Start or stop requested sensors from the thread. Class mutex must already
+ // be locked.
+ void UpdateSensorUse();
+
+ // The actual thread on which we consume events.
+ std::unique_ptr<std::thread> thread_;
+
+ // Mutex for access to shutting_down_ and paused_ members.
+ std::mutex mutex_;
+
+ // Condition for signaling pause/unpause to the thread.
+ std::condition_variable condition_;
+
+ // Condition for signaling thread initialization.
+ std::condition_variable init_condition_;
+
+ // If this member is set to true, the thread will stop running at its next
+ // iteration. Only set with the mutex held and signal condition_ when changed.
+ bool shutting_down_;
+
+ // If this member is set to true, the thread will pause at its next
+ // iteration. Only set with the mutex held and signal condition_ when changed.
+ bool paused_;
+
+ // Thread start hand shake to verify that sensor initialization succeeded.
+ bool thread_started_;
+
+ // Initialization result (true for success).
+ bool initialization_result_;
+
+ // The callback.
+ EventConsumer consumer_;
+
+ // Sensor access
+ ALooper* looper_;
+ ASensorManager* sensor_manager_;
+ ASensorEventQueue* event_queue_;
+
+ // Sensor list from NDK.
+ ASensorList sensor_list_;
+ int sensor_count_;
+
+ // Requests to the sensor thread to enable or disable given sensors.
+ std::vector<int> enable_sensors_;
+ std::vector<int> disable_sensors_;
+
+ // A count of how many users each sensor has. Protected by user_count_mutex.
+ std::vector<int> sensor_user_count_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
diff --git a/services/vr/sensord/sensor_service.cpp b/services/vr/sensord/sensor_service.cpp
new file mode 100644
index 0000000..4396851
--- /dev/null
+++ b/services/vr/sensord/sensor_service.cpp
@@ -0,0 +1,187 @@
+#include "sensor_service.h"
+
+#include <cutils/log.h>
+#include <hardware/sensors.h>
+#include <poll.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/sensor-ipc.h>
+#include <time.h>
+
+using android::pdx::default_transport::Endpoint;
+
+namespace android {
+namespace dvr {
+
+SensorService::SensorService(SensorThread* sensor_thread)
+ : BASE("SensorService", Endpoint::Create(DVR_SENSOR_SERVICE_CLIENT)),
+ sensor_thread_(sensor_thread) {
+ sensor_clients_.resize(sensor_thread_->GetSensorCount());
+
+ for (int i = 0; i < sensor_thread_->GetSensorCount(); ++i)
+ type_to_sensor_[sensor_thread_->GetSensorType(i)] = i;
+}
+
+std::shared_ptr<pdx::Channel> SensorService::OnChannelOpen(pdx::Message& msg) {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ const pdx::MessageInfo& info = msg.GetInfo();
+
+ std::shared_ptr<SensorClient> client(
+ new SensorClient(*this, info.pid, info.cid));
+ AddClient(client);
+ return client;
+}
+
+void SensorService::OnChannelClose(pdx::Message& /*msg*/,
+ const std::shared_ptr<pdx::Channel>& chan) {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ auto client = std::static_pointer_cast<SensorClient>(chan);
+ if (!client) {
+ ALOGW("WARNING: SensorClient was NULL!\n");
+ return;
+ }
+ RemoveClient(client);
+}
+
+void SensorService::AddClient(const std::shared_ptr<SensorClient>& client) {
+ clients_.push_front(client);
+}
+
+void SensorService::RemoveClient(const std::shared_ptr<SensorClient>& client) {
+ // First remove it from the clients associated with its sensor, if any.
+ RemoveSensorClient(client.get());
+
+ // Finally, remove it from the list of clients we're aware of, and decrease
+ // its reference count.
+ clients_.remove(client);
+}
+
+void SensorService::RemoveSensorClient(SensorClient* client) {
+ if (!client->has_sensor())
+ return;
+
+ std::forward_list<SensorClient*>& sensor_clients =
+ sensor_clients_[client->sensor()];
+ sensor_clients.remove(client);
+ sensor_thread_->StopUsingSensor(client->sensor());
+
+ client->unset_sensor();
+}
+
+int SensorService::HandleMessage(pdx::Message& msg) {
+ int ret = 0;
+ const pdx::MessageInfo& info = msg.GetInfo();
+ switch (info.op) {
+ case DVR_SENSOR_START: {
+ std::lock_guard<std::mutex> guard(mutex_);
+ // Associate this channel with the indicated sensor,
+ // unless it already has an association. In that case,
+ // fail.
+ auto client = std::static_pointer_cast<SensorClient>(msg.GetChannel());
+ if (client->has_sensor())
+ REPLY_ERROR(msg, EINVAL, error);
+ int sensor_type;
+ if (msg.Read(&sensor_type, sizeof(sensor_type)) <
+ (ssize_t)sizeof(sensor_type))
+ REPLY_ERROR(msg, EIO, error);
+
+ // Find the sensor of the requested type.
+ if (type_to_sensor_.find(sensor_type) == type_to_sensor_.end())
+ REPLY_ERROR(msg, EINVAL, error);
+ const int sensor_index = type_to_sensor_[sensor_type];
+
+ sensor_clients_[sensor_index].push_front(client.get());
+ client->set_sensor(sensor_index);
+ sensor_thread_->StartUsingSensor(sensor_index);
+
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_SENSOR_STOP: {
+ std::lock_guard<std::mutex> guard(mutex_);
+ auto client = std::static_pointer_cast<SensorClient>(msg.GetChannel());
+ if (!client->has_sensor())
+ REPLY_ERROR(msg, EINVAL, error);
+ RemoveSensorClient(client.get());
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ case DVR_SENSOR_POLL: {
+ std::lock_guard<std::mutex> guard(mutex_);
+ auto client = std::static_pointer_cast<SensorClient>(msg.GetChannel());
+
+ // Package up the events we've got for this client. Number of
+ // events, followed by 0 or more sensor events, popped from
+ // this client's queue until it's empty.
+ int num_events = client->EventCount();
+ sensors_event_t out_buffer[num_events];
+ client->WriteEvents(out_buffer);
+ struct iovec svec[] = {
+ {.iov_base = &num_events, .iov_len = sizeof(num_events)},
+ {.iov_base = out_buffer,
+ .iov_len = num_events * sizeof(sensors_event_t)},
+ };
+ ret = msg.WriteVector(svec, 2);
+ int expected_size = sizeof(int) + num_events * sizeof(sensors_event_t);
+ if (ret < expected_size) {
+ ALOGI("error: msg.WriteVector wrote too little.");
+ REPLY_ERROR(msg, EIO, error);
+ }
+ REPLY_SUCCESS(msg, 0, error);
+ }
+ default:
+ // Do not lock mutex_ here, because this may call the on*() handlers,
+ // which will lock the mutex themselves.
+ ret = Service::HandleMessage(msg);
+ break;
+ }
+error:
+ return ret;
+}
+
+void SensorService::EnqueueEvents(const sensors_event_t* begin_events,
+ const sensors_event_t* end_events) {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ // Put the sensor values we got in the circular queue for each client that
+ // cares about the given event.
+ for (const sensors_event_t* event = begin_events; event != end_events;
+ ++event) {
+ const int sensor_index = type_to_sensor_[event->type];
+ for (const auto& client : sensor_clients_[sensor_index]) {
+ client->EnqueueEvent(*event);
+ }
+ }
+}
+
+void SensorClient::WriteEvents(sensors_event_t* buffer) {
+ while (!event_queue_.Empty()) {
+ *buffer = *(event_queue_.Top());
+ event_queue_.Pop();
+ ++buffer;
+ }
+}
+
+void SensorClient::CircularQ::Push(const sensors_event_t& event) {
+ if (count_ != 0 && head_ == tail_) {
+ Pop(); // If we're full, throw away the oldest event.
+ }
+ events_[head_] = event;
+ head_ = (head_ + 1) % kCqSize;
+ ++count_;
+}
+
+const sensors_event_t* SensorClient::CircularQ::Top() const {
+ if (count_ == 0)
+ return nullptr;
+ return &events_[tail_];
+}
+
+void SensorClient::CircularQ::Pop() {
+ if (count_ == 0)
+ return;
+ tail_ = (tail_ + 1) % kCqSize;
+ --count_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/sensord/sensor_service.h b/services/vr/sensord/sensor_service.h
new file mode 100644
index 0000000..c35fada
--- /dev/null
+++ b/services/vr/sensord/sensor_service.h
@@ -0,0 +1,132 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_SERVICE_H_
+#define ANDROID_DVR_SENSORD_SENSOR_SERVICE_H_
+
+#include <forward_list>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/service.h>
+#include <pthread.h>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+class SensorClient;
+
+/*
+ * SensorService implements the sensor service over ServiceFS.
+ * The sensor service provides an interface to one sensor over
+ * each channel.
+ */
+class SensorService : public pdx::ServiceBase<SensorService> {
+ public:
+ int HandleMessage(pdx::Message& msg) override;
+ std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& msg) override;
+ void OnChannelClose(pdx::Message& msg,
+ const std::shared_ptr<pdx::Channel>& chan) override;
+
+ // Enqueue the events in [begin_events, end_events) onto any clients that care
+ // about them.
+ // Safe to call concurrently with any other public member functions.
+ void EnqueueEvents(const sensors_event_t* begin_events,
+ const sensors_event_t* end_events);
+
+ private:
+ friend BASE;
+
+ // Initializes the service. Keeps a reference to sensor_thread, which must be
+ // non-null.
+ explicit SensorService(SensorThread* sensor_thread);
+
+ // The abstraction around the sensor HAL.
+ SensorThread* sensor_thread_;
+
+ // All of the clients we are connected to. This is the one place in this class
+ // where we keep the SensorClient instances alive using shared_ptr instances.
+ std::forward_list<std::shared_ptr<SensorClient>> clients_;
+
+ // Map types back to sensor indexes.
+ std::unordered_map<int, int> type_to_sensor_;
+ // For each sensor, the list of clients that are connected to it.
+ // Every entry in here must also be in clients_, so that its reference count
+ // remains positive.
+ std::vector<std::forward_list<SensorClient*>> sensor_clients_;
+
+ // Protects access to all member variables.
+ std::mutex mutex_;
+
+ // None of the following functions is thread-safe; callers must lock mutex_
+ // before calling one.
+ void AddClient(const std::shared_ptr<SensorClient>& client);
+ void RemoveClient(const std::shared_ptr<SensorClient>& client);
+ // Dissociate the indicated client from its sensor, if it has one; otherwise
+ // do nothing.
+ void RemoveSensorClient(SensorClient* client);
+
+ SensorService(const SensorService&) = delete;
+ void operator=(const SensorService&) = delete;
+};
+
+/*
+ * SensorClient manages the service-side per-client context for each client
+ * using the service.
+ */
+class SensorClient : public pdx::Channel {
+ public:
+ SensorClient(SensorService& /*service*/, int /*pid*/, int /*cid*/)
+ : sensor_index_(-1), has_sensor_index_(false) {}
+
+ bool has_sensor() const { return has_sensor_index_; }
+ int sensor() const { return sensor_index_; }
+ void set_sensor(int sensor) {
+ sensor_index_ = sensor;
+ has_sensor_index_ = true;
+ }
+ void unset_sensor() {
+ sensor_index_ = -1;
+ has_sensor_index_ = false;
+ }
+
+ int EventCount() const { return event_queue_.Count(); }
+
+ // Push an event onto our queue.
+ void EnqueueEvent(const sensors_event_t& event) { event_queue_.Push(event); }
+
+ // Write all the events in our queue (and clear it) to the supplied
+ // buffer. Buffer must be large enough.
+ void WriteEvents(sensors_event_t* buffer);
+
+ private:
+ SensorClient(const SensorClient&) = delete;
+ SensorClient& operator=(const SensorClient&) = delete;
+
+ int sensor_index_ = -1;
+ bool has_sensor_index_ = false;
+ // Circular queue holds as-yet-unasked-for events for the sensor associated
+ // with this client.
+ class CircularQ {
+ public:
+ static const int kCqSize = 10;
+ CircularQ() : head_(0), tail_(0), count_(0) {}
+ ~CircularQ() {}
+ void Push(const sensors_event_t& event);
+ const sensors_event_t* Top() const;
+ void Pop();
+ bool Empty() const { return count_ == 0; }
+ int Count() const { return count_; }
+
+ private:
+ sensors_event_t events_[kCqSize];
+ int head_ = 0;
+ int tail_ = 0;
+ int count_ = 0;
+ };
+ CircularQ event_queue_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_SENSOR_SERVICE_H_
diff --git a/services/vr/sensord/sensor_thread.cpp b/services/vr/sensord/sensor_thread.cpp
new file mode 100644
index 0000000..01e4e7e
--- /dev/null
+++ b/services/vr/sensord/sensor_thread.cpp
@@ -0,0 +1,9 @@
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+SensorThread::~SensorThread() {}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/sensord/sensor_thread.h b/services/vr/sensord/sensor_thread.h
new file mode 100644
index 0000000..46aba17
--- /dev/null
+++ b/services/vr/sensord/sensor_thread.h
@@ -0,0 +1,58 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_THREAD_H_
+
+#include <hardware/sensors.h>
+
+#include <functional>
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor data. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorThread {
+ public:
+ // A function type that can be called to provide it with new events.
+ // [events_begin, events_end) forms a contiguous array of events.
+ using EventConsumer = std::function<void(const sensors_event_t* events_begin,
+ const sensors_event_t* events_end)>;
+
+ // Tells the polling thread to shut down if it's running, and waits for it to
+ // complete its polling loop.
+ virtual ~SensorThread();
+
+ // Begins polling on the thread. The provided consumer will be notified of
+ // events. Event notification occurs on the polling thread.
+ // Calling Start() more than once on an instance of SensorThread is
+ // invalid.
+ virtual void StartPolling(const EventConsumer& consumer) = 0;
+
+ // Set whether the sensor polling thread is paused or not. This is useful
+ // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+ // must be paused while the 6DoF codepath is using the IMU event stream.
+ virtual void SetPaused(bool is_paused) = 0;
+
+ // Increase the number of users of the given sensor by one. Activates the
+ // sensor if it wasn't already active.
+ // Safe to call concurrently with any other functions in this class.
+ virtual void StartUsingSensor(int sensor_index) = 0;
+
+ // Decrease the number of users of the given sensor by one. Deactivates the
+ // sensor if its usage count has dropped to zero.
+ // Safe to call concurrently with any other functions in this class.
+ virtual void StopUsingSensor(int sensor_index) = 0;
+
+ // The number of sensors that are available. Returns a negative number if
+ // initialization failed.
+ virtual int GetSensorCount() const = 0;
+
+ // Get the sensor type for the sensor at the given index.
+ virtual int GetSensorType(int index) const = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_SENSOR_THREAD_H_
diff --git a/services/vr/sensord/sensord.cpp b/services/vr/sensord/sensord.cpp
new file mode 100644
index 0000000..0a75318
--- /dev/null
+++ b/services/vr/sensord/sensord.cpp
@@ -0,0 +1,87 @@
+#define LOG_TAG "sensord"
+
+#include <string.h>
+
+#include <binder/ProcessState.h>
+
+#include <dvr/performance_client_api.h>
+#include <pdx/default_transport/service_dispatcher.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/sensor-ipc.h>
+
+#include "pose_service.h"
+#include "sensor_hal_thread.h"
+#include "sensor_ndk_thread.h"
+#include "sensor_service.h"
+#include "sensor_thread.h"
+
+using android::dvr::PoseService;
+using android::dvr::SensorHalThread;
+using android::dvr::SensorNdkThread;
+using android::dvr::SensorService;
+using android::dvr::SensorThread;
+using android::pdx::Service;
+using android::pdx::ServiceDispatcher;
+
+int main(int, char**) {
+ ALOGI("Starting up...");
+
+ // We need to be able to create endpoints with full perms.
+ umask(0000);
+
+ android::ProcessState::self()->startThreadPool();
+
+ bool sensor_thread_succeeded = false;
+#ifdef SENSORD_USES_HAL
+ std::unique_ptr<SensorThread> sensor_thread(
+ new SensorHalThread(&sensor_thread_succeeded));
+#else
+ std::unique_ptr<SensorThread> sensor_thread(
+ new SensorNdkThread(&sensor_thread_succeeded));
+#endif
+
+ if (!sensor_thread_succeeded) {
+ ALOGE("ERROR: Failed to initialize SensorThread! No 3DoF!\n");
+ }
+
+ if (sensor_thread->GetSensorCount() == 0)
+ ALOGW("No sensors found\n");
+
+ auto sensor_service = SensorService::Create(sensor_thread.get());
+ if (!sensor_service) {
+ ALOGE("TERMINATING: failed to create SensorService!!!\n");
+ return -1;
+ }
+
+ auto pose_service = PoseService::Create(sensor_thread.get());
+ if (!pose_service) {
+ ALOGE("TERMINATING: failed to create PoseService!!!\n");
+ return -1;
+ }
+
+ std::unique_ptr<ServiceDispatcher> dispatcher =
+ android::pdx::default_transport::ServiceDispatcher::Create();
+ if (!dispatcher) {
+ ALOGE("TERMINATING: failed to create ServiceDispatcher!!!\n");
+ return -1;
+ }
+
+ dispatcher->AddService(sensor_service);
+ dispatcher->AddService(pose_service);
+
+ sensor_thread->StartPolling([sensor_service, pose_service](
+ const sensors_event_t* events_begin, const sensors_event_t* events_end) {
+ sensor_service->EnqueueEvents(events_begin, events_end);
+ pose_service->HandleEvents(events_begin, events_end);
+ });
+
+ const int priority_error = dvrSetSchedulerClass(0, "sensors:low");
+ LOG_ALWAYS_FATAL_IF(priority_error < 0,
+ "SensorService: Failed to set scheduler class: %s",
+ strerror(-priority_error));
+
+ int ret = dispatcher->EnterDispatchLoop();
+ ALOGI("Dispatch loop exited because: %s\n", strerror(-ret));
+
+ return ret;
+}
diff --git a/services/vr/sensord/sensord.rc b/services/vr/sensord/sensord.rc
new file mode 100644
index 0000000..0311474
--- /dev/null
+++ b/services/vr/sensord/sensord.rc
@@ -0,0 +1,5 @@
+service sensord /system/bin/sensord
+ class core
+ user system
+ group system camera sdcard_rw
+ cpuset /system
diff --git a/services/vr/sensord/test/poselatencytest.cpp b/services/vr/sensord/test/poselatencytest.cpp
new file mode 100644
index 0000000..615fc75
--- /dev/null
+++ b/services/vr/sensord/test/poselatencytest.cpp
@@ -0,0 +1,87 @@
+#include <dvr/pose_client.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <vector>
+
+// Creates a pose client and polls 30x for new data. Prints timestamp and
+// latency. Latency is calculated based on the difference between the
+// current clock and the timestamp from the Myriad, which has been synced
+// to QC time. Note that there is some clock drift and clocks are only sycned
+// when the FW is loaded.
+int main(int /*argc*/, char** /*argv*/) {
+ DvrPose* pose_client = dvrPoseCreate();
+ if (pose_client == nullptr) {
+ printf("Unable to create pose client\n");
+ return -1;
+ }
+
+ DvrPoseAsync last_state;
+ DvrPoseAsync current_state;
+ last_state.timestamp_ns = 0;
+ current_state.timestamp_ns = 0;
+
+ double avg_latency = 0;
+ double min_latency = (float)UINT64_MAX;
+ double max_latency = 0;
+ double std = 0;
+ std::vector<uint64_t> latency;
+
+ int num_samples = 100;
+ for (int i = 0; i < num_samples; ++i) {
+ while (last_state.timestamp_ns == current_state.timestamp_ns) {
+ uint32_t vsync_count = dvrPoseGetVsyncCount(pose_client);
+ int err = dvrPoseGet(pose_client, vsync_count, ¤t_state);
+ if (err) {
+ printf("Error polling pose: %d\n", err);
+ dvrPoseDestroy(pose_client);
+ return err;
+ }
+ }
+ struct timespec timespec;
+ uint64_t timestamp, diff;
+ clock_gettime(CLOCK_MONOTONIC, ×pec);
+ timestamp =
+ ((uint64_t)timespec.tv_sec * 1000000000) + (uint64_t)timespec.tv_nsec;
+ if (timestamp < current_state.timestamp_ns) {
+ printf("ERROR: excessive clock drift detected, reload FW to resync\n");
+ return -1;
+ }
+ diff = timestamp - current_state.timestamp_ns;
+ printf("%02d) ts = %" PRIu64 " time = %" PRIu64 "\n", i + 1,
+ current_state.timestamp_ns, timestamp);
+ printf("\tlatency: %" PRIu64 " ns (%" PRIu64 " us) (%" PRIu64 " ms)\n",
+ diff, diff / 1000, diff / 1000000);
+
+ avg_latency += diff;
+ if (diff < min_latency) {
+ min_latency = diff;
+ }
+ if (diff > max_latency) {
+ max_latency = diff;
+ }
+ latency.push_back(diff);
+
+ last_state = current_state;
+ }
+ avg_latency /= num_samples;
+ for (unsigned int i = 0; i < latency.size(); i++) {
+ std += pow(latency[i] - avg_latency, 2);
+ }
+ std /= latency.size();
+ std = sqrt(std);
+
+ printf("\n************************\n");
+ printf("Avg latency = %lf ns (%lf us) (%lf ms)\n", avg_latency,
+ avg_latency / 1000, avg_latency / 1000000);
+ printf("Max latency = %lf ns (%lf us) (%lf ms)\n", max_latency,
+ max_latency / 1000, max_latency / 1000000);
+ printf("Min latency = %lf ns (%lf us) (%lf ms)\n", min_latency,
+ min_latency / 1000, min_latency / 1000000);
+ printf("Standard dev = %lf ns (%lf us) (%lf ms)\n", std, std / 1000,
+ std / 1000000);
+ printf("\n************************\n");
+ return 0;
+}
diff --git a/services/vr/virtual_touchpad/Android.mk b/services/vr/virtual_touchpad/Android.mk
new file mode 100644
index 0000000..4224aaa
--- /dev/null
+++ b/services/vr/virtual_touchpad/Android.mk
@@ -0,0 +1,76 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+
+# Touchpad implementation.
+
+src := \
+ EvdevInjector.cpp \
+ VirtualTouchpad.cpp
+
+shared_libs := \
+ libbase
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\"
+LOCAL_MODULE := libvirtualtouchpad
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+
+# Touchpad unit tests.
+
+test_src_files := \
+ tests/VirtualTouchpad_test.cpp
+
+static_libs := \
+ libbase \
+ libcutils \
+ libvirtualtouchpad
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libs)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libs)) \
+ $(eval LOCAL_CPPFLAGS += -std=c++11) \
+ $(eval LOCAL_LDLIBS := -llog) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval LOCAL_MODULE_TAGS := optional) \
+ $(eval LOCAL_CXX_STL := libc++_static) \
+ $(eval include $(BUILD_NATIVE_TEST)) \
+)
+
+
+# Service.
+
+src := \
+ main.cpp \
+ VirtualTouchpadService.cpp \
+ aidl/android/dvr/VirtualTouchpadService.aidl
+
+static_libs := \
+ libcutils \
+ libvirtualtouchpad
+
+shared_libs := \
+ libbase \
+ libbinder \
+ libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := virtual_touchpad
+LOCAL_MODULE_TAGS := optional
+LOCAL_INIT_RC := virtual_touchpad.rc
+LOCAL_MULTILIB := 64
+LOCAL_CXX_STL := libc++_static
+include $(BUILD_EXECUTABLE)
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
new file mode 100644
index 0000000..be20c6c
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -0,0 +1,311 @@
+#include "EvdevInjector.h"
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/input.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+namespace android {
+namespace dvr {
+
+int EvdevInjector::UInput::Open() {
+ errno = 0;
+ fd_.reset(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+ if (fd_.get() < 0) {
+ ALOGE("couldn't open uinput (r=%d errno=%d)", fd_.get(), errno);
+ }
+ return errno;
+}
+
+int EvdevInjector::UInput::Close() {
+ errno = 0;
+ fd_.reset();
+ return errno;
+}
+
+int EvdevInjector::UInput::Write(const void* buf, size_t count) {
+ ALOGV("UInput::Write(%zu, %02X...)", count, *static_cast<const char*>(buf));
+ errno = 0;
+ ssize_t r = write(fd_.get(), buf, count);
+ if (r != static_cast<ssize_t>(count)) {
+ ALOGE("write(%zu) failed (r=%zd errno=%d)", count, r, errno);
+ }
+ return errno;
+}
+
+int EvdevInjector::UInput::IoctlSetInt(int request, int value) {
+ ALOGV("UInput::IoctlSetInt(0x%X, 0x%X)", request, value);
+ errno = 0;
+ if (const int status = ioctl(fd_.get(), request, value)) {
+ ALOGE("ioctl(%d, 0x%X, 0x%X) failed (r=%d errno=%d)", fd_.get(), request,
+ value, status, errno);
+ }
+ return errno;
+}
+
+int EvdevInjector::UInput::IoctlVoid(int request) {
+ ALOGV("UInput::IoctlVoid(0x%X)", request);
+ errno = 0;
+ if (const int status = ioctl(fd_.get(), request)) {
+ ALOGE("ioctl(%d, 0x%X) failed (r=%d errno=%d)", fd_.get(), request, status,
+ errno);
+ }
+ return errno;
+}
+
+void EvdevInjector::Close() {
+ uinput_->Close();
+ state_ = State::CLOSED;
+}
+
+int EvdevInjector::ConfigureBegin(const char* device_name, int16_t bustype,
+ int16_t vendor, int16_t product,
+ int16_t version) {
+ ALOGV("ConfigureBegin %s 0x%04" PRIX16 " 0x%04" PRIX16 " 0x%04" PRIX16
+ " 0x%04" PRIX16 "",
+ device_name, bustype, vendor, product, version);
+ if (!device_name || strlen(device_name) >= UINPUT_MAX_NAME_SIZE) {
+ return Error(ERROR_DEVICE_NAME);
+ }
+ if (const int status = RequireState(State::NEW)) {
+ return status;
+ }
+ if (!uinput_) {
+ owned_uinput_.reset(new EvdevInjector::UInput());
+ uinput_ = owned_uinput_.get();
+ }
+ if (const int status = uinput_->Open()) {
+ // Without uinput we're dead in the water.
+ state_ = State::CLOSED;
+ return Error(status);
+ }
+ state_ = State::CONFIGURING;
+ // Initialize device setting structure.
+ memset(&uidev_, 0, sizeof(uidev_));
+ strncpy(uidev_.name, device_name, UINPUT_MAX_NAME_SIZE);
+ uidev_.id.bustype = bustype;
+ uidev_.id.vendor = vendor;
+ uidev_.id.product = product;
+ uidev_.id.version = version;
+ return 0;
+}
+
+int EvdevInjector::ConfigureInputProperty(int property) {
+ ALOGV("ConfigureInputProperty %d", property);
+ if (property < 0 || property >= INPUT_PROP_CNT) {
+ ALOGE("property 0x%X out of range [0,0x%X)", property, INPUT_PROP_CNT);
+ return Error(ERROR_PROPERTY_RANGE);
+ }
+ if (const int status = RequireState(State::CONFIGURING)) {
+ return status;
+ }
+ if (const int status = uinput_->IoctlSetInt(UI_SET_PROPBIT, property)) {
+ ALOGE("failed to set property %d", property);
+ return Error(status);
+ }
+ return 0;
+}
+
+int EvdevInjector::ConfigureKey(uint16_t key) {
+ ALOGV("ConfigureKey 0x%02" PRIX16 "", key);
+ if (key < 0 || key >= KEY_CNT) {
+ ALOGE("key 0x%X out of range [0,0x%X)", key, KEY_CNT);
+ return Error(ERROR_KEY_RANGE);
+ }
+ if (const int status = RequireState(State::CONFIGURING)) {
+ return status;
+ }
+ if (const int status = EnableEventType(EV_KEY)) {
+ return status;
+ }
+ if (const int status = uinput_->IoctlSetInt(UI_SET_KEYBIT, key)) {
+ ALOGE("failed to enable EV_KEY 0x%02" PRIX16 "", key);
+ return Error(status);
+ }
+ return 0;
+}
+
+int EvdevInjector::ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max,
+ int32_t fuzz, int32_t flat) {
+ ALOGV("ConfigureAbs 0x%" PRIX16 " %" PRId32 " %" PRId32 " %" PRId32
+ " %" PRId32 "",
+ abs_type, min, max, fuzz, flat);
+ if (abs_type < 0 || abs_type >= ABS_CNT) {
+ ALOGE("EV_ABS type 0x%" PRIX16 " out of range [0,0x%X)", abs_type, ABS_CNT);
+ return Error(ERROR_ABS_RANGE);
+ }
+ if (const int status = RequireState(State::CONFIGURING)) {
+ return status;
+ }
+ if (const int status = EnableEventType(EV_ABS)) {
+ return status;
+ }
+ if (const int status = uinput_->IoctlSetInt(UI_SET_ABSBIT, abs_type)) {
+ ALOGE("failed to enable EV_ABS 0x%" PRIX16 "", abs_type);
+ return Error(status);
+ }
+ uidev_.absmin[abs_type] = min;
+ uidev_.absmax[abs_type] = max;
+ uidev_.absfuzz[abs_type] = fuzz;
+ uidev_.absflat[abs_type] = flat;
+ return 0;
+}
+
+int EvdevInjector::ConfigureMultiTouchXY(int x0, int y0, int x1, int y1) {
+ if (const int status = ConfigureAbs(ABS_MT_POSITION_X, x0, x1, 0, 0)) {
+ return status;
+ }
+ if (const int status = ConfigureAbs(ABS_MT_POSITION_Y, y0, y1, 0, 0)) {
+ return status;
+ }
+ return 0;
+}
+
+int EvdevInjector::ConfigureAbsSlots(int slots) {
+ return ConfigureAbs(ABS_MT_SLOT, 0, slots, 0, 0);
+}
+
+int EvdevInjector::ConfigureEnd() {
+ ALOGV("ConfigureEnd:");
+ ALOGV(" name=\"%s\"", uidev_.name);
+ ALOGV(" id.bustype=0x%04" PRIX16, uidev_.id.bustype);
+ ALOGV(" id.vendor=0x%04" PRIX16, uidev_.id.vendor);
+ ALOGV(" id.product=0x%04" PRIX16, uidev_.id.product);
+ ALOGV(" id.version=0x%04" PRIX16, uidev_.id.version);
+ ALOGV(" ff_effects_max=%" PRIu32, uidev_.ff_effects_max);
+ for (int i = 0; i < ABS_CNT; ++i) {
+ if (uidev_.absmin[i]) {
+ ALOGV(" absmin[%d]=%" PRId32, i, uidev_.absmin[i]);
+ }
+ if (uidev_.absmax[i]) {
+ ALOGV(" absmax[%d]=%" PRId32, i, uidev_.absmax[i]);
+ }
+ if (uidev_.absfuzz[i]) {
+ ALOGV(" absfuzz[%d]=%" PRId32, i, uidev_.absfuzz[i]);
+ }
+ if (uidev_.absflat[i]) {
+ ALOGV(" absflat[%d]=%" PRId32, i, uidev_.absflat[i]);
+ }
+ }
+
+ if (const int status = RequireState(State::CONFIGURING)) {
+ return status;
+ }
+ // Write out device settings.
+ if (const int status = uinput_->Write(&uidev_, sizeof uidev_)) {
+ ALOGE("failed to write device settings");
+ return Error(status);
+ }
+ // Create device node.
+ if (const int status = uinput_->IoctlVoid(UI_DEV_CREATE)) {
+ ALOGE("failed to create device node");
+ return Error(status);
+ }
+ state_ = State::READY;
+ return 0;
+}
+
+int EvdevInjector::Send(uint16_t type, uint16_t code, int32_t value) {
+ ALOGV("Send(0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32 ")", type, code, value);
+ if (const int status = RequireState(State::READY)) {
+ return status;
+ }
+ struct input_event event;
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ if (const int status = uinput_->Write(&event, sizeof(event))) {
+ ALOGE("failed to write event 0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32,
+ type, code, value);
+ return Error(status);
+ }
+ return 0;
+}
+
+int EvdevInjector::SendSynReport() { return Send(EV_SYN, SYN_REPORT, 0); }
+
+int EvdevInjector::SendKey(uint16_t code, int32_t value) {
+ return Send(EV_KEY, code, value);
+}
+
+int EvdevInjector::SendAbs(uint16_t code, int32_t value) {
+ return Send(EV_ABS, code, value);
+}
+
+int EvdevInjector::SendMultiTouchSlot(int32_t slot) {
+ if (latest_slot_ != slot) {
+ if (const int status = SendAbs(ABS_MT_SLOT, slot)) {
+ return status;
+ }
+ latest_slot_ = slot;
+ }
+ return 0;
+}
+
+int EvdevInjector::SendMultiTouchXY(int32_t slot, int32_t id, int32_t x,
+ int32_t y) {
+ if (const int status = SendMultiTouchSlot(slot)) {
+ return status;
+ }
+ if (const int status = SendAbs(ABS_MT_TRACKING_ID, id)) {
+ return status;
+ }
+ if (const int status = SendAbs(ABS_MT_POSITION_X, x)) {
+ return status;
+ }
+ if (const int status = SendAbs(ABS_MT_POSITION_Y, y)) {
+ return status;
+ }
+ return 0;
+}
+
+int EvdevInjector::SendMultiTouchLift(int32_t slot) {
+ if (const int status = SendMultiTouchSlot(slot)) {
+ return status;
+ }
+ if (const int status = SendAbs(ABS_MT_TRACKING_ID, -1)) {
+ return status;
+ }
+ return 0;
+}
+
+int EvdevInjector::Error(int code) {
+ if (!error_) {
+ error_ = code;
+ }
+ return code;
+}
+
+int EvdevInjector::RequireState(State required_state) {
+ if (error_) {
+ return error_;
+ }
+ if (state_ != required_state) {
+ ALOGE("in state %d but require state %d", static_cast<int>(state_),
+ static_cast<int>(required_state));
+ return Error(ERROR_SEQUENCING);
+ }
+ return 0;
+}
+
+int EvdevInjector::EnableEventType(uint16_t type) {
+ if (const int status = RequireState(State::CONFIGURING)) {
+ return status;
+ }
+ if (enabled_event_types_.count(type) > 0) {
+ return 0;
+ }
+ if (const int status = uinput_->IoctlSetInt(UI_SET_EVBIT, type)) {
+ ALOGE("failed to enable event type 0x%X", type);
+ return Error(status);
+ }
+ enabled_event_types_.insert(type);
+ return 0;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/virtual_touchpad/EvdevInjector.h b/services/vr/virtual_touchpad/EvdevInjector.h
new file mode 100644
index 0000000..1b1c4da
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.h
@@ -0,0 +1,139 @@
+#ifndef ANDROID_DVR_EVDEV_INJECTOR_H
+#define ANDROID_DVR_EVDEV_INJECTOR_H
+
+#include <android-base/unique_fd.h>
+#include <linux/uinput.h>
+
+#include <cstdint>
+#include <memory>
+#include <unordered_set>
+
+namespace android {
+namespace dvr {
+
+// Simulated evdev input device.
+//
+class EvdevInjector {
+ public:
+ // EvdevInjector-specific error codes are negative integers; other non-zero
+ // values returned from public routines are |errno| codes from underlying I/O.
+ // EvdevInjector maintains a 'sticky' error state, similar to |errno|, so that
+ // a caller can perform a sequence of operations and check for errors at the
+ // end using |GetError()|. In general, the first such error will be recorded
+ // and will suppress effects of further device operations until |ResetError()|
+ // is called.
+ //
+ enum : int {
+ ERROR_DEVICE_NAME = -1, // Invalid device name.
+ ERROR_PROPERTY_RANGE = -2, // |INPUT_PROP_*| code out of range.
+ ERROR_KEY_RANGE = -3, // |KEY_*|/|BTN_*| code out of range.
+ ERROR_ABS_RANGE = -4, // |ABS_*| code out of range.
+ ERROR_SEQUENCING = -5, // Configure/Send out of order.
+ };
+
+ // Key event |value| is not defined in <linux/input.h>.
+ enum : int32_t { KEY_RELEASE = 0, KEY_PRESS = 1, KEY_REPEAT = 2 };
+
+ // UInput provides a shim to intercept /dev/uinput operations
+ // just above the system call level, for testing.
+ //
+ class UInput {
+ public:
+ UInput() {}
+ virtual ~UInput() {}
+ virtual int Open();
+ virtual int Close();
+ virtual int Write(const void* buf, size_t count);
+ virtual int IoctlVoid(int request);
+ virtual int IoctlSetInt(int request, int value);
+
+ private:
+ base::unique_fd fd_;
+ };
+
+ EvdevInjector() {}
+ ~EvdevInjector() { Close(); }
+ void Close();
+
+ int GetError() const { return error_; }
+ void ResetError() { error_ = 0; }
+
+ // Configuration must be performed before sending any events.
+ // |ConfigureBegin()| must be called first, and |ConfigureEnd()| last,
+ // with zero or more other |Configure...()| calls in between in any order.
+
+ // Configure the basic evdev device properties; must be called first.
+ int ConfigureBegin(const char* device_name, int16_t bustype, int16_t vendor,
+ int16_t product, int16_t version);
+
+ // Configure an optional input device property.
+ // @param property One of the |INPUT_PROP_*| constants from <linux/input.h>.
+ int ConfigureInputProperty(int property);
+
+ // Configure an input key.
+ // @param key One of the |KEY_*| or |BTN_*| constants from <linux/input.h>.
+ int ConfigureKey(uint16_t key);
+
+ // Configure an absolute axis.
+ // @param abs_type One of the |KEY_*| or |BTN_*| constants from
+ // <linux/input.h>.
+ int ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max, int32_t fuzz,
+ int32_t flat);
+
+ // Configure the number of multitouch slots.
+ int ConfigureAbsSlots(int slots);
+
+ // Configure multitouch coordinate range.
+ int ConfigureMultiTouchXY(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
+
+ // Complete configuration and create the input device.
+ int ConfigureEnd();
+
+ // Send various events.
+ //
+ int Send(uint16_t type, uint16_t code, int32_t value);
+ int SendSynReport();
+ int SendKey(uint16_t code, int32_t value);
+ int SendAbs(uint16_t code, int32_t value);
+ int SendMultiTouchSlot(int32_t slot);
+ int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
+ int SendMultiTouchLift(int32_t slot);
+
+ protected:
+ // Must be called only between construction and ConfigureBegin().
+ inline void SetUInputForTesting(UInput* uinput) { uinput_ = uinput; }
+ // Caller must not retain pointer longer than EvdevInjector.
+ inline const uinput_user_dev* GetUiDevForTesting() const { return &uidev_; }
+
+ private:
+ // Phase to enforce that configuration is complete before events are sent.
+ enum class State { NEW, CONFIGURING, READY, CLOSED };
+
+ // Sets |error_| if it is not already set; returns |code|.
+ int Error(int code);
+
+ // Returns a nonzero error if the injector is not in the required |state|.
+ int RequireState(State state);
+
+ // Configures an event type if necessary.
+ // @param type One of the |EV_*| constants from <linux/input.h>.
+ int EnableEventType(uint16_t type);
+
+ // Active pointer to owned or testing UInput.
+ UInput* uinput_ = nullptr;
+ std::unique_ptr<UInput> owned_uinput_;
+
+ State state_ = State::NEW;
+ int error_ = 0;
+ uinput_user_dev uidev_;
+ std::unordered_set<uint16_t> enabled_event_types_;
+ int32_t latest_slot_ = -1;
+
+ EvdevInjector(const EvdevInjector&) = delete;
+ void operator=(const EvdevInjector&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_EVDEV_INJECTOR_H
diff --git a/services/vr/virtual_touchpad/VirtualTouchpad.cpp b/services/vr/virtual_touchpad/VirtualTouchpad.cpp
new file mode 100644
index 0000000..b137dd7
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpad.cpp
@@ -0,0 +1,80 @@
+#include "VirtualTouchpad.h"
+
+#include <cutils/log.h>
+#include <inttypes.h>
+#include <linux/input.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// Virtual evdev device properties.
+static const char* const kDeviceName = "vr window manager virtual touchpad";
+static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
+static constexpr int16_t kDeviceVendor = 0x18D1; // Google USB vendor ID.
+static constexpr int16_t kDeviceProduct = 0x5652; // 'VR'
+static constexpr int16_t kDeviceVersion = 0x0001;
+static constexpr int32_t kWidth = 0x10000;
+static constexpr int32_t kHeight = 0x10000;
+static constexpr int32_t kSlots = 2;
+
+} // anonymous namespace
+
+int VirtualTouchpad::Initialize() {
+ if (!injector_) {
+ owned_injector_.reset(new EvdevInjector());
+ injector_ = owned_injector_.get();
+ }
+ injector_->ConfigureBegin(kDeviceName, kDeviceBusType, kDeviceVendor,
+ kDeviceProduct, kDeviceVersion);
+ injector_->ConfigureInputProperty(INPUT_PROP_DIRECT);
+ injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
+ injector_->ConfigureAbsSlots(kSlots);
+ injector_->ConfigureKey(BTN_TOUCH);
+ injector_->ConfigureEnd();
+ return injector_->GetError();
+}
+
+int VirtualTouchpad::Touch(float x, float y, float pressure) {
+ int error = 0;
+ int32_t device_x = x * kWidth;
+ int32_t device_y = y * kHeight;
+ touches_ = ((touches_ & 1) << 1) | (pressure > 0);
+ ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d",
+ x, y, pressure, device_x, device_y, touches_);
+
+ injector_->ResetError();
+ switch (touches_) {
+ case 0b00: // Hover continues.
+ if (device_x != last_device_x_ || device_y != last_device_y_) {
+ injector_->SendMultiTouchXY(0, 0, device_x, device_y);
+ injector_->SendSynReport();
+ }
+ break;
+ case 0b01: // Touch begins.
+ // Press.
+ injector_->SendMultiTouchXY(0, 0, device_x, device_y);
+ injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
+ injector_->SendSynReport();
+ break;
+ case 0b10: // Touch ends.
+ injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
+ injector_->SendMultiTouchLift(0);
+ injector_->SendSynReport();
+ break;
+ case 0b11: // Touch continues.
+ if (device_x != last_device_x_ || device_y != last_device_y_) {
+ injector_->SendMultiTouchXY(0, 0, device_x, device_y);
+ injector_->SendSynReport();
+ }
+ break;
+ }
+ last_device_x_ = device_x;
+ last_device_y_ = device_y;
+
+ return injector_->GetError();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpad.h b/services/vr/virtual_touchpad/VirtualTouchpad.h
new file mode 100644
index 0000000..7e7801e
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpad.h
@@ -0,0 +1,44 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_H
+
+#include <memory>
+
+#include "EvdevInjector.h"
+
+namespace android {
+namespace dvr {
+
+class EvdevInjector;
+
+class VirtualTouchpad {
+ public:
+ VirtualTouchpad() {}
+ int Initialize();
+ int Touch(float x, float y, float pressure);
+
+ protected:
+ // Must be called only between construction and Initialize().
+ inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
+ injector_ = injector;
+ }
+
+ private:
+ // Active pointer to |owned_injector_| or to a testing injector.
+ EvdevInjector* injector_ = nullptr;
+ std::unique_ptr<EvdevInjector> owned_injector_;
+
+ // Previous (x,y) position to suppress redundant events.
+ int32_t last_device_x_ = INT32_MIN;
+ int32_t last_device_y_ = INT32_MIN;
+
+ // Records current touch state in bit 0 and previous state in bit 1.
+ int touches_ = 0;
+
+ VirtualTouchpad(const VirtualTouchpad&) = delete;
+ void operator=(const VirtualTouchpad&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_H
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
new file mode 100644
index 0000000..e5ead0e
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -0,0 +1,23 @@
+#include "VirtualTouchpadService.h"
+
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <linux/input.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace dvr {
+
+int VirtualTouchpadService::Initialize() {
+ return touchpad_.Initialize();
+}
+
+binder::Status VirtualTouchpadService::touch(float x, float y, float pressure) {
+ // Permissions check added and removed here :^)
+ const int error = touchpad_.Touch(x, y, pressure);
+ return error ? binder::Status::fromServiceSpecificError(error)
+ : binder::Status::ok();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.h b/services/vr/virtual_touchpad/VirtualTouchpadService.h
new file mode 100644
index 0000000..05a2a50
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
+
+#include <android/dvr/BnVirtualTouchpadService.h>
+
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+class VirtualTouchpadService : public BnVirtualTouchpadService {
+ public:
+ VirtualTouchpadService(VirtualTouchpad& touchpad)
+ : touchpad_(touchpad) {}
+
+ // Must be called before clients can connect.
+ // Returns 0 if initialization is successful.
+ int Initialize();
+
+ static char const* getServiceName() { return "virtual_touchpad"; }
+
+ protected:
+ // Implements IVirtualTouchpadService.
+ ::android::binder::Status touch(float x, float y, float pressure) override;
+
+ private:
+ VirtualTouchpad& touchpad_;
+
+ VirtualTouchpadService(const VirtualTouchpadService&) = delete;
+ void operator=(const VirtualTouchpadService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VIRTUAL_TOUCHPAD_SERVICE_H
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
new file mode 100644
index 0000000..da4de94
--- /dev/null
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -0,0 +1,16 @@
+package android.dvr;
+
+/** @hide */
+interface VirtualTouchpadService
+{
+ /**
+ * Generate a simulated touch event.
+ *
+ * @param x Horizontal touch position.
+ * @param y Vertical touch position.
+ * @param pressure Touch pressure; use 0.0 for no touch (lift or hover).
+ *
+ * Position values in the range [0.0, 1.0) map to the screen.
+ */
+ void touch(float x, float y, float pressure);
+}
diff --git a/services/vr/virtual_touchpad/main.cpp b/services/vr/virtual_touchpad/main.cpp
new file mode 100644
index 0000000..57471c5
--- /dev/null
+++ b/services/vr/virtual_touchpad/main.cpp
@@ -0,0 +1,36 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/log.h>
+
+#include "VirtualTouchpadService.h"
+
+int main() {
+ ALOGI("Starting");
+ android::dvr::VirtualTouchpad touchpad;
+ android::dvr::VirtualTouchpadService touchpad_service(touchpad);
+ const int touchpad_status = touchpad_service.Initialize();
+ if (touchpad_status) {
+ ALOGE("virtual touchpad initialization failed: %d", touchpad_status);
+ exit(1);
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+ android::sp<android::ProcessState> ps(android::ProcessState::self());
+ ps->setThreadPoolMaxThreadCount(4);
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+
+ android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+ const android::status_t service_status =
+ sm->addService(android::String16(touchpad_service.getServiceName()),
+ &touchpad_service, false /*allowIsolated*/);
+ if (service_status != android::OK) {
+ ALOGE("virtual touchpad service not added: %d",
+ static_cast<int>(service_status));
+ exit(2);
+ }
+
+ android::IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
new file mode 100644
index 0000000..874ef80
--- /dev/null
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -0,0 +1,233 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+
+#include "EvdevInjector.h"
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class UInputForTesting : public EvdevInjector::UInput {
+ public:
+ void WriteInputEvent(uint16_t type, uint16_t code, int32_t value) {
+ struct input_event event;
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ Write(&event, sizeof (event));
+ }
+};
+
+// Recording test implementation of UInput.
+//
+class UInputRecorder : public UInputForTesting {
+ public:
+ UInputRecorder() {}
+ virtual ~UInputRecorder() {}
+
+ const std::string& GetString() const { return s_; }
+ void Reset() { s_.clear(); }
+
+ // UInput overrides:
+
+ int Open() override {
+ s_ += "o;";
+ return 0;
+ }
+
+ int Close() override {
+ s_ += "c;";
+ return 0;
+ }
+
+ int Write(const void* buf, size_t count) override {
+ s_ += "w(";
+ s_ += Encode(&count, sizeof(count));
+ s_ += ",";
+ s_ += Encode(buf, count);
+ s_ += ");";
+ return 0;
+ }
+
+ int IoctlVoid(int request) override {
+ s_ += "i(";
+ s_ += Encode(&request, sizeof(request));
+ s_ += ");";
+ return 0;
+ }
+
+ int IoctlSetInt(int request, int value) override {
+ s_ += "i(";
+ s_ += Encode(&request, sizeof(request));
+ s_ += ",";
+ s_ += Encode(&value, sizeof(value));
+ s_ += ");";
+ return 0;
+ }
+
+ private:
+ std::string s_;
+
+ std::string Encode(const void* buf, size_t count) {
+ const char* in = static_cast<const char*>(buf);
+ char out[2 * count + 1];
+ for (size_t i = 0; i < count; ++i) {
+ snprintf(&out[2 * i], 3, "%02X", in[i]);
+ }
+ return out;
+ }
+};
+
+class EvdevInjectorForTesting : public EvdevInjector {
+ public:
+ EvdevInjectorForTesting(UInput& uinput) {
+ SetUInputForTesting(&uinput);
+ }
+ const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
+};
+
+class VirtualTouchpadForTesting : public VirtualTouchpad {
+ public:
+ VirtualTouchpadForTesting(EvdevInjector& injector) {
+ SetEvdevInjectorForTesting(&injector);
+ }
+};
+
+void DumpDifference(const char* expect, const char* actual) {
+ printf(" common: ");
+ while (*expect && *expect == *actual) {
+ putchar(*expect);
+ ++expect;
+ ++actual;
+ }
+ printf("\n expect: %s\n", expect);
+ printf(" actual: %s\n", actual);
+}
+
+} // anonymous namespace
+
+class VirtualTouchpadTest : public testing::Test {
+};
+
+TEST_F(VirtualTouchpadTest, Goodness) {
+ UInputRecorder expect;
+ UInputRecorder record;
+ EvdevInjectorForTesting injector(record);
+ VirtualTouchpadForTesting touchpad(injector);
+
+ const int initialization_status = touchpad.Initialize();
+ EXPECT_EQ(0, initialization_status);
+
+ // Check some aspects of uinput_user_dev.
+ const uinput_user_dev* uidev = injector.GetUiDev();
+ for (int i = 0; i < ABS_CNT; ++i) {
+ EXPECT_EQ(0, uidev->absmin[i]);
+ EXPECT_EQ(0, uidev->absfuzz[i]);
+ EXPECT_EQ(0, uidev->absflat[i]);
+ if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y && i != ABS_MT_SLOT) {
+ EXPECT_EQ(0, uidev->absmax[i]);
+ }
+ }
+ const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
+ const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
+ const int32_t slots = uidev->absmax[ABS_MT_SLOT];
+
+ // Check the system calls performed by initialization.
+ // From ConfigureBegin():
+ expect.Open();
+ // From ConfigureInputProperty(INPUT_PROP_DIRECT):
+ expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ // From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
+ expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
+ expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
+ expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+ // From ConfigureAbsSlots(kSlots):
+ expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
+ // From ConfigureKey(BTN_TOUCH):
+ expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
+ expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
+ // From ConfigureEnd():
+ expect.Write(uidev, sizeof (uinput_user_dev));
+ expect.IoctlVoid(UI_DEV_CREATE);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+
+ expect.Reset();
+ record.Reset();
+ int touch_status = touchpad.Touch(0, 0, 0);
+ EXPECT_EQ(0, touch_status);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
+ expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+
+ expect.Reset();
+ record.Reset();
+ touch_status = touchpad.Touch(0.25f, 0.75f, 0.5f);
+ EXPECT_EQ(0, touch_status);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height);
+ expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS);
+ expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+
+ expect.Reset();
+ record.Reset();
+ touch_status = touchpad.Touch(1.0f, 1.0f, 1.0f);
+ EXPECT_EQ(0, touch_status);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, width);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, height);
+ expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+
+ expect.Reset();
+ record.Reset();
+ touch_status = touchpad.Touch(0.25f, 0.75f, -0.01f);
+ EXPECT_EQ(0, touch_status);
+ expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE);
+ expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+
+ expect.Reset();
+ record.Reset();
+}
+
+TEST_F(VirtualTouchpadTest, Badness) {
+ UInputRecorder expect;
+ UInputRecorder record;
+ EvdevInjectorForTesting injector(record);
+ VirtualTouchpadForTesting touchpad(injector);
+
+ // Touch before initialization should return an error,
+ // and should not result in any system calls.
+ expect.Reset();
+ record.Reset();
+ int touch_status = touchpad.Touch(0.25f, 0.75f, -0.01f);
+ EXPECT_NE(0, touch_status);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+
+ expect.Reset();
+ record.Reset();
+ touchpad.Initialize();
+
+ // Repeated initialization should return an error,
+ // and should not result in any system calls.
+ expect.Reset();
+ record.Reset();
+ const int initialization_status = touchpad.Initialize();
+ EXPECT_NE(0, initialization_status);
+ EXPECT_EQ(expect.GetString(), record.GetString());
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc
new file mode 100644
index 0000000..b4f9f00
--- /dev/null
+++ b/services/vr/virtual_touchpad/virtual_touchpad.rc
@@ -0,0 +1,5 @@
+service virtual_touchpad /system/bin/virtual_touchpad
+ class core
+ user system
+ group system input
+ cpuset /system
diff --git a/services/vr/vr_manager/Android.mk b/services/vr/vr_manager/Android.mk
new file mode 100644
index 0000000..54b1c1a
--- /dev/null
+++ b/services/vr/vr_manager/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+src_files := \
+ vr_manager.cpp \
+
+inc_files := \
+ frameworks/native/include/vr/vr_manager
+
+static_libs := \
+ libutils \
+ libbinder \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src_files)
+LOCAL_C_INCLUDES := $(inc_files)
+LOCAL_CFLAGS += -Wall
+LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Wunused
+LOCAL_CFLAGS += -Wunreachable-code
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(inc_files)
+#LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_MODULE := libvr_manager
+include $(BUILD_STATIC_LIBRARY)
diff --git a/services/vr/vr_manager/vr_manager.cpp b/services/vr/vr_manager/vr_manager.cpp
new file mode 100644
index 0000000..a31fcb7
--- /dev/null
+++ b/services/vr/vr_manager/vr_manager.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VrManager"
+#include <utils/Log.h>
+
+#include <vr/vr_manager/vr_manager.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+// Must be kept in sync with interface defined in IVrStateCallbacks.aidl.
+
+class BpVrStateCallbacks : public BpInterface<IVrStateCallbacks> {
+ public:
+ explicit BpVrStateCallbacks(const sp<IBinder>& impl)
+ : BpInterface<IVrStateCallbacks>(impl) {}
+
+ void onVrStateChanged(bool enabled) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IVrStateCallbacks::getInterfaceDescriptor());
+ data.writeBool(enabled);
+ remote()->transact(ON_VR_STATE_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(VrStateCallbacks, "android.service.vr.IVrStateCallbacks");
+
+status_t BnVrStateCallbacks::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case ON_VR_STATE_CHANGED: {
+ CHECK_INTERFACE(IVrStateCallbacks, data, reply);
+ onVrStateChanged(data.readBool());
+ return OK;
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// Must be kept in sync with interface defined in IVrManager.aidl.
+
+class BpVrManager : public BpInterface<IVrManager> {
+ public:
+ explicit BpVrManager(const sp<IBinder>& impl)
+ : BpInterface<IVrManager>(impl) {}
+
+ void registerListener(const sp<IVrStateCallbacks>& cb) override {
+ Parcel data;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(cb));
+ remote()->transact(REGISTER_LISTENER, data, NULL);
+ }
+
+ void unregisterListener(const sp<IVrStateCallbacks>& cb) override {
+ Parcel data;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(cb));
+ remote()->transact(UNREGISTER_LISTENER, data, NULL);
+ }
+
+ bool getVrModeState() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ remote()->transact(GET_VR_MODE_STATE, data, &reply);
+ int32_t ret = reply.readExceptionCode();
+ if (ret != 0) {
+ return false;
+ }
+ return reply.readBool();
+ }
+};
+
+IMPLEMENT_META_INTERFACE(VrManager, "android.service.vr.IVrManager");
+
+class BpVrDisplayStateService : public BpInterface<IVrDisplayStateService> {
+ public:
+ explicit BpVrDisplayStateService(const sp<IBinder>& impl)
+ : BpInterface<IVrDisplayStateService>(impl) {}
+
+ void displayAvailable(bool available) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IVrDisplayStateService::getInterfaceDescriptor());
+ data.writeBool(available);
+ remote()->transact(static_cast<uint32_t>(
+ VrDisplayStateTransaction::ON_DISPLAY_STATE_CHANGED),
+ data, &reply);
+ }
+};
+
+status_t BnVrDisplayStateService::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ switch (static_cast<VrDisplayStateTransaction>(code)) {
+ case VrDisplayStateTransaction::ON_DISPLAY_STATE_CHANGED:
+ CHECK_INTERFACE(IVrDisplayStateService, data, reply);
+ displayAvailable(data.readBool());
+ return OK;
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+IMPLEMENT_META_INTERFACE(VrDisplayStateService,
+ "android.service.vr.IVrDisplayStateService");
+
+} // namespace android
diff --git a/services/vr/vr_window_manager/Android.bp b/services/vr/vr_window_manager/Android.bp
deleted file mode 100644
index c30219f..0000000
--- a/services/vr/vr_window_manager/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-subdirs = [
- "composer/1.0",
-]
-
-cc_library_shared {
- name: "libvrhwc",
-
- srcs: [
- "composer/impl/sync_timeline.cpp",
- "composer/impl/vr_hwc.cpp",
- "composer/impl/vr_composer_client.cpp",
- ],
-
- static_libs: [
- "libhwcomposer-client",
- ],
-
- shared_libs: [
- "android.dvr.composer@1.0",
- "android.hardware.graphics.composer@2.1",
- "libbase",
- "libcutils",
- "libfmq",
- "libhardware",
- "libhidlbase",
- "libhidltransport",
- "liblog",
- "libsync",
- "libui",
- "libutils",
- ],
-
- // Access to software sync timeline.
- include_dirs: [ "system/core/libsync" ],
-
- cflags: [
- "-DLOG_TAG=\"vrhwc\"",
- ],
-}
diff --git a/services/vr/vr_window_manager/Android.mk b/services/vr/vr_window_manager/Android.mk
new file mode 100644
index 0000000..adce4b9
--- /dev/null
+++ b/services/vr/vr_window_manager/Android.mk
@@ -0,0 +1,100 @@
+# 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)
+
+src := \
+ vr_window_manager_jni.cpp \
+ application.cpp \
+ controller_mesh.cpp \
+ elbow_model.cpp \
+ hwc_callback.cpp \
+ reticle.cpp \
+ render_thread.cpp \
+ shell_view.cpp \
+ surface_flinger_view.cpp \
+ texture.cpp \
+ ../virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl \
+
+static_libs := \
+ libdisplay \
+ libbufferhub \
+ libbufferhubqueue \
+ libeds \
+ libdvrgraphics \
+ libdvrcommon \
+ libhwcomposer-client \
+ libsensor \
+ libperformance \
+ libpdx_default_transport \
+ libchrome \
+ libcutils \
+
+shared_libs := \
+ android.dvr.composer@1.0 \
+ android.hardware.graphics.composer@2.1 \
+ libvrhwc \
+ libandroid \
+ libbase \
+ libbinder \
+ libinput \
+ libhardware \
+ libsync \
+ libutils \
+ libgui \
+ libEGL \
+ libGLESv2 \
+ libvulkan \
+ libsync \
+ libui \
+ libhidlbase \
+ libhidltransport
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_C_INCLUDES := hardware/qcom/display/msm8996/libgralloc
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libevent
+LOCAL_SHARED_LIBRARIES += libgvr
+LOCAL_STATIC_LIBRARIES += libgvr_ext
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DLOG_TAG=\"VrWindowManager\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := libvr_window_manager_jni
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := 64
+LOCAL_CXX_STL := libc++_static
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := VrWindowManager
+
+# We need to be priveleged to run as the system user, which is necessary for
+# getting hmd input events and doing input injection.
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_JNI_SHARED_LIBRARIES := libvr_window_manager_jni
+LOCAL_STATIC_JAVA_AAR_LIBRARIES := gvr_common_library_aar
+# gvr_common_library_aar depends on nano version of libprotobuf
+LOCAL_STATIC_JAVA_LIBRARIES := libprotobuf-java-nano
+# Make sure that libgvr's resources are loaded
+LOCAL_AAPT_FLAGS += --auto-add-overlay
+LOCAL_AAPT_FLAGS += --extra-packages com.google.vr.cardboard
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+include $(BUILD_PACKAGE)
diff --git a/services/vr/vr_window_manager/AndroidManifest.xml b/services/vr/vr_window_manager/AndroidManifest.xml
new file mode 100644
index 0000000..5cc4b5c
--- /dev/null
+++ b/services/vr/vr_window_manager/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.vr.windowmanager"
+ coreApp="true"
+ android:sharedUserId="android.uid.system"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <!-- The GVR SDK requires API 19+ and OpenGL ES 2+. -->
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24" />
+ <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+ <!-- We need the DIAGNOSTIC permission to read HMD button events. DIAGNOSTIC
+ ensures our process runs with the "input" group, so we can access
+ /dev/input. See frameworks/base/data/etc/platform.xml for the permission
+ to group mappings.
+
+ TODO(steventhomas): We shouldn't use this DIAGNOSTIC permission. Figure
+ out the correct way to get access to the HMD buttons.
+ Bug: b/33253485. -->
+ <uses-permission android:name="android.permission.DIAGNOSTIC"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+ <application
+ android:label="vr_window_manager"
+ android:theme="@style/AppStyle">
+ <service android:name=".VrWindowManagerService" />
+ <receiver android:name="com.google.vr.windowmanager.BootCompletedReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
new file mode 100644
index 0000000..f84a0d1
--- /dev/null
+++ b/services/vr/vr_window_manager/application.cpp
@@ -0,0 +1,312 @@
+#include "application.h"
+
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <gui/ISurfaceComposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+Application::Application()
+ : controller_api_status_logged_(false),
+ controller_connection_state_logged_(false) {}
+
+Application::~Application() {
+}
+
+int Application::Initialize(JNIEnv* env, jobject app_context,
+ jobject class_loader) {
+ dvrSetCpuPartition(0, "/application/performance");
+
+ bool is_right_handed = true; // TODO: retrieve setting from system
+ elbow_model_.Enable(ElbowModel::kDefaultNeckPosition, is_right_handed);
+ last_frame_time_ = std::chrono::system_clock::now();
+
+ java_env_ = env;
+ app_context_ = app_context;
+ class_loader_ = class_loader;
+
+ return 0;
+}
+
+int Application::AllocateResources() {
+ int surface_width = 0, surface_height = 0;
+ DvrLensInfo lens_info = {};
+ GLuint texture_id = 0;
+ GLenum texture_target = 0;
+ std::vector<DvrSurfaceParameter> surface_params = {
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+ DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &lens_info.inter_lens_meters),
+ DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, &lens_info.left_fov),
+ DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, &lens_info.right_fov),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_TYPE, &texture_target),
+ DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_ID, &texture_id),
+ DVR_SURFACE_PARAMETER_IN(VISIBLE, 0),
+ DVR_SURFACE_PARAMETER_IN(Z_ORDER, 1),
+ DVR_SURFACE_PARAMETER_IN(GEOMETRY, DVR_SURFACE_GEOMETRY_SINGLE),
+ DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, 0),
+ DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, 0),
+ DVR_SURFACE_PARAMETER_LIST_END,
+ };
+
+ int ret = dvrGraphicsContextCreate(surface_params.data(), &graphics_context_);
+ if (ret)
+ return ret;
+
+ GLuint fbo = 0;
+ GLuint depth_stencil_buffer = 0;
+ GLuint samples = 1;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target, texture_id, 0, samples);
+
+ glGenRenderbuffers(1, &depth_stencil_buffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
+ GL_DEPTH_COMPONENT24, surface_width,
+ surface_height);
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer);
+
+ ALOGI("Surface size=%dx%d", surface_width, surface_height);
+ pose_client_ = dvrPoseCreate();
+ if (!pose_client_)
+ return 1;
+
+ vec2i eye_size(surface_width / 2, surface_height);
+
+ eye_viewport_[0] = Range2i::FromSize(vec2i(0, 0), eye_size);
+ eye_viewport_[1] = Range2i::FromSize(vec2i(surface_width / 2, 0), eye_size);
+
+ eye_from_head_[0] = Eigen::Translation3f(
+ vec3(lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
+ eye_from_head_[1] = Eigen::Translation3f(
+ vec3(-lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
+
+ fov_[0] = FieldOfView(lens_info.left_fov[0], lens_info.left_fov[1],
+ lens_info.left_fov[2], lens_info.left_fov[3]);
+ fov_[1] = FieldOfView(lens_info.right_fov[0], lens_info.right_fov[1],
+ lens_info.right_fov[2], lens_info.right_fov[3]);
+
+ gvr_context_ = gvr::GvrApi::Create(java_env_, app_context_, class_loader_);
+ if (gvr_context_ == nullptr) {
+ ALOGE("Gvr context creation failed");
+ return 1;
+ }
+
+ int32_t options = gvr_controller_get_default_options();
+ options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL;
+
+ controller_.reset(new gvr::ControllerApi);
+ if (!controller_->Init(java_env_, app_context_, class_loader_, options,
+ gvr_context_->cobj())) {
+ ALOGE("Gvr controller init failed");
+ return 1;
+ }
+
+ controller_state_.reset(new gvr::ControllerState);
+
+ return 0;
+}
+
+void Application::DeallocateResources() {
+ gvr_context_.reset();
+ controller_.reset();
+ controller_state_.reset();
+
+ if (graphics_context_)
+ dvrGraphicsContextDestroy(graphics_context_);
+
+ if (pose_client_)
+ dvrPoseDestroy(pose_client_);
+
+ initialized_ = false;
+}
+
+void Application::ProcessTasks(const std::vector<MainThreadTask>& tasks) {
+ for (auto task : tasks) {
+ switch (task) {
+ case MainThreadTask::EnableDebugMode:
+ if (!debug_mode_) {
+ debug_mode_ = true;
+ SetVisibility(debug_mode_);
+ }
+ break;
+ case MainThreadTask::DisableDebugMode:
+ if (debug_mode_) {
+ debug_mode_ = false;
+ SetVisibility(debug_mode_);
+ }
+ break;
+ case MainThreadTask::EnteringVrMode:
+ if (!initialized_)
+ AllocateResources();
+ break;
+ case MainThreadTask::ExitingVrMode:
+ if (initialized_)
+ DeallocateResources();
+ break;
+ case MainThreadTask::Show:
+ if (!is_visible_)
+ SetVisibility(true);
+ break;
+ }
+ }
+}
+
+void Application::DrawFrame() {
+ // Thread should block if we are invisible or not fully initialized.
+ std::unique_lock<std::mutex> lock(mutex_);
+ wake_up_init_and_render_.wait(lock, [this]() {
+ return is_visible_ && initialized_ || !main_thread_tasks_.empty();
+ });
+
+ // Process main thread tasks if there are any.
+ std::vector<MainThreadTask> tasks;
+ tasks.swap(main_thread_tasks_);
+ lock.unlock();
+
+ if (!tasks.empty())
+ ProcessTasks(tasks);
+
+ if (!initialized_)
+ return;
+
+ // TODO(steventhomas): If we're not visible, block until we are. For now we
+ // throttle by calling dvrGraphicsWaitNextFrame.
+ DvrFrameSchedule schedule;
+ dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
+
+ OnDrawFrame();
+
+ if (is_visible_) {
+ ProcessControllerInput();
+
+ DvrPoseAsync pose;
+ dvrPoseGet(pose_client_, schedule.vsync_count, &pose);
+ last_pose_ = Posef(
+ quat(pose.orientation[3], pose.orientation[0], pose.orientation[1],
+ pose.orientation[2]),
+ vec3(pose.translation[0], pose.translation[1], pose.translation[2]));
+
+ std::chrono::time_point<std::chrono::system_clock> now =
+ std::chrono::system_clock::now();
+ double delta =
+ std::chrono::duration<double>(now - last_frame_time_).count();
+ last_frame_time_ = now;
+
+ if (delta > 1.0f)
+ delta = 0.05f;
+
+ fade_value_ += delta / 0.25f;
+ if (fade_value_ > 1.0f)
+ fade_value_ = 1.0f;
+
+ quat controller_quat(controller_orientation_.qw, controller_orientation_.qx,
+ controller_orientation_.qy, controller_orientation_.qz);
+ controller_position_ = elbow_model_.Update(
+ delta, last_pose_.GetRotation(), controller_quat, false);
+
+ dvrBeginRenderFrameEds(graphics_context_, pose.orientation,
+ pose.translation);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ mat4 head_matrix = last_pose_.GetObjectFromReferenceMatrix();
+ glViewport(eye_viewport_[kLeftEye].GetMinPoint()[0],
+ eye_viewport_[kLeftEye].GetMinPoint()[1],
+ eye_viewport_[kLeftEye].GetSize()[0],
+ eye_viewport_[kLeftEye].GetSize()[1]);
+ DrawEye(kLeftEye, fov_[kLeftEye].GetProjectionMatrix(0.1f, 500.0f),
+ eye_from_head_[kLeftEye], head_matrix);
+
+ glViewport(eye_viewport_[kRightEye].GetMinPoint()[0],
+ eye_viewport_[kRightEye].GetMinPoint()[1],
+ eye_viewport_[kRightEye].GetSize()[0],
+ eye_viewport_[kRightEye].GetSize()[1]);
+ DrawEye(kRightEye, fov_[kRightEye].GetProjectionMatrix(0.1f, 500.0f),
+ eye_from_head_[kRightEye], head_matrix);
+
+ dvrPresent(graphics_context_);
+ }
+}
+
+void Application::ProcessControllerInput() {
+ controller_state_->Update(*controller_);
+ gvr::ControllerApiStatus new_api_status = controller_state_->GetApiStatus();
+ gvr::ControllerConnectionState new_connection_state =
+ controller_state_->GetConnectionState();
+
+ if (!controller_api_status_logged_) {
+ controller_api_status_logged_ = true;
+ ALOGI("Controller api status: %s",
+ gvr::ControllerApi::ToString(new_api_status));
+ } else if (new_api_status != controller_api_status_) {
+ ALOGI("Controller api status changed: %s --> %s",
+ gvr::ControllerApi::ToString(controller_api_status_),
+ gvr::ControllerApi::ToString(new_api_status));
+ }
+
+ if (new_api_status == gvr::kControllerApiOk) {
+ if (!controller_connection_state_logged_) {
+ controller_connection_state_logged_ = true;
+ ALOGI("Controller connection state: %s",
+ gvr::ControllerApi::ToString(new_connection_state));
+ } else if (new_connection_state != controller_connection_state_) {
+ ALOGI("Controller connection state changed: %s --> %s",
+ gvr::ControllerApi::ToString(controller_connection_state_),
+ gvr::ControllerApi::ToString(new_connection_state));
+ }
+ } else {
+ controller_connection_state_logged_ = false;
+ }
+
+ if (new_api_status == gvr::kControllerApiOk)
+ controller_orientation_ = controller_state_->GetOrientation();
+
+ controller_api_status_ = new_api_status;
+ controller_connection_state_ = new_connection_state;
+}
+
+void Application::SetVisibility(bool visible) {
+ bool changed = is_visible_ != visible;
+ if (changed) {
+ is_visible_ = visible;
+ dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
+ if (is_visible_)
+ controller_->Resume();
+ else
+ controller_->Pause();
+ OnVisibilityChanged(is_visible_);
+ }
+}
+
+void Application::OnVisibilityChanged(bool visible) {
+ if (visible) {
+ fade_value_ = 0;
+ // We have been sleeping so to ensure correct deltas, reset the time.
+ last_frame_time_ = std::chrono::system_clock::now();
+ }
+}
+
+void Application::QueueTask(MainThreadTask task) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ main_thread_tasks_.push_back(task);
+ wake_up_init_and_render_.notify_one();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
new file mode 100644
index 0000000..3321682
--- /dev/null
+++ b/services/vr/vr_window_manager/application.h
@@ -0,0 +1,101 @@
+#ifndef VR_WINDOW_MANAGER_APPLICATION_H_
+#define VR_WINDOW_MANAGER_APPLICATION_H_
+
+#include <jni.h>
+#include <memory>
+#include <private/dvr/types.h>
+#include <stdint.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_controller.h>
+
+#include <chrono>
+#include <mutex>
+
+#include "elbow_model.h"
+
+struct DvrGraphicsContext;
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Application {
+ public:
+ Application();
+ virtual ~Application();
+
+ virtual int Initialize(JNIEnv* env, jobject app_context,
+ jobject class_loader);
+
+ virtual int AllocateResources();
+ virtual void DeallocateResources();
+
+ void DrawFrame();
+
+ protected:
+ enum class MainThreadTask {
+ EnteringVrMode,
+ ExitingVrMode,
+ EnableDebugMode,
+ DisableDebugMode,
+ Show,
+ };
+
+ virtual void OnDrawFrame() = 0;
+ virtual void DrawEye(EyeType eye, const mat4& perspective,
+ const mat4& eye_matrix, const mat4& head_matrix) = 0;
+
+ void SetVisibility(bool visible);
+ virtual void OnVisibilityChanged(bool visible);
+
+ void ProcessControllerInput();
+
+ void ProcessTasks(const std::vector<MainThreadTask>& tasks);
+
+ void QueueTask(MainThreadTask task);
+
+ DvrGraphicsContext* graphics_context_ = nullptr;
+ DvrPose* pose_client_ = nullptr;
+
+ Range2i eye_viewport_[2];
+ mat4 eye_from_head_[2];
+ FieldOfView fov_[2];
+ Posef last_pose_;
+
+ std::unique_ptr<gvr::GvrApi> gvr_context_;
+ std::unique_ptr<gvr::ControllerApi> controller_;
+ std::unique_ptr<gvr::ControllerState> controller_state_;
+ gvr::ControllerApiStatus controller_api_status_;
+ gvr::ControllerConnectionState controller_connection_state_;
+ gvr_quatf controller_orientation_;
+ bool controller_api_status_logged_;
+ bool controller_connection_state_logged_;
+
+ bool is_visible_ = false;
+ std::chrono::time_point<std::chrono::system_clock> visibility_button_press_;
+ bool debug_mode_ = false;
+
+ std::chrono::time_point<std::chrono::system_clock> last_frame_time_;
+ vec3 controller_position_;
+ ElbowModel elbow_model_;
+
+ float fade_value_ = 0;
+
+ std::mutex mutex_;
+ std::condition_variable wake_up_init_and_render_;
+ bool initialized_ = false;
+ std::vector<MainThreadTask> main_thread_tasks_;
+
+ // Java Resources.
+ JNIEnv* java_env_;
+ jobject app_context_;
+ jobject class_loader_;
+
+ Application(const Application&) = delete;
+ void operator=(const Application&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_APPLICATION_H_
diff --git a/services/vr/vr_window_manager/composer/1.0/Android.bp b/services/vr/vr_window_manager/composer/1.0/Android.bp
index f69481f..e3e47ff 100644
--- a/services/vr/vr_window_manager/composer/1.0/Android.bp
+++ b/services/vr/vr_window_manager/composer/1.0/Android.bp
@@ -6,9 +6,13 @@
cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hidl:system/libhidl/transport -randroid.hardware:hardware/interfaces/ -randroid.dvr:frameworks/native/services/vr/vr_window_manager android.dvr.composer@1.0",
srcs: [
"IVrComposerClient.hal",
+ "IVrComposerView.hal",
+ "IVrComposerCallback.hal",
],
out: [
"android/dvr/composer/1.0/VrComposerClientAll.cpp",
+ "android/dvr/composer/1.0/VrComposerViewAll.cpp",
+ "android/dvr/composer/1.0/VrComposerCallbackAll.cpp",
],
}
@@ -18,6 +22,8 @@
cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hidl:system/libhidl/transport -randroid.hardware:hardware/interfaces/ -randroid.dvr:frameworks/native/services/vr/vr_window_manager android.dvr.composer@1.0",
srcs: [
"IVrComposerClient.hal",
+ "IVrComposerView.hal",
+ "IVrComposerCallback.hal",
],
out: [
"android/dvr/composer/1.0/IVrComposerClient.h",
@@ -25,6 +31,18 @@
"android/dvr/composer/1.0/BnVrComposerClient.h",
"android/dvr/composer/1.0/BpVrComposerClient.h",
"android/dvr/composer/1.0/BsVrComposerClient.h",
+
+ "android/dvr/composer/1.0/IVrComposerView.h",
+ "android/dvr/composer/1.0/IHwVrComposerView.h",
+ "android/dvr/composer/1.0/BnVrComposerView.h",
+ "android/dvr/composer/1.0/BpVrComposerView.h",
+ "android/dvr/composer/1.0/BsVrComposerView.h",
+
+ "android/dvr/composer/1.0/IVrComposerCallback.h",
+ "android/dvr/composer/1.0/IHwVrComposerCallback.h",
+ "android/dvr/composer/1.0/BnVrComposerCallback.h",
+ "android/dvr/composer/1.0/BpVrComposerCallback.h",
+ "android/dvr/composer/1.0/BsVrComposerCallback.h",
],
}
diff --git a/services/vr/vr_window_manager/composer/1.0/IVrComposerCallback.hal b/services/vr/vr_window_manager/composer/1.0/IVrComposerCallback.hal
new file mode 100644
index 0000000..6e7255e
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/IVrComposerCallback.hal
@@ -0,0 +1,18 @@
+package android.dvr.composer@1.0;
+
+import android.hardware.graphics.composer@2.1::IComposerClient;
+
+interface IVrComposerCallback {
+ struct Layer {
+ handle buffer;
+ handle fence;
+ android.hardware.graphics.composer@2.1::IComposerClient.Rect display_frame;
+ android.hardware.graphics.composer@2.1::IComposerClient.FRect crop;
+ android.hardware.graphics.composer@2.1::IComposerClient.BlendMode blend_mode;
+ float alpha;
+ uint32_t type;
+ uint32_t app_id;
+ };
+
+ onNewFrame(vec<Layer> frame);
+};
diff --git a/services/vr/vr_window_manager/composer/1.0/IVrComposerView.hal b/services/vr/vr_window_manager/composer/1.0/IVrComposerView.hal
new file mode 100644
index 0000000..e16131a
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/IVrComposerView.hal
@@ -0,0 +1,9 @@
+package android.dvr.composer@1.0;
+
+import IVrComposerCallback;
+
+interface IVrComposerView {
+ registerCallback(IVrComposerCallback callback);
+
+ releaseFrame();
+};
diff --git a/services/vr/vr_window_manager/composer/Android.bp b/services/vr/vr_window_manager/composer/Android.bp
new file mode 100644
index 0000000..ad63c7f
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/Android.bp
@@ -0,0 +1,47 @@
+subdirs = [
+ "1.0",
+]
+
+cc_library_shared {
+ name: "libvrhwc",
+
+ srcs: [
+ "impl/sync_timeline.cpp",
+ "impl/vr_composer_view.cpp",
+ "impl/vr_hwc.cpp",
+ "impl/vr_composer_client.cpp",
+ ],
+
+ static_libs: [
+ "libhwcomposer-client",
+ ],
+
+ shared_libs: [
+ "android.dvr.composer@1.0",
+ "android.hardware.graphics.composer@2.1",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ export_include_dirs: ["."],
+
+ include_dirs: [
+ // Access to software sync timeline.
+ "system/core/libsync",
+
+ // Access to internal gralloc implementation.
+ "hardware/qcom/display/msm8996/libgralloc",
+ ],
+
+ cflags: [
+ "-DLOG_TAG=\"vrhwc\"",
+ ],
+}
diff --git a/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp b/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp
index aa55aed..e63ed26 100644
--- a/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp
+++ b/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "composer/impl/sync_timeline.h"
+#include "sync_timeline.h"
#include <sys/cdefs.h>
#include <sw_sync.h>
diff --git a/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp b/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp
new file mode 100644
index 0000000..5f8168d
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp
@@ -0,0 +1,80 @@
+#include "vr_composer_view.h"
+
+namespace android {
+namespace dvr {
+
+VrComposerView::VrComposerView() : composer_view_(nullptr) {}
+
+VrComposerView::~VrComposerView() {
+ composer_view_->UnregisterObserver(this);
+}
+
+void VrComposerView::Initialize(ComposerView* composer_view) {
+ composer_view_ = composer_view;
+ composer_view_->RegisterObserver(this);
+}
+
+Return<void> VrComposerView::registerCallback(
+ const sp<IVrComposerCallback>& callback) {
+ callback_ = callback;
+ return Void();
+}
+
+Return<void> VrComposerView::releaseFrame() {
+ composer_view_->ReleaseFrame();
+ return Void();
+}
+
+void VrComposerView::OnNewFrame(const ComposerView::Frame& frame) {
+ if (!callback_.get()) {
+ releaseFrame();
+ return;
+ }
+
+ std::vector<IVrComposerCallback::Layer> layers;
+ std::vector<native_handle_t*> fences;
+ for (size_t i = 0; i < frame.size(); ++i) {
+ native_handle_t* fence;
+ if (frame[i].fence->isValid()) {
+ fence = native_handle_create(1, 0);
+ fence->data[0] = frame[i].fence->dup();
+ } else {
+ fence = native_handle_create(0, 0);
+ }
+ fences.push_back(fence);
+
+ layers.push_back(IVrComposerCallback::Layer{
+ .buffer = hidl_handle(frame[i].buffer->getNativeBuffer()->handle),
+ .fence = hidl_handle(fence),
+ .display_frame = frame[i].display_frame,
+ .crop = frame[i].crop,
+ .blend_mode= frame[i].blend_mode,
+ .alpha = frame[i].alpha,
+ .type = frame[i].type,
+ .app_id = frame[i].app_id,
+ });
+ }
+
+ auto status =
+ callback_->onNewFrame(hidl_vec<IVrComposerCallback::Layer>(layers));
+ if (!status.isOk()) {
+ ALOGE("Failed to send onNewFrame: %s", status.description().c_str());
+ releaseFrame();
+ }
+
+ for (size_t i = 0; i < fences.size(); ++i) {
+ native_handle_close(fences[i]);
+ native_handle_delete(fences[i]);
+ }
+}
+
+VrComposerView* GetVrComposerViewFromIVrComposerView(IVrComposerView* view) {
+ return static_cast<VrComposerView*>(view);
+}
+
+IVrComposerView* HIDL_FETCH_IVrComposerView(const char* name) {
+ return new VrComposerView();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/composer/impl/vr_composer_view.h b/services/vr/vr_window_manager/composer/impl/vr_composer_view.h
new file mode 100644
index 0000000..133bbc8
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_view.h
@@ -0,0 +1,42 @@
+#ifndef VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
+#define VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
+
+#include <android/dvr/composer/1.0/IVrComposerCallback.h>
+#include <android/dvr/composer/1.0/IVrComposerView.h>
+
+#include "vr_hwc.h"
+
+namespace android {
+namespace dvr {
+
+using composer::V1_0::IVrComposerView;
+using composer::V1_0::IVrComposerCallback;
+
+class VrComposerView : public IVrComposerView, public ComposerView::Observer {
+ public:
+ VrComposerView();
+ ~VrComposerView() override;
+
+ void Initialize(ComposerView* composer_view);
+
+ // IVrComposerView
+ Return<void> registerCallback(const sp<IVrComposerCallback>& callback)
+ override;
+ Return<void> releaseFrame() override;
+
+ // ComposerView::Observer
+ void OnNewFrame(const ComposerView::Frame& frame) override;
+
+ private:
+ ComposerView* composer_view_;
+ sp<IVrComposerCallback> callback_;
+};
+
+VrComposerView* GetVrComposerViewFromIVrComposerView(IVrComposerView* view);
+
+IVrComposerView* HIDL_FETCH_IVrComposerView(const char* name);
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
index d64a99a..53c7d8e 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "composer/impl/vr_hwc.h"
+#include "vr_hwc.h"
+#include <gralloc_priv.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferMapper.h>
#include <mutex>
-#include "composer/impl/sync_timeline.h"
-#include "composer/impl/vr_composer_client.h"
+#include "sync_timeline.h"
+#include "vr_composer_client.h"
using namespace android::hardware::graphics::common::V1_0;
using namespace android::hardware::graphics::composer::V2_1;
@@ -42,6 +43,21 @@
const Display kDefaultDisplayId = 1;
const Config kDefaultConfigId = 1;
+sp<GraphicBuffer> GetBufferFromHandle(const native_handle_t* handle) {
+ // TODO(dnicoara): Fix this once gralloc1 is available.
+ private_handle_t* private_handle = private_handle_t::dynamicCast(handle);
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ private_handle->width, private_handle->height, private_handle->format, 1,
+ GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE,
+ private_handle->width, native_handle_clone(handle), true);
+ if (GraphicBufferMapper::get().registerBuffer(buffer.get()) != OK) {
+ ALOGE("Failed to register buffer");
+ return nullptr;
+ }
+
+ return buffer;
+}
+
} // namespace
HwcDisplay::HwcDisplay() {}
@@ -52,17 +68,8 @@
bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
base::unique_fd fence) {
- // OK, so this is where we cheat a lot because we don't have direct access to
- // buffer information. Everything is hardcoded, but once gralloc1 is available
- // we should use it to read buffer properties from the handle.
- buffer_ = new GraphicBuffer(
- 1080, 1920, PIXEL_FORMAT_RGBA_8888, 1,
- GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE, 1088,
- native_handle_clone(handle), true);
- if (GraphicBufferMapper::get().registerBuffer(buffer_.get()) != OK) {
- ALOGE("Failed to set client target");
- return false;
- }
+ if (handle)
+ buffer_ = GetBufferFromHandle(handle);
fence_ = new Fence(fence.release());
return true;
@@ -95,9 +102,43 @@
void HwcDisplay::GetChangedCompositionTypes(
std::vector<Layer>* layer_ids,
std::vector<IComposerClient::Composition>* types) {
- for (const auto& layer : layers_) {
- layer_ids->push_back(layer.id);
- types->push_back(IComposerClient::Composition::CLIENT);
+ std::sort(layers_.begin(), layers_.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.z_order < rhs.z_order;
+ });
+
+ int first_client_layer = -1, last_client_layer = -1;
+ for (size_t i = 0; i < layers_.size(); ++i) {
+ switch (layers_[i].composition_type) {
+ case IComposerClient::Composition::SOLID_COLOR:
+ case IComposerClient::Composition::CURSOR:
+ case IComposerClient::Composition::SIDEBAND:
+ if (first_client_layer < 0)
+ first_client_layer = i;
+
+ last_client_layer = i;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < layers_.size(); ++i) {
+ if (i >= first_client_layer && i <= last_client_layer) {
+ if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) {
+ layer_ids->push_back(layers_[i].id);
+ types->push_back(IComposerClient::Composition::CLIENT);
+ layers_[i].composition_type = IComposerClient::Composition::CLIENT;
+ }
+
+ continue;
+ }
+
+ if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
+ layer_ids->push_back(layers_[i].id);
+ types->push_back(IComposerClient::Composition::DEVICE);
+ layers_[i].composition_type = IComposerClient::Composition::DEVICE;
+ }
}
}
@@ -107,26 +148,48 @@
// the current frame.
fence_time_++;
- // TODO(dnicoara): Send the actual layers when we process layers as overlays.
- ComposerView::ComposerLayer layer = {
- .buffer = buffer_,
- .fence = fence_.get() ? fence_ : new Fence(-1),
- .display_frame = {0, 0, 1080, 1920},
- .crop = {0.0f, 0.0f, 1080.0f, 1920.0f},
- .blend_mode = IComposerClient::BlendMode::NONE,
- };
- return std::vector<ComposerView::ComposerLayer>(1, layer);
+ bool queued_client_target = false;
+ std::vector<ComposerView::ComposerLayer> frame;
+ for (const auto& layer : layers_) {
+ if (layer.composition_type == IComposerClient::Composition::CLIENT) {
+ if (!queued_client_target) {
+ ComposerView::ComposerLayer client_target_layer = {
+ .buffer = buffer_,
+ .fence = fence_.get() ? fence_ : new Fence(-1),
+ .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
+ static_cast<int32_t>(buffer_->getHeight())},
+ .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
+ static_cast<float>(buffer_->getHeight())},
+ .blend_mode = IComposerClient::BlendMode::NONE,
+ };
+
+ frame.push_back(client_target_layer);
+ queued_client_target = true;
+ }
+ } else {
+ frame.push_back(layer.info);
+ }
+ }
+
+ return frame;
}
-void HwcDisplay::GetReleaseFences(std::vector<Layer>* layer_ids,
+void HwcDisplay::GetReleaseFences(int* present_fence,
+ std::vector<Layer>* layer_ids,
std::vector<int>* fences) {
+ *present_fence = hwc_timeline_.CreateFence(fence_time_);
for (const auto& layer : layers_) {
layer_ids->push_back(layer.id);
fences->push_back(hwc_timeline_.CreateFence(fence_time_));
}
}
-void HwcDisplay::ReleaseFrame() { hwc_timeline_.IncrementTimeline(); }
+void HwcDisplay::ReleaseFrame() {
+ hwc_timeline_.IncrementTimeline();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VrHwcClient
VrHwc::VrHwc() {}
@@ -212,10 +275,10 @@
switch (attribute) {
case IComposerClient::Attribute::WIDTH:
- *outValue = 1080;
+ *outValue = 1920;
break;
case IComposerClient::Attribute::HEIGHT:
- *outValue = 1920;
+ *outValue = 1080;
break;
case IComposerClient::Attribute::VSYNC_PERIOD:
*outValue = 1000 * 1000 * 1000 / 30; // 30fps
@@ -357,14 +420,18 @@
return Error::BAD_DISPLAY;
}
- std::lock_guard<std::mutex> guard(mutex_);
+ std::vector<ComposerView::ComposerLayer> frame;
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ frame = display_.GetFrame();
+ display_.GetReleaseFences(outPresentFence, outLayers, outReleaseFences);
+ }
if (observer_)
- observer_->OnNewFrame(display_.GetFrame());
+ observer_->OnNewFrame(frame);
else
- display_.ReleaseFrame();
+ ReleaseFrame();
- display_.GetReleaseFences(outLayers, outReleaseFences);
return Error::NONE;
}
@@ -380,6 +447,12 @@
base::unique_fd fence(acquireFence);
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->info.buffer = GetBufferFromHandle(buffer);
+ hwc_layer->info.fence = new Fence(fence.release());
+
return Error::NONE;
}
@@ -393,6 +466,12 @@
Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->info.blend_mode =
+ static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
+
return Error::NONE;
}
@@ -407,6 +486,11 @@
int32_t type) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
+
return Error::NONE;
}
@@ -421,12 +505,23 @@
const hwc_rect_t& frame) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->info.display_frame =
+ {frame.left, frame.top, frame.right, frame.bottom};
+
return Error::NONE;
}
Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->info.alpha = alpha;
+
return Error::NONE;
}
@@ -441,6 +536,11 @@
const hwc_frect_t& crop) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
+
return Error::NONE;
}
@@ -461,6 +561,11 @@
Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->z_order = z;
+
return Error::NONE;
}
@@ -468,6 +573,12 @@
uint32_t appId) {
if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+ HwcLayer* hwc_layer = display_.GetLayer(layer);
+ if (!hwc_layer) return Error::BAD_LAYER;
+
+ hwc_layer->info.type = type;
+ hwc_layer->info.app_id = appId;
+
return Error::NONE;
}
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.h b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
index fbbf028..1de056a 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.h
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
@@ -23,7 +23,7 @@
#include <mutex>
-#include "composer/impl/sync_timeline.h"
+#include "sync_timeline.h"
using namespace android::hardware::graphics::common::V1_0;
using namespace android::hardware::graphics::composer::V2_1;
@@ -58,9 +58,12 @@
// it going.
sp<GraphicBuffer> buffer;
sp<Fence> fence;
- Recti display_frame;
+ Recti display_frame;
Rectf crop;
BlendMode blend_mode;
+ float alpha;
+ uint32_t type;
+ uint32_t app_id;
};
using Frame = std::vector<ComposerLayer>;
@@ -84,9 +87,15 @@
};
struct HwcLayer {
+ using Composition =
+ hardware::graphics::composer::V2_1::IComposerClient::Composition;
+
HwcLayer(Layer new_id) : id(new_id) {}
Layer id;
+ Composition composition_type;
+ uint32_t z_order;
+ ComposerView::ComposerLayer info;
};
class HwcDisplay {
@@ -108,8 +117,8 @@
std::vector<ComposerView::ComposerLayer> GetFrame();
- void GetReleaseFences(
- std::vector<Layer>* layer_ids, std::vector<int>* fences);
+ void GetReleaseFences(int* present_fence, std::vector<Layer>* layer_ids,
+ std::vector<int>* fences);
void ReleaseFrame();
diff --git a/services/vr/vr_window_manager/composer_view/Android.bp b/services/vr/vr_window_manager/composer_view/Android.bp
new file mode 100644
index 0000000..7e25c85
--- /dev/null
+++ b/services/vr/vr_window_manager/composer_view/Android.bp
@@ -0,0 +1,28 @@
+cc_binary {
+ name: "vr_composer_view",
+
+ srcs: ["vr_composer_view.cpp"],
+
+ static_libs: [
+ "libhwcomposer-client",
+ ],
+
+ shared_libs: [
+ "android.dvr.composer@1.0",
+ "android.hardware.graphics.composer@2.1",
+ "libbase",
+ "libbinder",
+ "libhardware",
+ "libhwbinder",
+ "libutils",
+ "libvrhwc",
+ ],
+
+ cflags: [
+ "-DLOG_TAG=\"vr_composer_view\"",
+ ],
+
+ init_rc: [
+ "vr_composer_view.rc",
+ ],
+}
diff --git a/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp b/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
new file mode 100644
index 0000000..b5030e3
--- /dev/null
+++ b/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <binder/ProcessState.h>
+#include <hwbinder/IPCThreadState.h>
+#include <impl/vr_composer_view.h>
+#include <impl/vr_hwc.h>
+
+using namespace android;
+using namespace android::dvr;
+
+int main(int, char**) {
+ android::ProcessState::self()->startThreadPool();
+
+ const char instance[] = "vr_hwcomposer";
+ sp<IComposer> service = HIDL_FETCH_IComposer(instance);
+ LOG_FATAL_IF(!service, "Failed to get service");
+ LOG_FATAL_IF(service->isRemote(), "Service is remote");
+
+ service->registerAsService(instance);
+
+ sp<IVrComposerView> composer_view = HIDL_FETCH_IVrComposerView(
+ "DaydreamDisplay");
+ LOG_FATAL_IF(!composer_view, "Failed to get vr_composer_view service");
+ LOG_FATAL_IF(composer_view->isRemote(), "vr_composer_view service is remote");
+
+ composer_view->registerAsService("DaydreamDisplay");
+
+ GetVrComposerViewFromIVrComposerView(composer_view.get())->Initialize(
+ GetComposerViewFromIComposer(service.get()));
+
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::hardware::IPCThreadState::self()->joinThreadPool();
+
+ return 0;
+}
diff --git a/services/vr/vr_window_manager/composer_view/vr_composer_view.rc b/services/vr/vr_window_manager/composer_view/vr_composer_view.rc
new file mode 100644
index 0000000..abb5265
--- /dev/null
+++ b/services/vr/vr_window_manager/composer_view/vr_composer_view.rc
@@ -0,0 +1,5 @@
+service vr_composer_view /system/bin/vr_composer_view
+ class core
+ user system
+ group system graphics
+ cpuset /system
diff --git a/services/vr/vr_window_manager/controller_mesh.cpp b/services/vr/vr_window_manager/controller_mesh.cpp
new file mode 100644
index 0000000..c6095b1
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_mesh.cpp
@@ -0,0 +1,75 @@
+#include "controller_mesh.h"
+
+namespace android {
+namespace dvr {
+
+const int kNumControllerMeshVertices = 60;
+
+// Vertices in position.xyz, normal.xyz, uv.xy oder.
+// Generated from an .obj mesh.
+const float kControllerMeshVertices[] = {
+ 0.002023, 0.001469, -0.5, 0.809016, 0.587787, 0, 0, 0,
+ 0.000773, 0.002378, -0.5, 0.309004, 0.951061, 0, 0.1, 0,
+ 0.000773, 0.002378, 0, 0.309004, 0.951061, 0, 0.1, 1,
+ 0.002023, 0.001469, -0.5, 0.809016, 0.587787, 0, 0, 0,
+ 0.000773, 0.002378, 0, 0.309004, 0.951061, 0, 0.1, 1,
+ 0.002023, 0.001469, 0, 0.809016, 0.587787, 0, 0, 1,
+ 0.000773, 0.002378, -0.5, 0.309004, 0.951061, 0, 0.1, 0,
+ -0.000773, 0.002378, -0.5, -0.309004, 0.951061, 0, 0.2, 0,
+ -0.000773, 0.002378, 0, -0.309004, 0.951061, 0, 0.2, 1,
+ 0.000773, 0.002378, -0.5, 0.309004, 0.951061, 0, 0.1, 0,
+ -0.000773, 0.002378, 0, -0.309004, 0.951061, 0, 0.2, 1,
+ 0.000773, 0.002378, 0, 0.309004, 0.951061, 0, 0.1, 1,
+ -0.000773, 0.002378, -0.5, -0.309004, 0.951061, 0, 0.2, 0,
+ -0.002023, 0.001469, -0.5, -0.809016, 0.587787, 0, 0.3, 0,
+ -0.002023, 0.001469, 0, -0.809016, 0.587787, 0, 0.3, 1,
+ -0.000773, 0.002378, -0.5, -0.309004, 0.951061, 0, 0.2, 0,
+ -0.002023, 0.001469, 0, -0.809016, 0.587787, 0, 0.3, 1,
+ -0.000773, 0.002378, 0, -0.309004, 0.951061, 0, 0.2, 1,
+ -0.002023, 0.001469, -0.5, -0.809016, 0.587787, 0, 0.3, 0,
+ -0.0025, 0, -0.5, -1, -0, 0, 0.4, 0,
+ -0.0025, 0, 0, -1, -0, 0, 0.4, 1,
+ -0.002023, 0.001469, -0.5, -0.809016, 0.587787, 0, 0.3, 0,
+ -0.0025, 0, 0, -1, -0, 0, 0.4, 1,
+ -0.002023, 0.001469, 0, -0.809016, 0.587787, 0, 0.3, 1,
+ -0.0025, 0, -0.5, -1, -0, 0, 0.4, 0,
+ -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+ -0.002023, -0.001469, 0, -0.809016, -0.587787, 0, 0.5, 1,
+ -0.0025, 0, -0.5, -1, -0, 0, 0.4, 0,
+ -0.002023, -0.001469, 0, -0.809016, -0.587787, 0, 0.5, 1,
+ -0.0025, 0, 0, -1, -0, 0, 0.4, 1,
+ -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+ -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+ -0.000773, -0.002378, 0, -0.309004, -0.951061, 0, 0.6, 1,
+ -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+ -0.000773, -0.002378, 0, -0.309004, -0.951061, 0, 0.6, 1,
+ -0.002023, -0.001469, 0, -0.809016, -0.587787, 0, 0.5, 1,
+ -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+ 0.000773, -0.002378, -0.5, 0.309004, -0.951061, 0, 0.7, 0,
+ 0.000773, -0.002378, 0, 0.309004, -0.951061, 0, 0.7, 1,
+ -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+ 0.000773, -0.002378, 0, 0.309004, -0.951061, 0, 0.7, 1,
+ -0.000773, -0.002378, 0, -0.309004, -0.951061, 0, 0.6, 1,
+ 0.000773, -0.002378, -0.5, 0.309004, -0.951061, 0, 0.7, 0,
+ 0.002023, -0.001469, -0.5, 0.809016, -0.587787, 0, 0.8, 0,
+ 0.002023, -0.001469, 0, 0.809016, -0.587787, 0, 0.8, 1,
+ 0.000773, -0.002378, -0.5, 0.309004, -0.951061, 0, 0.7, 0,
+ 0.002023, -0.001469, 0, 0.809016, -0.587787, 0, 0.8, 1,
+ 0.000773, -0.002378, 0, 0.309004, -0.951061, 0, 0.7, 1,
+ 0.002023, -0.001469, -0.5, 0.809016, -0.587787, 0, 0.8, 0,
+ 0.0025, 0, -0.5, 1, 0, 0, 0.9, 0,
+ 0.0025, 0, 0, 1, 0, 0, 0.9, 1,
+ 0.002023, -0.001469, -0.5, 0.809016, -0.587787, 0, 0.8, 0,
+ 0.0025, 0, 0, 1, 0, 0, 0.9, 1,
+ 0.002023, -0.001469, 0, 0.809016, -0.587787, 0, 0.8, 1,
+ 0.0025, 0, -0.5, 1, 0, 0, 0.9, 0,
+ 0.002023, 0.001469, -0.5, 0.809016, 0.587787, 0, 1, 0,
+ 0.002023, 0.001469, 0, 0.809016, 0.587787, 0, 1, 1,
+ 0.0025, 0, -0.5, 1, 0, 0, 0.9, 0,
+ 0.002023, 0.001469, 0, 0.809016, 0.587787, 0, 1, 1,
+ 0.0025, 0, 0, 1, 0, 0, 0.9, 1,
+
+};
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/controller_mesh.h b/services/vr/vr_window_manager/controller_mesh.h
new file mode 100644
index 0000000..88872c7
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_mesh.h
@@ -0,0 +1,13 @@
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+
+namespace android {
+namespace dvr {
+
+extern const int kNumControllerMeshVertices;
+extern const float kControllerMeshVertices[];
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
diff --git a/services/vr/vr_window_manager/elbow_model.cpp b/services/vr/vr_window_manager/elbow_model.cpp
new file mode 100644
index 0000000..54d1eb4
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.cpp
@@ -0,0 +1,134 @@
+#include "elbow_model.h"
+
+#include <cutils/log.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+const vec3 kControllerForearm(0.0f, 0.0f, -0.25f);
+const vec3 kControllerPosition(0.0f, 0.0f, -0.05f);
+const vec3 kLeftElbowPosition(-0.195f, -0.5f, 0.075f);
+const vec3 kLeftArmExtension(0.13f, 0.14f, -0.08f);
+const vec3 kRightElbowPosition(0.195f, -0.5f, 0.075f);
+const vec3 kRightArmExtension(-0.13f, 0.14f, -0.08f);
+constexpr float kElbowBendRatio = 0.4f;
+constexpr float kCosMaxExtensionAngle =
+ 0.87f; // Cos of 30 degrees (90-30 = 60)
+constexpr float kCosMinExtensionAngle = 0.12f; // Cos of 83 degrees (90-83 = 7)
+constexpr float kYAxisExtensionFraction = 0.4f;
+constexpr float kMinRotationSpeed = 0.61f; // 35 degrees in radians
+constexpr float kMinAngleDelta = 0.175f; // 10 degrees in radians
+
+float clamp(float v, float min, float max) {
+ if (v < min)
+ return min;
+ if (v > max)
+ return max;
+ return v;
+}
+
+float NormalizeAngle(float angle) {
+ if (angle > M_PI)
+ angle = 2.0f * M_PI - angle;
+ return angle;
+}
+
+} // namespace
+
+const vec3 ElbowModel::kDefaultNeckPosition = vec3(0, -0.075f, -0.080f);
+
+ElbowModel::ElbowModel() {}
+ElbowModel::~ElbowModel() {}
+
+void ElbowModel::Enable(const vec3& neck_position, bool right_handed) {
+ enabled_ = true;
+ neck_position_ = neck_position;
+
+ if (right_handed) {
+ elbow_position_ = kRightElbowPosition;
+ arm_extension_ = kRightArmExtension;
+ } else {
+ elbow_position_ = kLeftElbowPosition;
+ arm_extension_ = kLeftArmExtension;
+ }
+
+ ResetRoot();
+}
+
+void ElbowModel::Disable() { enabled_ = false; }
+
+vec3 ElbowModel::Update(float delta_t, const quat& hmd_orientation,
+ const quat& controller_orientation, bool recenter) {
+ if (!enabled_)
+ return vec3::Zero();
+
+ float heading_rad = GetHeading(hmd_orientation);
+
+ quat y_rotation;
+ y_rotation = Eigen::AngleAxis<float>(heading_rad, vec3::UnitY());
+
+ // If the controller's angular velocity is above a certain amount, we can
+ // assume torso rotation and move the elbow joint relative to the
+ // camera orientation.
+ float angle_delta = last_controller_.angularDistance(controller_orientation);
+ float rot_speed = angle_delta / delta_t;
+
+ if (recenter) {
+ root_rot_ = y_rotation;
+ } else if (rot_speed > kMinRotationSpeed) {
+ root_rot_.slerp(angle_delta / kMinAngleDelta, y_rotation);
+ }
+
+ // Calculate angle (or really, cos thereof) between controller forward vector
+ // and Y axis to determine extension amount.
+ vec3 controller_forward_rotated = controller_orientation * -vec3::UnitZ();
+ float dot_y = controller_forward_rotated.y();
+ float amt_extension = clamp(dot_y - kCosMinExtensionAngle, 0, 1);
+
+ // Remove the root rotation from the orientation reading--we'll add it back in
+ // later.
+ quat controller_rot = root_rot_.inverse() * controller_orientation;
+ controller_forward_rotated = controller_rot * -vec3::UnitZ();
+ quat rot_xy;
+ rot_xy.setFromTwoVectors(-vec3::UnitZ(), controller_forward_rotated);
+
+ // Fixing polar singularity
+ float total_angle = NormalizeAngle(atan2f(rot_xy.norm(), rot_xy.w()) * 2.0f);
+ float lerp_amount = (1.0f - powf(total_angle / M_PI, 6.0f)) *
+ (1.0f - (kElbowBendRatio +
+ (1.0f - kElbowBendRatio) *
+ (amt_extension + kYAxisExtensionFraction)));
+
+ // Calculate the relative rotations of the elbow and wrist joints.
+ quat wrist_rot = quat::Identity();
+ wrist_rot.slerp(lerp_amount, rot_xy);
+ quat elbow_rot = wrist_rot.inverse() * rot_xy;
+
+ last_controller_ = controller_orientation;
+
+ vec3 position =
+ root_rot_ *
+ ((controller_root_offset_ + arm_extension_ * amt_extension) +
+ elbow_rot * (kControllerForearm + wrist_rot * kControllerPosition));
+
+ return position;
+}
+
+float ElbowModel::GetHeading(const quat& orientation) {
+ vec3 gaze = orientation * -vec3::UnitZ();
+
+ if (gaze.y() > 0.99)
+ gaze = orientation * -vec3::UnitY();
+ else if (gaze.y() < -0.99)
+ gaze = orientation * vec3::UnitY();
+
+ return atan2f(-gaze.x(), -gaze.z());
+}
+
+void ElbowModel::ResetRoot() {
+ controller_root_offset_ = elbow_position_ + neck_position_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/elbow_model.h b/services/vr/vr_window_manager/elbow_model.h
new file mode 100644
index 0000000..a6d5ca9
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.h
@@ -0,0 +1,45 @@
+#ifndef VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+#define VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class ElbowModel {
+ public:
+ ElbowModel();
+ ~ElbowModel();
+
+ void Enable(const vec3& neck_position, bool right_handed);
+ void Disable();
+
+ vec3 Update(float delta_t, const quat& hmd_orientation,
+ const quat& controller_orientation, bool recenter);
+
+ static const vec3 kDefaultNeckPosition;
+
+ private:
+ ElbowModel(const ElbowModel&) = delete;
+ void operator=(const ElbowModel&) = delete;
+
+ void ResetRoot();
+
+ float GetHeading(const quat& orientation);
+
+ bool enabled_ = false;
+
+ quat last_controller_ = quat::Identity();
+
+ quat root_rot_ = quat::Identity();
+
+ vec3 controller_root_offset_ = vec3::Zero();
+ vec3 elbow_position_ = vec3::Zero();
+ vec3 arm_extension_ = vec3::Zero();
+ vec3 neck_position_ = vec3::Zero();
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_ELBOW_MODEL_H_
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
new file mode 100644
index 0000000..b2edc20
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -0,0 +1,98 @@
+#include "hwc_callback.h"
+
+#include <gralloc_priv.h>
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <private/dvr/native_buffer.h>
+#include <sync/sync.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+sp<GraphicBuffer> GetBufferFromHandle(const native_handle_t* handle) {
+ // TODO(dnicoara): Fix this once gralloc1 is available.
+ private_handle_t* private_handle = private_handle_t::dynamicCast(handle);
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ private_handle->width, private_handle->height, private_handle->format, 1,
+ GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_HW_2D | GraphicBuffer::USAGE_HW_RENDER,
+ private_handle->width, native_handle_clone(handle), true);
+ if (GraphicBufferMapper::get().registerBuffer(buffer.get()) != OK) {
+ ALOGE("Failed to register buffer");
+ return nullptr;
+ }
+
+ return buffer;
+}
+
+HwcCallback::FrameStatus GetFrameStatus(const HwcCallback::Frame& frame) {
+ for (const auto& layer : frame.layers()) {
+ // If there is no fence it means the buffer is already finished.
+ if (layer.fence->isValid()) {
+ status_t result = layer.fence->wait(0);
+ if (result != OK) {
+ if (result != -ETIME) {
+ ALOGE("fence wait on buffer fence failed. status=%d (%s).",
+ result, strerror(-result));
+ return HwcCallback::FrameStatus::kError;
+ }
+ return HwcCallback::FrameStatus::kUnfinished;
+ }
+ }
+ }
+
+ return HwcCallback::FrameStatus::kFinished;
+}
+
+} // namespace
+
+HwcCallback::HwcCallback(IVrComposerView* composer_view, Client* client)
+ : composer_view_(composer_view),
+ client_(client) {
+ composer_view_->registerCallback(this);
+}
+
+HwcCallback::~HwcCallback() {
+ composer_view_->registerCallback(nullptr);
+}
+
+Return<void> HwcCallback::onNewFrame(
+ const hidl_vec<IVrComposerCallback::Layer>& frame) {
+
+ std::vector<HwcLayer> hwc_frame(frame.size());
+ for (size_t i = 0; i < frame.size(); ++i) {
+ int fence = frame[i].fence.getNativeHandle()->numFds ?
+ dup(frame[i].fence.getNativeHandle()->data[0]) : -1;
+
+ hwc_frame[i] = HwcLayer{
+ .fence = new Fence(fence),
+ .buffer = GetBufferFromHandle(frame[i].buffer.getNativeHandle()),
+ .crop = frame[i].crop,
+ .display_frame = frame[i].display_frame,
+ .blending = static_cast<int32_t>(frame[i].blend_mode),
+ .appid = frame[i].app_id,
+ .type = static_cast<HwcLayer::LayerType>(frame[i].type),
+ .alpha = frame[i].alpha,
+ };
+ }
+
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (client_)
+ client_->OnFrame(std::make_unique<Frame>(std::move(hwc_frame)));
+
+ return Void();
+}
+
+HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers)
+ : layers_(std::move(layers)) {}
+
+HwcCallback::FrameStatus HwcCallback::Frame::Finish() {
+ if (status_ == FrameStatus::kUnfinished)
+ status_ = GetFrameStatus(*this);
+ return status_;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/hwc_callback.h b/services/vr/vr_window_manager/hwc_callback.h
new file mode 100644
index 0000000..05a889b
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.h
@@ -0,0 +1,110 @@
+#ifndef VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+#define VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include <android/dvr/composer/1.0/IVrComposerCallback.h>
+#include <android/dvr/composer/1.0/IVrComposerView.h>
+#include <impl/vr_hwc.h>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+namespace dvr {
+
+using Recti = ComposerView::ComposerLayer::Recti;
+using Rectf = ComposerView::ComposerLayer::Rectf;
+
+using composer::V1_0::IVrComposerCallback;
+using composer::V1_0::IVrComposerView;
+
+class HwcCallback : public IVrComposerCallback {
+ public:
+ struct HwcLayer {
+ enum LayerType : uint32_t {
+ // These are from frameworks/base/core/java/android/view/WindowManager.java
+ kUndefinedWindow = 0,
+ kFirstApplicationWindow = 1,
+ kLastApplicationWindow = 99,
+ kFirstSubWindow = 1000,
+ kLastSubWindow = 1999,
+ kFirstSystemWindow = 2000,
+ kStatusBar = kFirstSystemWindow,
+ kInputMethod = kFirstSystemWindow + 11,
+ kNavigationBar = kFirstSystemWindow + 19,
+ kLastSystemWindow = 2999,
+ };
+
+ bool should_skip_layer() const {
+ switch (type) {
+ // Always skip the following layer types
+ case kNavigationBar:
+ case kStatusBar:
+ case kUndefinedWindow:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ Rectf crop;
+ Recti display_frame;
+ int32_t blending;
+ uint32_t appid;
+ LayerType type;
+ float alpha;
+ };
+
+ enum class FrameStatus {
+ kUnfinished,
+ kFinished,
+ kError
+ };
+
+ class Frame {
+ public:
+ Frame(std::vector<HwcLayer>&& layers);
+
+ FrameStatus Finish();
+ const std::vector<HwcLayer>& layers() const { return layers_; }
+
+ private:
+ std::vector<HwcLayer> layers_;
+ FrameStatus status_ = FrameStatus::kUnfinished;
+ };
+
+ class Client {
+ public:
+ virtual ~Client() {}
+ virtual void OnFrame(std::unique_ptr<Frame>) = 0;
+ };
+
+ explicit HwcCallback(IVrComposerView* composer_view, Client* client);
+ ~HwcCallback() override;
+
+ private:
+ // This is the only method called on the binder thread. Everything else is
+ // called on the render thread.
+ Return<void> onNewFrame(const hidl_vec<IVrComposerCallback::Layer>& frame)
+ override;
+
+ IVrComposerView* composer_view_;
+ Client *client_;
+ std::mutex mutex_;
+
+
+ HwcCallback(const HwcCallback&) = delete;
+ void operator=(const HwcCallback&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_HWC_CALLBACK_H_
diff --git a/services/vr/vr_window_manager/java/com/google/vr/windowmanager/BootCompletedReceiver.java b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/BootCompletedReceiver.java
new file mode 100644
index 0000000..01d1bdb
--- /dev/null
+++ b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/BootCompletedReceiver.java
@@ -0,0 +1,17 @@
+package com.google.vr.windowmanager;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class BootCompletedReceiver extends BroadcastReceiver {
+ private static final String TAG = BootCompletedReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Starting VRWindowManager");
+ Intent vrWindowManagerIntent = new Intent(context, VrWindowManagerService.class);
+ context.startService(vrWindowManagerIntent);
+ }
+}
diff --git a/services/vr/vr_window_manager/java/com/google/vr/windowmanager/VrWindowManagerService.java b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/VrWindowManagerService.java
new file mode 100644
index 0000000..1d815ca
--- /dev/null
+++ b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/VrWindowManagerService.java
@@ -0,0 +1,87 @@
+package com.google.vr.windowmanager;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+public class VrWindowManagerService extends Service {
+ private static final String TAG = VrWindowManagerService.class.getSimpleName();
+ private long nativeVrWindowManager;
+
+ // This is a temporary debugging tool for development only.
+ // It allows us to show VrWindowManager in debug mode via command line.
+ private final BroadcastReceiver debugReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals("com.google.vr.windowmanager.intent.SHOW")) {
+ nativeEnableDebug(nativeVrWindowManager);
+ } else if (action.equals("com.google.vr.windowmanager.intent.HIDE")) {
+ nativeDisableDebug(nativeVrWindowManager);
+ } else if (action.equals("com.google.vr.windowmanager.intent.ENTER_VR")) {
+ nativeEnterVrMode(nativeVrWindowManager);
+ } else if (action.equals("com.google.vr.windowmanager.intent.EXIT_VR")) {
+ nativeExitVrMode(nativeVrWindowManager);
+ }
+ }
+ };
+
+ static {
+ System.loadLibrary("vr_window_manager_jni");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ destroyRenderer();
+ nativeVrWindowManager = nativeCreate(getClass().getClassLoader(), getApplicationContext());
+ if (nativeVrWindowManager == 0) {
+ Log.e(TAG, "Failed to create native renderer");
+ }
+
+ // For development, testing and debugging.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction("com.google.vr.windowmanager.intent.SHOW");
+ filter.addAction("com.google.vr.windowmanager.intent.HIDE");
+ filter.addAction("com.google.vr.windowmanager.intent.ENTER_VR");
+ filter.addAction("com.google.vr.windowmanager.intent.EXIT_VR");
+ registerReceiver(debugReceiver, filter);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "Ignoring bind request");
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(debugReceiver);
+ destroyRenderer();
+ }
+
+ private void destroyRenderer() {
+ if (nativeVrWindowManager != 0) {
+ nativeDestroy(nativeVrWindowManager);
+ nativeVrWindowManager = 0;
+ }
+ }
+
+ private native long nativeCreate(ClassLoader appClassLoader, Context context);
+ private native void nativeDestroy(long nativeVrWindowManager);
+ private native void nativeEnableDebug(long nativeVrWindowManager);
+ private native void nativeDisableDebug(long nativeVrWindowManager);
+ private native void nativeEnterVrMode(long nativeVrWindowManager);
+ private native void nativeExitVrMode(long nativeVrWindowManager);
+}
diff --git a/services/vr/vr_window_manager/proguard.flags b/services/vr/vr_window_manager/proguard.flags
new file mode 100644
index 0000000..7683d6e
--- /dev/null
+++ b/services/vr/vr_window_manager/proguard.flags
@@ -0,0 +1,22 @@
+# Don't obfuscate any NDK/SDK code. This makes the debugging of stack traces in
+# in release builds easier.
+-keepnames class com.google.vr.ndk.** { *; }
+-keepnames class com.google.vr.sdk.** { *; }
+
+# These are part of the SDK <-> VrCore interfaces for GVR.
+-keepnames class com.google.vr.vrcore.library.api.** { *; }
+
+# These are part of the Java <-> native interfaces for GVR.
+-keep class com.google.vr.** { native <methods>; }
+
+-keep class com.google.vr.cardboard.annotations.UsedByNative
+-keep @com.google.vr.cardboard.annotations.UsedByNative class *
+-keepclassmembers class * {
+ @com.google.vr.cardboard.annotations.UsedByNative *;
+}
+
+-keep class com.google.vr.cardboard.UsedByNative
+-keep @com.google.vr.cardboard.UsedByNative class *
+-keepclassmembers class * {
+ @com.google.vr.cardboard.UsedByNative *;
+}
diff --git a/services/vr/vr_window_manager/render_thread.cpp b/services/vr/vr_window_manager/render_thread.cpp
new file mode 100644
index 0000000..00e3161
--- /dev/null
+++ b/services/vr/vr_window_manager/render_thread.cpp
@@ -0,0 +1,92 @@
+#include <cutils/log.h>
+#include <future>
+#include <jni.h>
+
+#include "render_thread.h"
+#include "shell_view.h"
+
+namespace android {
+namespace dvr {
+
+RenderThread::RenderThread(JNIEnv* env, jobject class_loader,
+ jobject android_context)
+ : jvm_(nullptr),
+ class_loader_global_ref_(0),
+ android_context_global_ref_(0),
+ quit_(false) {
+ env->GetJavaVM(&jvm_);
+
+ // Create global references so we can access these objects on the render
+ // thread
+ class_loader_global_ref_ = env->NewGlobalRef(class_loader);
+ android_context_global_ref_ = env->NewGlobalRef(android_context);
+
+ std::promise<int> render_thread_init_result_promise;
+ thread_ = std::thread([this, &render_thread_init_result_promise] {
+ JNIEnv* render_thread_jni_env = nullptr;
+ jvm_->AttachCurrentThread(&render_thread_jni_env, nullptr);
+ RunRenderLoop(&render_thread_init_result_promise);
+ jvm_->DetachCurrentThread();
+ });
+
+ // Wait to see if the render thread started successfully. If not bail.
+ int render_thread_init_result =
+ render_thread_init_result_promise.get_future().get();
+ LOG_ALWAYS_FATAL_IF(render_thread_init_result != 0,
+ "Failed initializing render thread. result=%d",
+ render_thread_init_result);
+}
+
+RenderThread::~RenderThread() { Quit(); }
+
+void RenderThread::Quit() {
+ if (thread_.joinable()) {
+ quit_ = true;
+ thread_.join();
+ }
+
+ JNIEnv* env = GetJniEnv();
+ if (class_loader_global_ref_ != 0) {
+ env->DeleteGlobalRef(class_loader_global_ref_);
+ class_loader_global_ref_ = 0;
+ }
+ if (android_context_global_ref_ != 0) {
+ env->DeleteGlobalRef(android_context_global_ref_);
+ android_context_global_ref_ = 0;
+ }
+}
+
+void RenderThread::EnableDebug(bool debug) { shell_view_.EnableDebug(debug); }
+
+void RenderThread::VrMode(bool mode) { shell_view_.VrMode(mode); }
+
+JNIEnv* RenderThread::GetJniEnv() {
+ JNIEnv* env;
+ jvm_->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ return env;
+}
+
+void RenderThread::RunRenderLoop(
+ std::promise<int>* init_result_promise) {
+ // TODO(steventhomas): Create local refs to work around b/33251144. Remove
+ // once that bug is fixed.
+ JNIEnv* env = GetJniEnv();
+ jobject class_loader = env->NewLocalRef(class_loader_global_ref_);
+ jobject android_context = env->NewLocalRef(android_context_global_ref_);
+
+ int init_result = shell_view_.Initialize(env, android_context, class_loader);
+ init_result += shell_view_.AllocateResources();
+ init_result_promise->set_value(init_result);
+ if (init_result == 0) {
+ while (!quit_)
+ shell_view_.DrawFrame();
+ } else {
+ ALOGE("Failed to initialize ShellView");
+ }
+
+ env->DeleteLocalRef(class_loader);
+ env->DeleteLocalRef(android_context);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/render_thread.h b/services/vr/vr_window_manager/render_thread.h
new file mode 100644
index 0000000..e193643
--- /dev/null
+++ b/services/vr/vr_window_manager/render_thread.h
@@ -0,0 +1,47 @@
+#ifndef VR_WINDOW_MANAGER_RENDER_THREAD_H_
+#define VR_WINDOW_MANAGER_RENDER_THREAD_H_
+
+#include <atomic>
+#include <future>
+#include <jni.h>
+#include <thread>
+
+#include "shell_view.h"
+
+namespace android {
+namespace dvr {
+
+class RenderThread {
+ public:
+ RenderThread(JNIEnv* env, jobject class_loader, jobject android_context);
+ ~RenderThread();
+ void Quit();
+ void EnableDebug(bool debug);
+ void VrMode(bool mode);
+
+ RenderThread(const RenderThread&) = delete;
+ void operator=(const RenderThread&) = delete;
+
+ private:
+ // Called by both the main thread and render thread. Will return the correct
+ // JNIEnv for the current thread.
+ JNIEnv* GetJniEnv();
+
+ void RunRenderLoop(std::promise<int>* init_result_promise);
+
+ // Accessed only by the main thread.
+ std::thread thread_;
+
+ // The vars below are accessed by both the main thread and the render thread.
+ JavaVM* jvm_;
+ jobject class_loader_global_ref_;
+ jobject android_context_global_ref_;
+ std::atomic_bool quit_;
+
+ ShellView shell_view_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_RENDER_THREAD_H_
diff --git a/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon.png b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon.png
new file mode 100644
index 0000000..06f896d
--- /dev/null
+++ b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon.png
Binary files differ
diff --git a/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon_background.png b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon_background.png
new file mode 100644
index 0000000..d336da3
--- /dev/null
+++ b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon_background.png
Binary files differ
diff --git a/services/vr/vr_window_manager/res/values/styles.xml b/services/vr/vr_window_manager/res/values/styles.xml
new file mode 100644
index 0000000..8a1a74b
--- /dev/null
+++ b/services/vr/vr_window_manager/res/values/styles.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<add-resource type="style" name="AppStyle"></add-resource>
+<style name="AppStyle"
+ parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+ <item name="android:windowDisablePreview">true</item>
+</style>
+</resources>
diff --git a/services/vr/vr_window_manager/reticle.cpp b/services/vr/vr_window_manager/reticle.cpp
new file mode 100644
index 0000000..cbd0caf
--- /dev/null
+++ b/services/vr/vr_window_manager/reticle.cpp
@@ -0,0 +1,100 @@
+#include "reticle.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+const std::string kVertexShader = SHADER0([]() {
+ layout(location = 0) in vec4 aPosition;
+ layout(location = 1) in vec4 aTexCoord;
+ uniform mat4 uViewProjection;
+ uniform mat4 uTransform;
+
+ out vec2 vTexCoord;
+ void main() {
+ gl_Position = uViewProjection * uTransform * aPosition;
+ vTexCoord = aTexCoord.xy;
+ }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+ precision mediump float;
+
+ in vec2 vTexCoord;
+ uniform vec3 uColor;
+
+ out vec4 fragColor;
+ void main() {
+ float alpha = smoothstep(1.0, 0.0, length(vTexCoord));
+ fragColor = vec4(uColor, alpha);
+ }
+});
+
+} // namespace
+
+Reticle::Reticle() {}
+
+Reticle::~Reticle() {}
+
+bool Reticle::Initialize() {
+ program_.Link(kVertexShader, kFragmentShader);
+ if (!program_)
+ return false;
+
+ return true;
+}
+
+void Reticle::ShowAt(const mat4& hit_transform, const vec3& color) {
+ transform_ = hit_transform;
+ shown_ = true;
+
+ GLint view_projection_location =
+ glGetUniformLocation(program_.GetProgram(), "uColor");
+ glProgramUniform3f(program_.GetProgram(), view_projection_location, color.x(),
+ color.y(), color.z());
+}
+
+void Reticle::Draw(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ if (!shown_)
+ return;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ program_.Use();
+
+ const float kRadius = 0.015;
+ GLfloat vertices[] = {
+ -kRadius, -kRadius, 0, kRadius, -kRadius, 0,
+ -kRadius, kRadius, 0, kRadius, kRadius, 0,
+ };
+ GLfloat texture_vertices[] = {
+ -1, 1, 1, 1, -1, -1, 1, -1,
+ };
+
+ mat4 mvp = perspective * eye_matrix * head_matrix;
+ GLint view_projection_location =
+ glGetUniformLocation(program_.GetProgram(), "uViewProjection");
+ glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+ GLint transform_location =
+ glGetUniformLocation(program_.GetProgram(), "uTransform");
+ glUniformMatrix4fv(transform_location, 1, 0, transform_.data());
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texture_vertices);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisable(GL_BLEND);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/reticle.h b/services/vr/vr_window_manager/reticle.h
new file mode 100644
index 0000000..d8522aa
--- /dev/null
+++ b/services/vr/vr_window_manager/reticle.h
@@ -0,0 +1,35 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+#define VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class Reticle {
+ public:
+ Reticle();
+ ~Reticle();
+
+ bool Initialize();
+
+ void ShowAt(const mat4& hit_transform, const vec3& color);
+ void Hide() { shown_ = false; }
+
+ void Draw(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+
+ private:
+ bool shown_ = false;
+ ShaderProgram program_;
+ mat4 transform_;
+
+ Reticle(const Reticle&) = delete;
+ void operator=(const Reticle&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_SHELL_RETICLE_H_
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
new file mode 100644
index 0000000..cef111c
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -0,0 +1,669 @@
+#include "shell_view.h"
+
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <hardware/hwcomposer2.h>
+
+#include "controller_mesh.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr unsigned int kVRAppLayerCount = 2;
+
+constexpr unsigned int kMaximumPendingFrames = 8;
+
+const std::string kVertexShader = SHADER0([]() {
+ layout(location = 0) in vec4 aPosition;
+ layout(location = 1) in vec4 aTexCoord;
+ uniform mat4 uViewProjection;
+ uniform mat4 uTransform;
+
+ out vec2 vTexCoord;
+ void main() {
+ gl_Position = uViewProjection * uTransform * aPosition;
+ vTexCoord = aTexCoord.xy;
+ }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+ precision mediump float;
+
+ in vec2 vTexCoord;
+ uniform sampler2D tex;
+ uniform float uAlpha;
+
+ out vec4 fragColor;
+ void main() {
+ fragColor = texture(tex, vTexCoord);
+ fragColor.a *= uAlpha;
+ }
+});
+
+// This shader provides a dim layer in a given rect. This is intended
+// to indicate the non-interactive region.
+// Texture coordinates between [uCoords.xy, uCoords.zw] are dim, otherwise
+// transparent.
+const std::string kOverlayFragmentShader = SHADER0([]() {
+ precision highp float;
+
+ in vec2 vTexCoord;
+ uniform sampler2D tex;
+ uniform vec4 uCoords;
+
+ out vec4 fragColor;
+ void main() {
+ vec4 color = vec4(0, 0, 0, 0);
+ if (all(greaterThan(vTexCoord, uCoords.xy)) &&
+ all(lessThan(vTexCoord, uCoords.zw))) {
+ color = vec4(0, 0, 0, 0.5);
+ }
+ fragColor = color;
+ }
+});
+
+const std::string kControllerFragmentShader = SHADER0([]() {
+ precision mediump float;
+
+ in vec2 vTexCoord;
+
+ out vec4 fragColor;
+ void main() { fragColor = vec4(0.8, 0.2, 0.2, 1.0); }
+});
+
+const GLfloat kVertices[] = {
+ -1, -1, 0,
+ 1, -1, 0,
+ -1, 1, 0,
+ 1, 1, 0,
+};
+
+const GLfloat kTextureVertices[] = {
+ 0, 1,
+ 1, 1,
+ 0, 0,
+ 1, 0,
+};
+
+// Returns true if the given point is inside the given rect.
+bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
+ return pt.x() >= tl.x() && pt.x() <= br.x() &&
+ pt.y() >= tl.y() && pt.y() <= br.y();
+}
+
+mat4 GetScalingMatrix(float width, float height) {
+ float xscale = 1, yscale = 1;
+ float ar = width / height;
+ if (ar > 1)
+ yscale = 1.0 / ar;
+ else
+ xscale = ar;
+
+ return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
+}
+
+mat4 GetHorizontallyAlignedMatrixFromPose(const Posef& pose) {
+ vec3 position = pose.GetPosition();
+ quat view_quaternion = pose.GetRotation();
+
+ vec3 z = vec3(view_quaternion * vec3(0.0f, 0.0f, 1.0f));
+ vec3 y(0.0f, 1.0f, 0.0f);
+ vec3 x = y.cross(z);
+ x.normalize();
+ y = z.cross(x);
+
+ mat4 m;
+ // clang-format off
+ m(0, 0) = x[0]; m(0, 1) = y[0]; m(0, 2) = z[0]; m(0, 3) = position[0];
+ m(1, 0) = x[1]; m(1, 1) = y[1]; m(1, 2) = z[1]; m(1, 3) = position[1];
+ m(2, 0) = x[2]; m(2, 1) = y[2]; m(2, 2) = z[2]; m(2, 3) = position[2];
+ m(3, 0) = 0.0f; m(3, 1) = 0.0f; m(3, 2) = 0.0f; m(3, 3) = 1.0f;
+ // clang-format on
+
+ return m;
+}
+
+// Helper function that applies the crop transform to the texture layer and
+// positions (and scales) the texture layer in the appropriate location in the
+// display space.
+mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
+ float display_height) {
+ // Map from vertex coordinates to [0, 1] coordinates:
+ // 1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
+ // in texture coordinates (0, 0) is at the top left.
+ // 2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
+ // 3) Scale by 1 / 2 to map coordinates to [0, 1] on x and y.
+ mat4 unit_space(
+ Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
+ Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
+ Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
+
+ mat4 texture_space(Eigen::AlignedScaling3f(
+ texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
+
+ // 1) Translate the layer to crop the left and top edge.
+ // 2) Scale the layer such that the cropped right and bottom edges map outside
+ // the exture region.
+ float crop_width = texture_layer.crop.right - texture_layer.crop.left;
+ float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
+ mat4 texture_crop(
+ Eigen::AlignedScaling3f(
+ texture_layer.texture->width() / crop_width,
+ texture_layer.texture->height() / crop_height,
+ 1.0f) *
+ Eigen::Translation3f(
+ -texture_layer.crop.left, -texture_layer.crop.top, 0.0f));
+
+ mat4 display_space(
+ Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
+
+ // 1) Scale the texture to fit the display frame.
+ // 2) Translate the texture in the display frame location.
+ float display_frame_width = texture_layer.display_frame.right -
+ texture_layer.display_frame.left;
+ float display_frame_height = texture_layer.display_frame.bottom -
+ texture_layer.display_frame.top;
+ mat4 display_frame(
+ Eigen::Translation3f(
+ texture_layer.display_frame.left,
+ texture_layer.display_frame.top,
+ 0.0f) *
+ Eigen::AlignedScaling3f(
+ display_frame_width / display_width,
+ display_frame_height / display_height,
+ 1.0f));
+
+ mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
+ display_frame * display_space * texture_space.inverse() * texture_crop *
+ texture_space * unit_space;
+ return layer_transform;
+}
+
+vec3 FromGvrVec3f(const gvr_vec3f& vec3f) {
+ return vec3(vec3f.x, vec3f.y, vec3f.z);
+}
+
+quat FromGvrQuatf(const gvr_quatf& quaternion) {
+ return quat(quaternion.qw, quaternion.qx, quaternion.qy, quaternion.qz);
+}
+
+// Determine if ths frame should be shown or hidden.
+bool CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+ uint32_t vr_app) {
+ auto& layers = frame.layers();
+
+ // We assume the first two layers are the VR app.
+ if (layers.size() < kVRAppLayerCount)
+ return false;
+
+ if (vr_app != layers[0].appid || layers[0].appid == 0)
+ return false;
+
+ // If a non-VR-app, non-skipped layer appears, show.
+ size_t index = kVRAppLayerCount;
+ // Now, find a dim layer if it exists.
+ // If it does, ignore any layers behind it for visibility determination.
+ for (size_t i = index; i < layers.size(); i++) {
+ if (layers[i].appid == 0) {
+ index = i + 1;
+ break;
+ }
+ }
+
+ // If any non-skipped layers exist now then we show, otherwise hide.
+ for (size_t i = index; i < layers.size(); i++) {
+ if (!layers[i].should_skip_layer())
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+ShellView::ShellView() {
+ ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
+ ime_top_left_ = vec2(0, 0);
+ ime_size_ = vec2(0, 0);
+}
+
+ShellView::~ShellView() {}
+
+int ShellView::Initialize(JNIEnv* env, jobject app_context,
+ jobject class_loader) {
+ int ret = Application::Initialize(env, app_context, class_loader);
+ if (ret)
+ return ret;
+
+ translate_ = Eigen::Translation3f(0, 0, -2.5f);
+
+ if (!InitializeTouch())
+ ALOGE("Failed to initialize virtual touchpad");
+
+ return 0;
+}
+
+int ShellView::AllocateResources() {
+ int ret = Application::AllocateResources();
+ if (ret)
+ return ret;
+
+ program_.reset(new ShaderProgram);
+ program_->Link(kVertexShader, kFragmentShader);
+ overlay_program_.reset(new ShaderProgram);
+ overlay_program_->Link(kVertexShader, kOverlayFragmentShader);
+ controller_program_.reset(new ShaderProgram);
+ controller_program_->Link(kVertexShader, kControllerFragmentShader);
+ if (!program_ || !overlay_program_ || !controller_program_)
+ return 1;
+
+ surface_flinger_view_.reset(new SurfaceFlingerView);
+ if (!surface_flinger_view_->Initialize(this))
+ return 1;
+
+ reticle_.reset(new Reticle());
+ if (!reticle_->Initialize())
+ return 1;
+
+ controller_mesh_.reset(new Mesh<vec3, vec3, vec2>());
+ controller_mesh_->SetVertices(kNumControllerMeshVertices,
+ kControllerMeshVertices);
+
+ initialized_ = true;
+
+ return 0;
+}
+
+void ShellView::DeallocateResources() {
+ surface_flinger_view_.reset();
+ reticle_.reset();
+ controller_mesh_.reset();
+ program_.reset(new ShaderProgram);
+ overlay_program_.reset(new ShaderProgram);
+ controller_program_.reset(new ShaderProgram);
+ Application::DeallocateResources();
+}
+
+void ShellView::EnableDebug(bool debug) {
+ QueueTask(debug ? MainThreadTask::EnableDebugMode
+ : MainThreadTask::DisableDebugMode);
+}
+
+void ShellView::VrMode(bool mode) {
+ QueueTask(mode ? MainThreadTask::EnteringVrMode
+ : MainThreadTask::ExitingVrMode);
+}
+
+void ShellView::OnDrawFrame() {
+ textures_.clear();
+ has_ime_ = false;
+
+ {
+ std::unique_lock<std::mutex> l(pending_frame_mutex_);
+ if (!pending_frames_.empty()) {
+ // Check if we should advance the frame.
+ auto& frame = pending_frames_.front();
+ if (!frame.visibility ||
+ frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+ current_frame_ = std::move(frame);
+ pending_frames_.pop_front();
+ }
+ }
+ }
+
+ if (!debug_mode_ && current_frame_.visibility != is_visible_) {
+ SetVisibility(current_frame_.visibility);
+ }
+
+ if (!current_frame_.visibility)
+ return;
+
+ ime_texture_ = TextureLayer();
+
+ surface_flinger_view_->GetTextures(*current_frame_.frame.get(), &textures_,
+ &ime_texture_, debug_mode_);
+ has_ime_ = ime_texture_.texture != nullptr;
+}
+
+void ShellView::DrawEye(EyeType /* eye */, const mat4& perspective,
+ const mat4& eye_matrix, const mat4& head_matrix) {
+ if (should_recenter_) {
+ // Position the quad horizontally aligned in the direction the user
+ // is facing, effectively taking out head roll.
+ initial_head_matrix_ = GetHorizontallyAlignedMatrixFromPose(last_pose_);
+ should_recenter_ = false;
+ }
+
+ size_ = vec2(surface_flinger_view_->width(), surface_flinger_view_->height());
+ scale_ = GetScalingMatrix(size_.x(), size_.y());
+
+ DrawOverlays(perspective, eye_matrix, head_matrix);
+
+ // TODO(alexst): Replicate controller rendering from VR Home.
+ // Current approach in the function below is a quick visualization.
+ DrawController(perspective, eye_matrix, head_matrix);
+
+ // TODO: Make sure reticle is shown only over visible overlays.
+ DrawReticle(perspective, eye_matrix, head_matrix);
+}
+
+void ShellView::OnVisibilityChanged(bool visible) {
+ should_recenter_ = visible;
+ Application::OnVisibilityChanged(visible);
+}
+
+bool ShellView::OnClick(bool down) {
+ if (down) {
+ if (!is_touching_ && allow_input_) {
+ is_touching_ = true;
+ }
+ } else {
+ is_touching_ = false;
+ }
+ Touch();
+ return true;
+}
+
+void ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+ if (!frame || frame->layers().empty())
+ return;
+
+ bool visibility = debug_mode_ || CalculateVisibilityFromLayerConfig(
+ *frame.get(), current_vr_app_);
+ current_vr_app_ = frame->layers().front().appid;
+
+ // If we are not showing the frame there's no need to keep anything around.
+ if (!visibility) {
+ // Hidden, no change so drop it completely
+ if (!current_frame_.visibility)
+ return;
+
+ frame.reset(nullptr);
+ }
+
+ std::unique_lock<std::mutex> l(pending_frame_mutex_);
+
+ pending_frames_.emplace_back(std::move(frame), visibility);
+
+ if (pending_frames_.size() > kMaximumPendingFrames)
+ pending_frames_.pop_front();
+
+ // If we are showing ourselves the main thread is not processing anything,
+ // so give it a kick.
+ if (visibility && !current_frame_.visibility)
+ QueueTask(MainThreadTask::Show);
+}
+
+bool ShellView::IsHit(const vec3& view_location, const vec3& view_direction,
+ vec3* hit_location, vec2* hit_location_in_window_coord,
+ bool test_ime) {
+ mat4 m = initial_head_matrix_ * translate_;
+ if (test_ime)
+ m = m * ime_translate_;
+ mat4 inverse = (m * scale_).inverse();
+ vec4 transformed_loc =
+ inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
+ vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
+ view_direction[2], 0);
+
+ if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
+ return false;
+
+ float distance = -transformed_loc.z() / transformed_dir.z();
+ vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
+ if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
+ return false;
+ if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
+ return false;
+
+ hit_location_in_window_coord->x() =
+ (1 + transformed_hit_loc.x()) / 2 * size_.x();
+ hit_location_in_window_coord->y() =
+ (1 - transformed_hit_loc.y()) / 2 * size_.y();
+
+ *hit_location = view_location + view_direction * distance;
+ return true;
+}
+
+void ShellView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ if (textures_.empty())
+ return;
+
+ program_->Use();
+ mat4 mvp = perspective * eye_matrix * head_matrix;
+ GLint view_projection_location =
+ glGetUniformLocation(program_->GetProgram(), "uViewProjection");
+ glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+ GLint alpha_location =
+ glGetUniformLocation(program_->GetProgram(), "uAlpha");
+
+ GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
+ glUniform1i(tex_location, 0);
+ glActiveTexture(GL_TEXTURE0);
+
+ for (const auto& texture_layer : textures_) {
+ switch (texture_layer.blending) {
+ case HWC2_BLEND_MODE_PREMULTIPLIED:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case HWC2_BLEND_MODE_COVERAGE:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ default:
+ break;
+ }
+
+ glUniform1f(alpha_location, fade_value_ * texture_layer.alpha);
+
+ glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
+
+ mat4 layer_transform = GetLayerTransform(texture_layer, size_.x(),
+ size_.y());
+
+ mat4 transform = initial_head_matrix_ * translate_ * scale_ *
+ layer_transform;
+ DrawWithTransform(transform, *program_);
+
+ glDisable(GL_BLEND);
+ }
+
+ if (has_ime_) {
+ ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
+ static_cast<float>(ime_texture_.display_frame.top));
+ ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
+ ime_texture_.display_frame.left),
+ static_cast<float>(ime_texture_.display_frame.bottom -
+ ime_texture_.display_frame.top));
+
+ DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
+
+ DrawIme();
+ }
+}
+
+void ShellView::DrawIme() {
+ program_->Use();
+ glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
+
+ mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
+
+ mat4 transform = initial_head_matrix_ * translate_ * ime_translate_ * scale_ *
+ layer_transform;
+
+ DrawWithTransform(transform, *program_);
+}
+
+void ShellView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer, const vec2& top_left,
+ const vec2& bottom_right) {
+ overlay_program_->Use();
+ glUniformMatrix4fv(
+ glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
+ 1, 0, mvp.data());
+ glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
+ top_left.x() / size_.x(), top_left.y() / size_.y(),
+ bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ mat4 layer_transform =
+ GetLayerTransform(layer, size_.x(), size_.y());
+
+ mat4 transform =
+ initial_head_matrix_ * translate_ * scale_ * layer_transform;
+ DrawWithTransform(transform, *overlay_program_);
+ glDisable(GL_BLEND);
+}
+
+void ShellView::DrawWithTransform(const mat4& transform,
+ const ShaderProgram& program) {
+ GLint transform_location =
+ glGetUniformLocation(program.GetProgram(), "uTransform");
+ glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+bool ShellView::IsImeHit(const vec3& view_location, const vec3& view_direction,
+ vec3 *hit_location) {
+ // First, check if the IME window is hit.
+ bool is_hit = IsHit(view_location, view_direction, hit_location,
+ &hit_location_in_window_coord_, true);
+ if (is_hit) {
+ // If it is, check if the window coordinate is in the IME region;
+ // if so then we are done.
+ if (IsInside(hit_location_in_window_coord_, ime_top_left_,
+ ime_top_left_ + ime_size_)) {
+ allow_input_ = true;
+ return true;
+ }
+ }
+
+ allow_input_ = false;
+ // Check if we have hit the main window.
+ is_hit = IsHit(view_location, view_direction, hit_location,
+ &hit_location_in_window_coord_, false);
+ if (is_hit) {
+ // Only allow input if we are not hitting the region hidden by the IME.
+ // Allowing input here would cause clicks on the main window to actually
+ // be clicks on the IME.
+ if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
+ ime_top_left_ + ime_size_)) {
+ allow_input_ = true;
+ }
+ }
+ return is_hit;
+}
+
+void ShellView::DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ reticle_->Hide();
+
+ vec3 pointer_location = last_pose_.GetPosition();
+ quat view_quaternion = last_pose_.GetRotation();
+
+ if (controller_api_status_ == gvr::kControllerApiOk) {
+ view_quaternion = FromGvrQuatf(controller_orientation_);
+ vec4 controller_location = controller_translate_ * vec4(0, 0, 0, 1);
+ pointer_location = vec3(controller_location.x(), controller_location.y(),
+ controller_location.z());
+
+ if (controller_state_->GetButtonDown(gvr::kControllerButtonClick))
+ OnClick(true);
+
+ if (controller_state_->GetButtonUp(gvr::kControllerButtonClick))
+ OnClick(false);
+ }
+
+ vec3 view_direction = vec3(view_quaternion * vec3(0, 0, -1));
+
+ vec3 hit_location;
+
+ bool is_hit;
+ if(has_ime_) {
+ // This will set allow_input_ and hit_location_in_window_coord_.
+ is_hit = IsImeHit(pointer_location, view_direction, &hit_location);
+ } else {
+ is_hit = IsHit(pointer_location, view_direction, &hit_location,
+ &hit_location_in_window_coord_, false);
+ allow_input_ = is_hit;
+ }
+
+ if (is_hit) {
+ reticle_->ShowAt(
+ Eigen::Translation3f(hit_location) * view_quaternion.matrix(),
+ allow_input_ ? vec3(1, 0, 0) : vec3(0, 0, 0));
+ Touch();
+ }
+
+ reticle_->Draw(perspective, eye_matrix, head_matrix);
+}
+
+void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) {
+ controller_program_->Use();
+ mat4 mvp = perspective * eye_matrix * head_matrix;
+
+ GLint view_projection_location = glGetUniformLocation(
+ controller_program_->GetProgram(), "uViewProjection");
+ glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+ quat view_quaternion = FromGvrQuatf(controller_orientation_);
+ view_quaternion.toRotationMatrix();
+
+ vec3 world_pos = last_pose_.GetPosition() + controller_position_;
+
+ controller_translate_ =
+ Eigen::Translation3f(world_pos.x(), world_pos.y(), world_pos.z());
+
+ mat4 transform = controller_translate_ * view_quaternion *
+ mat4(Eigen::Scaling<float>(1, 1, 3.0));
+ GLint transform_location =
+ glGetUniformLocation(controller_program_->GetProgram(), "uTransform");
+ glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+ controller_mesh_->Draw();
+}
+
+bool ShellView::InitializeTouch() {
+ virtual_touchpad_ =
+ android::interface_cast<android::dvr::IVirtualTouchpadService>(
+ android::defaultServiceManager()->getService(
+ android::String16("virtual_touchpad")));
+ if (!virtual_touchpad_.get()) {
+ ALOGE("Failed to connect to virtual touchpad");
+ return false;
+ }
+ return true;
+}
+
+void ShellView::Touch() {
+ if (!virtual_touchpad_.get()) {
+ ALOGE("missing virtual touchpad");
+ // Try to reconnect; useful in development.
+ if (!InitializeTouch()) {
+ return;
+ }
+ }
+
+ const android::binder::Status status = virtual_touchpad_->touch(
+ hit_location_in_window_coord_.x() / size_.x(),
+ hit_location_in_window_coord_.y() / size_.y(),
+ is_touching_ ? 1.0f : 0.0f);
+ if (!status.isOk()) {
+ ALOGE("touch failed: %s", status.toString8().string());
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
new file mode 100644
index 0000000..0688c94
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -0,0 +1,119 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEW_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEW_H_
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <android/dvr/IVirtualTouchpadService.h>
+
+#include <deque>
+
+#include "application.h"
+#include "reticle.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+class ShellView : public Application, public HwcCallback::Client {
+ public:
+ ShellView();
+ virtual ~ShellView();
+
+ int Initialize(JNIEnv* env, jobject app_context,
+ jobject class_loader) override;
+
+ int AllocateResources() override;
+ void DeallocateResources() override;
+
+ void EnableDebug(bool debug);
+ void VrMode(bool mode);
+
+ protected:
+ void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix) override;
+ void OnVisibilityChanged(bool visible) override;
+
+ void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+ void DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+ void DrawIme();
+ void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+ const vec2& top_left, const vec2& bottom_right);
+ void DrawController(const mat4& perspective, const mat4& eye_matrix,
+ const mat4& head_matrix);
+
+ bool IsHit(const vec3& view_location, const vec3& view_direction,
+ vec3* hit_location, vec2* hit_location_in_window_coord,
+ bool test_ime);
+ bool IsImeHit(const vec3& view_location, const vec3& view_direction,
+ vec3 *hit_location);
+ bool InitializeTouch();
+ void Touch();
+
+ void OnDrawFrame() override;
+ void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
+
+ bool OnClick(bool down);
+
+ // HwcCallback::Client:
+ void OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
+
+ std::unique_ptr<ShaderProgram> program_;
+ std::unique_ptr<ShaderProgram> overlay_program_;
+ std::unique_ptr<ShaderProgram> controller_program_;
+
+ uint32_t current_vr_app_;
+
+ // Used to center the scene when the shell becomes visible.
+ bool should_recenter_ = true;
+ mat4 initial_head_matrix_;
+ mat4 scale_;
+ mat4 translate_;
+ mat4 ime_translate_;
+ vec2 size_;
+
+ std::unique_ptr<SurfaceFlingerView> surface_flinger_view_;
+ std::unique_ptr<Reticle> reticle_;
+ sp<IVirtualTouchpadService> virtual_touchpad_;
+ std::vector<TextureLayer> textures_;
+ TextureLayer ime_texture_;
+
+ bool is_touching_ = false;
+ bool allow_input_ = false;
+ vec2 hit_location_in_window_coord_;
+ vec2 ime_top_left_;
+ vec2 ime_size_;
+ bool has_ime_ = false;
+
+ std::unique_ptr<Mesh<vec3, vec3, vec2>> controller_mesh_;
+
+ struct PendingFrame {
+ PendingFrame() = default;
+ PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame, bool visibility)
+ : frame(std::move(frame)), visibility(visibility) {}
+ PendingFrame(PendingFrame&& r)
+ : frame(std::move(r.frame)), visibility(r.visibility) {}
+
+ void operator=(PendingFrame&& r) {
+ frame.reset(r.frame.release());
+ visibility = r.visibility;
+ }
+
+ std::unique_ptr<HwcCallback::Frame> frame;
+ bool visibility = false;
+ };
+ std::deque<PendingFrame> pending_frames_;
+ std::mutex pending_frame_mutex_;
+ PendingFrame current_frame_;
+
+ mat4 controller_translate_;
+
+ ShellView(const ShellView&) = delete;
+ void operator=(const ShellView&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_SHELL_VIEW_H_
diff --git a/services/vr/vr_window_manager/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
new file mode 100644
index 0000000..d38fcc0
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.cpp
@@ -0,0 +1,79 @@
+#include "surface_flinger_view.h"
+
+#include <binder/IServiceManager.h>
+#include <impl/vr_composer_view.h>
+#include <private/dvr/native_buffer.h>
+
+#include "hwc_callback.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+SurfaceFlingerView::SurfaceFlingerView() {}
+
+SurfaceFlingerView::~SurfaceFlingerView() {}
+
+bool SurfaceFlingerView::Initialize(HwcCallback::Client *client) {
+ const char instance[] = "DaydreamDisplay";
+ composer_service_ = IVrComposerView::getService(instance);
+ if (composer_service_ == nullptr) {
+ ALOGE("Failed to initialize composer service");
+ return false;
+ }
+
+ if (!composer_service_->isRemote()) {
+ ALOGE("Composer service is not remote");
+ return false;
+ }
+
+ // TODO(dnicoara): Query this from the composer service.
+ width_ = 1920;
+ height_ = 1080;
+
+ composer_observer_.reset(new HwcCallback(composer_service_.get(), client));
+ return true;
+}
+
+bool SurfaceFlingerView::GetTextures(const HwcCallback::Frame& frame,
+ std::vector<TextureLayer>* texture_layers,
+ TextureLayer* ime_layer,
+ bool debug) const {
+ auto& layers = frame.layers();
+ texture_layers->clear();
+
+ size_t start = 0;
+ // Skip the second layer if it is from the VR app.
+ if (!debug) {
+ start = 1;
+ if (layers[0].appid && layers[0].appid == layers[1].appid)
+ start = 2;
+ }
+
+ for (size_t i = start; i < layers.size(); ++i) {
+ if (!debug && layers[i].should_skip_layer())
+ continue;
+
+ std::unique_ptr<Texture> texture(new Texture());
+ if (!texture->Initialize(layers[i].buffer->getNativeBuffer())) {
+ ALOGE("Failed to create texture");
+ texture_layers->clear();
+ return false;
+ }
+
+ TextureLayer texture_layer = {
+ std::move(texture), layers[i].crop, layers[i].display_frame,
+ layers[i].blending, layers[i].alpha,
+ };
+ if (debug && layers[i].type == HwcCallback::HwcLayer::kInputMethod) {
+ *ime_layer = std::move(texture_layer);
+ } else {
+ texture_layers->emplace_back(std::move(texture_layer));
+ }
+ }
+
+ return true;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/services/vr/vr_window_manager/surface_flinger_view.h b/services/vr/vr_window_manager/surface_flinger_view.h
new file mode 100644
index 0000000..e079cdb
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.h
@@ -0,0 +1,52 @@
+#ifndef APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+#define APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+#include "hwc_callback.h"
+
+namespace android {
+namespace dvr {
+
+class IDisplay;
+class Texture;
+
+struct TextureLayer {
+ std::unique_ptr<Texture> texture;
+ Rectf crop;
+ Recti display_frame;
+ int32_t blending;
+ float alpha;
+};
+
+class SurfaceFlingerView {
+ public:
+ SurfaceFlingerView();
+ ~SurfaceFlingerView();
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+ bool Initialize(HwcCallback::Client *client);
+
+ bool GetTextures(const HwcCallback::Frame& layers,
+ std::vector<TextureLayer>* texture_layers,
+ TextureLayer* ime_layer, bool debug) const;
+
+ private:
+ sp<IVrComposerView> composer_service_;
+ std::unique_ptr<HwcCallback> composer_observer_;
+
+ int width_ = 0;
+ int height_ = 0;
+
+ SurfaceFlingerView(const SurfaceFlingerView&) = delete;
+ void operator=(const SurfaceFlingerView&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
diff --git a/services/vr/vr_window_manager/texture.cpp b/services/vr/vr_window_manager/texture.cpp
new file mode 100644
index 0000000..dbd91b7
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.cpp
@@ -0,0 +1,41 @@
+#include "texture.h"
+
+#include <cutils/log.h>
+#include <GLES/glext.h>
+#include <system/window.h>
+
+namespace android {
+namespace dvr {
+
+Texture::Texture() {}
+
+Texture::~Texture() {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (id_)
+ glDeleteTextures(1, &id_);
+ if (image_)
+ eglDestroyImageKHR(display, image_);
+}
+
+bool Texture::Initialize(ANativeWindowBuffer* buffer) {
+ width_ = buffer->width;
+ height_ = buffer->height;
+
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ image_ = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, buffer, nullptr);
+ if (!image_) {
+ ALOGE("Failed to create eglImage");
+ return false;
+ }
+
+ glGenTextures(1, &id_);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, id_);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+
+ return true;
+}
+
+} // namespace android
+} // namespace dvr
diff --git a/services/vr/vr_window_manager/texture.h b/services/vr/vr_window_manager/texture.h
new file mode 100644
index 0000000..9840f19
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.h
@@ -0,0 +1,37 @@
+#ifndef VR_WINDOW_MANAGER_TEXTURE_H_
+#define VR_WINDOW_MANAGER_TEXTURE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace dvr {
+
+class Texture {
+ public:
+ explicit Texture();
+ ~Texture();
+
+ bool Initialize(ANativeWindowBuffer* buffer);
+
+ GLuint id() const { return id_; }
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+ private:
+ EGLImageKHR image_ = nullptr;
+ GLuint id_ = 0;
+ int width_ = 0;
+ int height_ = 0;
+
+ Texture(const Texture&) = delete;
+ void operator=(const Texture&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // VR_WINDOW_MANAGER_TEXTURE_H_
diff --git a/services/vr/vr_window_manager/vr_window_manager_jni.cpp b/services/vr/vr_window_manager/vr_window_manager_jni.cpp
new file mode 100644
index 0000000..f52658a
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_jni.cpp
@@ -0,0 +1,58 @@
+#include <cutils/log.h>
+#include <jni.h>
+
+#include <memory>
+
+#include "render_thread.h"
+
+#define JNI_METHOD(return_type, method_name) \
+ JNIEXPORT return_type JNICALL \
+ Java_com_google_vr_windowmanager_VrWindowManagerService_##method_name
+
+namespace {
+
+inline jlong jptr(android::dvr::RenderThread* native_vr_window_manager) {
+ return reinterpret_cast<intptr_t>(native_vr_window_manager);
+}
+
+inline android::dvr::RenderThread* native(jlong ptr) {
+ return reinterpret_cast<android::dvr::RenderThread*>(ptr);
+}
+
+} // namespace
+
+extern "C" {
+
+JNI_METHOD(jlong, nativeCreate)(JNIEnv* env, jclass /*clazz*/,
+ jobject class_loader,
+ jobject android_context) {
+ return jptr(new android::dvr::RenderThread(
+ env, class_loader, android_context));
+}
+
+JNI_METHOD(void, nativeDestroy)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+ delete native(native_render_thread);
+}
+
+JNI_METHOD(void, nativeEnableDebug)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+ native(native_render_thread)->EnableDebug(true);
+}
+
+JNI_METHOD(void, nativeDisableDebug)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+ native(native_render_thread)->EnableDebug(false);
+}
+
+JNI_METHOD(void, nativeEnterVrMode)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+ native(native_render_thread)->VrMode(true);
+}
+
+JNI_METHOD(void, nativeExitVrMode)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+ native(native_render_thread)->VrMode(false);
+}
+
+} // extern "C"