Add DaydreamVR native libraries and services
Upstreaming the main VR system components from master-dreamos-dev
into goog/master.
Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/libs/vr/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