#include #include #include #include #include #include #include #include "dvr_api_test.h" #define LOG_TAG "dvr_buffer_queue-test" #ifndef ALOGD #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #endif #ifndef ALOGD_IF #define ALOGD_IF(cond, ...) \ ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0) #endif namespace { static constexpr uint32_t kBufferWidth = 100; static constexpr uint32_t kBufferHeight = 1; static constexpr uint32_t kLayerCount = 1; static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB; static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; static constexpr size_t kQueueCapacity = 3; class DvrBufferQueueTest : public DvrApiTest { public: static void BufferAvailableCallback(void* context) { DvrBufferQueueTest* thiz = static_cast(context); thiz->HandleBufferAvailable(); } static void BufferRemovedCallback(DvrReadBuffer* buffer, void* context) { DvrBufferQueueTest* thiz = static_cast(context); thiz->HandleBufferRemoved(buffer); } protected: void TearDown() override { if (write_queue_ != nullptr) { api_.WriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } DvrApiTest::TearDown(); } 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_); } DvrWriteBufferQueue* write_queue_ = nullptr; int buffer_available_count_{0}; int buffer_removed_count_{0}; }; TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); api_.WriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); size_t capacity = api_.WriteBufferQueueGetCapacity(write_queue_); ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); } TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); api_.ReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue1 = nullptr; DvrReadBufferQueue* read_queue2 = nullptr; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue1); ret = api_.ReadBufferQueueCreateReadQueue(read_queue1, &read_queue2); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue2); ASSERT_NE(read_queue1, read_queue2); api_.ReadBufferQueueDestroy(read_queue1); api_.ReadBufferQueueDestroy(read_queue2); } TEST_F(DvrBufferQueueTest, GainBuffer) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrWriteBuffer* wb = nullptr; EXPECT_FALSE(api_.WriteBufferIsValid(wb)); DvrNativeBufferMetadata meta; int fence_fd = -1; ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta, &fence_fd); ASSERT_EQ(ret, 0); EXPECT_EQ(fence_fd, -1); EXPECT_NE(wb, nullptr); EXPECT_TRUE(api_.WriteBufferIsValid(wb)); } TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrReadBufferQueue* read_queue = nullptr; DvrReadBuffer* rb = nullptr; DvrWriteBuffer* wb = nullptr; DvrNativeBufferMetadata meta1; DvrNativeBufferMetadata meta2; int fence_fd = -1; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(ret, 0); ASSERT_NE(read_queue, nullptr); api_.ReadBufferQueueSetBufferAvailableCallback( read_queue, &BufferAvailableCallback, this); // Gain buffer for writing. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1, &fence_fd); ASSERT_EQ(ret, 0); ASSERT_NE(wb, nullptr); ASSERT_TRUE(api_.WriteBufferIsValid(wb)); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", wb, fence_fd); close(fence_fd); // Post buffer to the read_queue. meta1.timestamp = 42; ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); ASSERT_EQ(ret, 0); ASSERT_FALSE(api_.WriteBufferIsValid(wb)); wb = nullptr; // Acquire buffer for reading. ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, &meta2, &fence_fd); ASSERT_EQ(ret, 0); ASSERT_NE(rb, nullptr); // Dequeue is successfully, BufferAvailableCallback should be fired once. ASSERT_EQ(buffer_available_count_, 1); ASSERT_TRUE(api_.ReadBufferIsValid(rb)); // Metadata should be passed along from producer to consumer properly. ASSERT_EQ(meta1.timestamp, meta2.timestamp); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb, fence_fd); close(fence_fd); // Release buffer to the write_queue. ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); ASSERT_FALSE(api_.ReadBufferIsValid(rb)); rb = nullptr; // TODO(b/34387835) Currently buffer allocation has to happen after all queues // are initialized. size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); api_.ReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, GetANativeWindow) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, /*user_metadata_size=*/0, &write_queue_); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, write_queue_); ANativeWindow* window = nullptr; ret = api_.WriteBufferQueueGetANativeWindow(write_queue_, &window); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, window); uint32_t width = ANativeWindow_getWidth(window); uint32_t height = ANativeWindow_getHeight(window); uint32_t format = ANativeWindow_getFormat(window); ASSERT_EQ(kBufferWidth, width); ASSERT_EQ(kBufferHeight, height); ASSERT_EQ(kBufferFormat, format); } // 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, ResizeBuffer) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); int fence_fd = -1; DvrNativeBufferMetadata meta; 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; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); api_.ReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback, this); // Handle all pending events on the read queue. ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue); ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); // Resize before dequeuing. constexpr uint32_t w1 = 10; ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight); ASSERT_EQ(0, ret); // Gain first buffer for writing. All buffers will be resized. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(api_.WriteBufferIsValid(wb1)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1); close(fence_fd); // Check the buffer dimension. ret = api_.WriteBufferGetAHardwareBuffer(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 = api_.ReadBufferQueueHandleEvents(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 = api_.WriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight); ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(api_.WriteBufferIsValid(wb2)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2, fence_fd); close(fence_fd); // Check the buffer dimension, should be new width ret = api_.WriteBufferGetAHardwareBuffer(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 = api_.ReadBufferQueueHandleEvents(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 = api_.WriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight); ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(api_.WriteBufferIsValid(wb3)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3, fence_fd); close(fence_fd); // Check the buffer dimension, should be new width ret = api_.WriteBufferGetAHardwareBuffer(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 = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); api_.ReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, ReadQueueEventFd) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); int event_fd = api_.ReadBufferQueueGetEventFd(read_queue); ASSERT_GT(event_fd, 0); } // Verifies a Dvr{Read,Write}BufferQueue contains the same set of // Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id, // the corresponding AHardwareBuffer handle stays the same. TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); int fence_fd = -1; DvrReadBufferQueue* read_queue = nullptr; EXPECT_EQ(0, api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); // Read buffers. std::array rbs; // Write buffers. std::array wbs; // Buffer metadata. std::array metas; // Hardware buffers for Read buffers. std::unordered_map rhbs; // Hardware buffers for Write buffers. std::unordered_map whbs; constexpr int kNumTests = 100; // This test runs the following operations many many times. Thus we prefer to // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output. std::function Gain = [&](size_t i) { int ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10, &wbs[i], &metas[i], &fence_fd); ASSERT_EQ(ret, 0); ASSERT_LT(fence_fd, 0); // expect invalid fence. ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i])); int buffer_id = api_.WriteBufferGetId(wbs[i]); ASSERT_GT(buffer_id, 0); AHardwareBuffer* hb = nullptr; ASSERT_EQ(0, api_.WriteBufferGetAHardwareBuffer(wbs[i], &hb)); auto whb_it = whbs.find(buffer_id); if (whb_it == whbs.end()) { // If this is a new buffer id, check that total number of unique // hardware buffers won't exceed queue capacity. ASSERT_LT(whbs.size(), kQueueCapacity); whbs.emplace(buffer_id, hb); } else { // If this is a buffer id we have seen before, check that the // buffer_id maps to the same AHardwareBuffer handle. ASSERT_EQ(hb, whb_it->second); } }; std::function Post = [&](size_t i) { ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i])); metas[i].timestamp++; int ret = api_.WriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i], /*fence=*/-1); ASSERT_EQ(ret, 0); }; std::function Acquire = [&](size_t i) { int ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rbs[i], &metas[i], &fence_fd); ASSERT_EQ(ret, 0); ASSERT_LT(fence_fd, 0); // expect invalid fence. ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i])); int buffer_id = api_.ReadBufferGetId(rbs[i]); ASSERT_GT(buffer_id, 0); AHardwareBuffer* hb = nullptr; ASSERT_EQ(0, api_.ReadBufferGetAHardwareBuffer(rbs[i], &hb)); auto rhb_it = rhbs.find(buffer_id); if (rhb_it == rhbs.end()) { // If this is a new buffer id, check that total number of unique hardware // buffers won't exceed queue capacity. ASSERT_LT(rhbs.size(), kQueueCapacity); rhbs.emplace(buffer_id, hb); } else { // If this is a buffer id we have seen before, check that the buffer_id // maps to the same AHardwareBuffer handle. ASSERT_EQ(hb, rhb_it->second); } }; std::function Release = [&](size_t i) { ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i])); int ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i], /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); }; // Scenario one: for (int i = 0; i < kNumTests; i++) { // Gain all write buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Gain(i)); } // Post all write buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Post(i)); } // Acquire all read buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Acquire(i)); } // Release all read buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Release(i)); } } // Scenario two: for (int i = 0; i < kNumTests; i++) { // Gain and post all write buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Gain(i)); ASSERT_NO_FATAL_FAILURE(Post(i)); } // Acquire and release all read buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Acquire(i)); ASSERT_NO_FATAL_FAILURE(Release(i)); } } // Scenario three: for (int i = 0; i < kNumTests; i++) { // Gain all write buffers then post them in reversed order. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Gain(i)); } for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i)); } // Acquire all write buffers then release them in reversed order. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Acquire(i)); } for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i)); } } } TEST_F(DvrBufferQueueTest, ConsumerReleaseAfterProducerDestroy) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrReadBufferQueue* read_queue = nullptr; DvrReadBuffer* rb = nullptr; DvrWriteBuffer* wb = nullptr; DvrNativeBufferMetadata meta1; DvrNativeBufferMetadata meta2; int fence_fd = -1; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(ret, 0); api_.ReadBufferQueueSetBufferAvailableCallback( read_queue, &BufferAvailableCallback, this); // Gain buffer for writing. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1, &fence_fd); ASSERT_EQ(ret, 0); close(fence_fd); // Post buffer to the read_queue. ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); ASSERT_EQ(ret, 0); wb = nullptr; // Acquire buffer for reading. ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, &meta2, &fence_fd); ASSERT_EQ(ret, 0); close(fence_fd); // Destroy the write buffer queue and make sure the reader queue is picking // these events up. api_.WriteBufferQueueDestroy(write_queue_); ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); // Release buffer to the write_queue. ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); rb = nullptr; api_.ReadBufferQueueDestroy(read_queue); } } // namespace