| #include <dvr/dvr_api.h> |
| #include <dvr/dvr_buffer_queue.h> |
| #include <gui/Surface.h> |
| #include <private/dvr/buffer_hub_queue_client.h> |
| |
| #include <base/logging.h> |
| #include <gtest/gtest.h> |
| |
| #include "../dvr_internal.h" |
| #include "../dvr_buffer_queue_internal.h" |
| |
| namespace android { |
| namespace dvr { |
| |
| namespace { |
| |
| static constexpr uint32_t kBufferWidth = 100; |
| static constexpr uint32_t kBufferHeight = 1; |
| static constexpr uint32_t kLayerCount = 1; |
| static constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; |
| static constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; |
| static constexpr size_t kQueueCapacity = 3; |
| |
| typedef uint64_t TestMeta; |
| |
| class DvrBufferQueueTest : public ::testing::Test { |
| public: |
| static void BufferAvailableCallback(void* context) { |
| DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context); |
| thiz->HandleBufferAvailable(); |
| } |
| |
| static void BufferRemovedCallback(DvrReadBuffer* buffer, void* context) { |
| DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context); |
| thiz->HandleBufferRemoved(buffer); |
| } |
| |
| protected: |
| void SetUp() override { |
| config_builder_ = ProducerQueueConfigBuilder() |
| .SetDefaultWidth(kBufferWidth) |
| .SetDefaultHeight(kBufferHeight) |
| .SetDefaultFormat(kBufferFormat) |
| .SetMetadata<TestMeta>(); |
| } |
| |
| void TearDown() override { |
| if (write_queue_ != nullptr) { |
| dvrWriteBufferQueueDestroy(write_queue_); |
| write_queue_ = nullptr; |
| } |
| } |
| |
| void CreateWriteBufferQueue() { |
| write_queue_ = new DvrWriteBufferQueue( |
| ProducerQueue::Create(config_builder_.Build(), UsagePolicy{})); |
| ASSERT_NE(nullptr, write_queue_); |
| } |
| |
| void AllocateBuffers(size_t buffer_count) { |
| auto status = write_queue_->producer_queue()->AllocateBuffers( |
| kBufferWidth, kBufferHeight, kLayerCount, kBufferFormat, kBufferUsage, |
| buffer_count); |
| ASSERT_TRUE(status.ok()); |
| } |
| |
| void HandleBufferAvailable() { |
| buffer_available_count_ += 1; |
| ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_); |
| } |
| |
| void HandleBufferRemoved(DvrReadBuffer* buffer) { |
| buffer_removed_count_ += 1; |
| ALOGD_IF(TRACE, "Buffer removed, buffer=%p, count=%d", buffer, |
| buffer_removed_count_); |
| } |
| |
| ProducerQueueConfigBuilder config_builder_; |
| DvrWriteBufferQueue* write_queue_{nullptr}; |
| int buffer_available_count_{0}; |
| int buffer_removed_count_{0}; |
| }; |
| |
| TEST_F(DvrBufferQueueTest, TestWrite_QueueCreateDestroy) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| |
| dvrWriteBufferQueueDestroy(write_queue_); |
| write_queue_ = nullptr; |
| } |
| |
| TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); |
| |
| size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_); |
| |
| ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity); |
| ASSERT_EQ(kQueueCapacity, capacity); |
| } |
| |
| TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| |
| DvrReadBufferQueue* read_queue = nullptr; |
| int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); |
| |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, read_queue); |
| |
| dvrReadBufferQueueDestroy(read_queue); |
| } |
| |
| TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| |
| DvrReadBufferQueue* read_queue1 = nullptr; |
| DvrReadBufferQueue* read_queue2 = nullptr; |
| int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); |
| |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, read_queue1); |
| |
| ret = dvrReadBufferQueueCreateReadQueue(read_queue1, &read_queue2); |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, read_queue2); |
| ASSERT_NE(read_queue1, read_queue2); |
| |
| dvrReadBufferQueueDestroy(read_queue1); |
| dvrReadBufferQueueDestroy(read_queue2); |
| } |
| |
| TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(3)); |
| |
| DvrReadBuffer* read_buffer = nullptr; |
| DvrWriteBuffer* write_buffer = nullptr; |
| |
| EXPECT_FALSE(dvrReadBufferIsValid(read_buffer)); |
| EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); |
| |
| dvrReadBufferCreateEmpty(&read_buffer); |
| ASSERT_NE(nullptr, read_buffer); |
| |
| dvrWriteBufferCreateEmpty(&write_buffer); |
| ASSERT_NE(nullptr, write_buffer); |
| |
| EXPECT_FALSE(dvrReadBufferIsValid(read_buffer)); |
| EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); |
| |
| DvrReadBufferQueue* read_queue = nullptr; |
| |
| ASSERT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); |
| |
| const int kTimeoutMs = 0; |
| int fence_fd = -1; |
| ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeoutMs, |
| write_buffer, &fence_fd)); |
| EXPECT_EQ(-1, fence_fd); |
| EXPECT_TRUE(dvrWriteBufferIsValid(write_buffer)); |
| |
| ASSERT_EQ(0, dvrWriteBufferClear(write_buffer)); |
| EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer)); |
| } |
| |
| TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); |
| |
| static constexpr int kTimeout = 0; |
| DvrReadBufferQueue* read_queue = nullptr; |
| DvrReadBuffer* rb = nullptr; |
| DvrWriteBuffer* wb = nullptr; |
| int fence_fd = -1; |
| |
| int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); |
| |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, read_queue); |
| |
| dvrReadBufferQueueSetBufferAvailableCallback(read_queue, |
| &BufferAvailableCallback, this); |
| |
| dvrWriteBufferCreateEmpty(&wb); |
| ASSERT_NE(nullptr, wb); |
| |
| dvrReadBufferCreateEmpty(&rb); |
| ASSERT_NE(nullptr, rb); |
| |
| // Gain buffer for writing. |
| ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd); |
| ASSERT_EQ(0, ret); |
| ASSERT_TRUE(dvrWriteBufferIsValid(wb)); |
| ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", |
| wb, fence_fd); |
| pdx::LocalHandle release_fence(fence_fd); |
| |
| // Post buffer to the read_queue. |
| TestMeta seq = 42U; |
| ret = dvrWriteBufferPost(wb, /* fence */ -1, &seq, sizeof(seq)); |
| ASSERT_EQ(0, ret); |
| dvrWriteBufferDestroy(wb); |
| wb = nullptr; |
| |
| // Acquire buffer for reading. |
| TestMeta acquired_seq = 0U; |
| ret = dvrReadBufferQueueDequeue(read_queue, kTimeout, rb, &fence_fd, |
| &acquired_seq, sizeof(acquired_seq)); |
| ASSERT_EQ(0, ret); |
| |
| // Dequeue is successfully, BufferAvailableCallback should be fired once. |
| ASSERT_EQ(1, buffer_available_count_); |
| ASSERT_TRUE(dvrReadBufferIsValid(rb)); |
| ASSERT_EQ(seq, acquired_seq); |
| ALOGD_IF(TRACE, |
| "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb, |
| fence_fd); |
| pdx::LocalHandle acquire_fence(fence_fd); |
| |
| // Release buffer to the write_queue. |
| ret = dvrReadBufferRelease(rb, -1); |
| ASSERT_EQ(0, ret); |
| dvrReadBufferDestroy(rb); |
| rb = nullptr; |
| |
| // TODO(b/34387835) Currently buffer allocation has to happen after all queues |
| // are initialized. |
| size_t capacity = dvrReadBufferQueueGetCapacity(read_queue); |
| |
| ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity); |
| ASSERT_EQ(kQueueCapacity, capacity); |
| |
| dvrReadBufferQueueDestroy(read_queue); |
| } |
| |
| TEST_F(DvrBufferQueueTest, TestGetExternalSurface) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| |
| ANativeWindow* window = nullptr; |
| |
| // The |write_queue_| doesn't have proper metadata (must be |
| // DvrNativeBufferMetadata) configured during creation. |
| int ret = dvrWriteBufferQueueGetExternalSurface(write_queue_, &window); |
| ASSERT_EQ(-EINVAL, ret); |
| ASSERT_EQ(nullptr, window); |
| |
| // A write queue with DvrNativeBufferMetadata should work fine. |
| auto config = ProducerQueueConfigBuilder() |
| .SetMetadata<DvrNativeBufferMetadata>() |
| .Build(); |
| std::unique_ptr<DvrWriteBufferQueue, decltype(&dvrWriteBufferQueueDestroy)> |
| write_queue( |
| new DvrWriteBufferQueue(ProducerQueue::Create(config, UsagePolicy{})), |
| dvrWriteBufferQueueDestroy); |
| ASSERT_NE(nullptr, write_queue.get()); |
| |
| ret = dvrWriteBufferQueueGetExternalSurface(write_queue.get(), &window); |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, window); |
| |
| sp<Surface> surface = static_cast<Surface*>(window); |
| ASSERT_TRUE(Surface::isValid(surface)); |
| } |
| |
| // Create buffer queue of three buffers and dequeue three buffers out of it. |
| // Before each dequeue operation, we resize the buffer queue and expect the |
| // queue always return buffer with desired dimension. |
| TEST_F(DvrBufferQueueTest, TestResizeBuffer) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); |
| |
| static constexpr int kTimeout = 0; |
| int fence_fd = -1; |
| |
| DvrReadBufferQueue* read_queue = nullptr; |
| DvrWriteBuffer* wb1 = nullptr; |
| DvrWriteBuffer* wb2 = nullptr; |
| DvrWriteBuffer* wb3 = nullptr; |
| AHardwareBuffer* ahb1 = nullptr; |
| AHardwareBuffer* ahb2 = nullptr; |
| AHardwareBuffer* ahb3 = nullptr; |
| AHardwareBuffer_Desc buffer_desc; |
| |
| int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); |
| |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, read_queue); |
| |
| dvrReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback, |
| this); |
| |
| dvrWriteBufferCreateEmpty(&wb1); |
| ASSERT_NE(nullptr, wb1); |
| dvrWriteBufferCreateEmpty(&wb2); |
| ASSERT_NE(nullptr, wb2); |
| dvrWriteBufferCreateEmpty(&wb3); |
| ASSERT_NE(nullptr, wb3); |
| |
| // Handle all pending events on the read queue. |
| ret = dvrReadBufferQueueHandleEvents(read_queue); |
| ASSERT_EQ(0, ret); |
| |
| size_t capacity = dvrReadBufferQueueGetCapacity(read_queue); |
| ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity); |
| ASSERT_EQ(kQueueCapacity, capacity); |
| |
| // Resize before dequeuing. |
| constexpr uint32_t w1 = 10; |
| ret = dvrWriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight); |
| ASSERT_EQ(0, ret); |
| |
| // Gain first buffer for writing. All buffers will be resized. |
| ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb1, &fence_fd); |
| ASSERT_EQ(0, ret); |
| ASSERT_TRUE(dvrWriteBufferIsValid(wb1)); |
| ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1); |
| pdx::LocalHandle release_fence1(fence_fd); |
| |
| // Check the buffer dimension. |
| ret = dvrWriteBufferGetAHardwareBuffer(wb1, &ahb1); |
| ASSERT_EQ(0, ret); |
| AHardwareBuffer_describe(ahb1, &buffer_desc); |
| ASSERT_EQ(w1, buffer_desc.width); |
| ASSERT_EQ(kBufferHeight, buffer_desc.height); |
| AHardwareBuffer_release(ahb1); |
| |
| // For the first resize, all buffers are reallocated. |
| int expected_buffer_removed_count = kQueueCapacity; |
| ret = dvrReadBufferQueueHandleEvents(read_queue); |
| ASSERT_EQ(0, ret); |
| ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); |
| |
| // Resize the queue. We are testing with blob format, keep height to be 1. |
| constexpr uint32_t w2 = 20; |
| ret = dvrWriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight); |
| ASSERT_EQ(0, ret); |
| |
| // The next buffer we dequeued should have new width. |
| ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb2, &fence_fd); |
| ASSERT_EQ(0, ret); |
| ASSERT_TRUE(dvrWriteBufferIsValid(wb2)); |
| ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2, |
| fence_fd); |
| pdx::LocalHandle release_fence2(fence_fd); |
| |
| // Check the buffer dimension, should be new width |
| ret = dvrWriteBufferGetAHardwareBuffer(wb2, &ahb2); |
| ASSERT_EQ(0, ret); |
| AHardwareBuffer_describe(ahb2, &buffer_desc); |
| ASSERT_EQ(w2, buffer_desc.width); |
| AHardwareBuffer_release(ahb2); |
| |
| // For the second resize, all but one buffers are reallocated. |
| expected_buffer_removed_count += (kQueueCapacity - 1); |
| ret = dvrReadBufferQueueHandleEvents(read_queue); |
| ASSERT_EQ(0, ret); |
| ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); |
| |
| // Resize the queue for the third time. |
| constexpr uint32_t w3 = 30; |
| ret = dvrWriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight); |
| ASSERT_EQ(0, ret); |
| |
| // The next buffer we dequeued should have new width. |
| ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb3, &fence_fd); |
| ASSERT_EQ(0, ret); |
| ASSERT_TRUE(dvrWriteBufferIsValid(wb3)); |
| ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3, |
| fence_fd); |
| pdx::LocalHandle release_fence3(fence_fd); |
| |
| // Check the buffer dimension, should be new width |
| ret = dvrWriteBufferGetAHardwareBuffer(wb3, &ahb3); |
| ASSERT_EQ(0, ret); |
| AHardwareBuffer_describe(ahb3, &buffer_desc); |
| ASSERT_EQ(w3, buffer_desc.width); |
| AHardwareBuffer_release(ahb3); |
| |
| // For the third resize, all but two buffers are reallocated. |
| expected_buffer_removed_count += (kQueueCapacity - 2); |
| ret = dvrReadBufferQueueHandleEvents(read_queue); |
| ASSERT_EQ(0, ret); |
| ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); |
| |
| dvrReadBufferQueueDestroy(read_queue); |
| } |
| |
| TEST_F(DvrBufferQueueTest, DequeueEmptyMetadata) { |
| // Overrides default queue parameters: Empty metadata. |
| config_builder_.SetMetadata<void>(); |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1)); |
| |
| DvrReadBuffer* rb = nullptr; |
| DvrWriteBuffer* wb = nullptr; |
| dvrReadBufferCreateEmpty(&rb); |
| dvrWriteBufferCreateEmpty(&wb); |
| |
| DvrReadBufferQueue* read_queue = nullptr; |
| EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); |
| |
| const int kTimeoutMs = 0; |
| int fence_fd = -1; |
| EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd)); |
| |
| EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, nullptr, 0)); |
| EXPECT_EQ(0, dvrWriteBufferClear(wb)); |
| dvrWriteBufferDestroy(wb); |
| wb = nullptr; |
| |
| // When acquire buffer, it's legit to pass nullptr as out_meta iff metadata |
| // size is Zero. |
| EXPECT_EQ(0, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd, |
| nullptr, 0)); |
| EXPECT_TRUE(dvrReadBufferIsValid(rb)); |
| } |
| |
| TEST_F(DvrBufferQueueTest, DequeueMismatchMetadata) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1)); |
| |
| DvrReadBuffer* rb = nullptr; |
| DvrWriteBuffer* wb = nullptr; |
| dvrReadBufferCreateEmpty(&rb); |
| dvrWriteBufferCreateEmpty(&wb); |
| |
| DvrReadBufferQueue* read_queue = nullptr; |
| EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); |
| |
| const int kTimeoutMs = 0; |
| int fence_fd = -1; |
| EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd)); |
| |
| TestMeta seq = 42U; |
| EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, &seq, sizeof(seq))); |
| EXPECT_EQ(0, dvrWriteBufferClear(wb)); |
| dvrWriteBufferDestroy(wb); |
| wb = nullptr; |
| |
| // Dequeue with wrong metadata will cause EINVAL. |
| int8_t wrong_metadata; |
| EXPECT_EQ(-EINVAL, |
| dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd, |
| &wrong_metadata, sizeof(wrong_metadata))); |
| EXPECT_FALSE(dvrReadBufferIsValid(rb)); |
| |
| // Dequeue with empty metadata will cause EINVAL. |
| EXPECT_EQ(-EINVAL, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, |
| &fence_fd, nullptr, 0)); |
| EXPECT_FALSE(dvrReadBufferIsValid(rb)); |
| } |
| |
| TEST_F(DvrBufferQueueTest, TestReadQueueEventFd) { |
| ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue()); |
| ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity)); |
| |
| DvrReadBufferQueue* read_queue = nullptr; |
| int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); |
| |
| ASSERT_EQ(0, ret); |
| ASSERT_NE(nullptr, read_queue); |
| |
| int event_fd = dvrReadBufferQueueGetEventFd(read_queue); |
| ASSERT_GT(event_fd, 0); |
| } |
| |
| } // namespace |
| |
| } // namespace dvr |
| } // namespace android |