| #include <android/hardware_buffer.h> |
| #include <android/log.h> |
| #include <dvr/dvr_api.h> |
| #include <dvr/dvr_display_types.h> |
| #include <dvr/dvr_surface.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include "dvr_api_test.h" |
| |
| #define LOG_TAG "dvr_display-test" |
| |
| #ifndef ALOGD |
| #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) |
| #endif |
| |
| class DvrDisplayTest : public DvrApiTest { |
| protected: |
| void SetUp() override { |
| DvrApiTest::SetUp(); |
| int ret = api_.GetNativeDisplayMetrics(sizeof(display_metrics_), |
| &display_metrics_); |
| ASSERT_EQ(ret, 0) << "Failed to get display metrics."; |
| ALOGD( |
| "display_width: %d, display_height: %d, display_x_dpi: %d, " |
| "display_y_dpi: %d, vsync_period_ns: %d.", |
| display_metrics_.display_width, display_metrics_.display_height, |
| display_metrics_.display_x_dpi, display_metrics_.display_y_dpi, |
| display_metrics_.vsync_period_ns); |
| } |
| |
| void TearDown() override { |
| if (write_queue_ != nullptr) { |
| api_.WriteBufferQueueDestroy(write_queue_); |
| write_queue_ = nullptr; |
| } |
| if (direct_surface_ != nullptr) { |
| api_.SurfaceDestroy(direct_surface_); |
| direct_surface_ = nullptr; |
| } |
| DvrApiTest::TearDown(); |
| } |
| |
| /* Convert a write buffer to an android hardware buffer and fill in |
| * color_textures evenly to the buffer. |
| * AssertionError if the width of the buffer is not equal to the input width, |
| * AssertionError if the height of the buffer is not equal to the input |
| * height. |
| */ |
| void FillWriteBuffer(DvrWriteBuffer* write_buffer, |
| const std::vector<uint32_t>& color_textures, |
| uint32_t width, uint32_t height); |
| |
| // Write buffer queue properties. |
| static constexpr uint64_t kUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | |
| AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | |
| AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; |
| uint32_t kFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; |
| static constexpr size_t kMetadataSize = 0; |
| static constexpr int kTimeoutMs = 1000; // Time for getting buffer. |
| uint32_t kLayerCount = 1; |
| DvrWriteBufferQueue* write_queue_ = nullptr; |
| DvrSurface* direct_surface_ = nullptr; |
| |
| // Device display properties. |
| DvrNativeDisplayMetrics display_metrics_; |
| }; |
| |
| TEST_F(DvrDisplayTest, DisplayWithOneBuffer) { |
| // Create a direct surface. |
| std::vector<DvrSurfaceAttribute> direct_surface_attributes = { |
| {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, |
| .value.int32_value = 10}, |
| {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| }; |
| int ret = |
| api_.SurfaceCreate(direct_surface_attributes.data(), |
| direct_surface_attributes.size(), &direct_surface_); |
| ASSERT_EQ(ret, 0) << "Failed to create direct surface."; |
| |
| // Create a buffer queue with the direct surface. |
| constexpr size_t kCapacity = 1; |
| uint32_t width = display_metrics_.display_width; |
| uint32_t height = display_metrics_.display_height; |
| ret = api_.SurfaceCreateWriteBufferQueue( |
| direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity, |
| kMetadataSize, &write_queue_); |
| EXPECT_EQ(0, ret) << "Failed to create buffer queue."; |
| ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null."; |
| |
| // Get buffer from WriteBufferQueue. |
| DvrWriteBuffer* write_buffer = nullptr; |
| DvrNativeBufferMetadata out_meta; |
| int out_fence_fd = -1; |
| ret = api_.WriteBufferQueueGainBuffer(write_queue_, kTimeoutMs, &write_buffer, |
| &out_meta, &out_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to get the buffer."; |
| ASSERT_NE(nullptr, write_buffer) << "Gained buffer should not be null."; |
| |
| // Color the write buffer. |
| FillWriteBuffer(write_buffer, |
| {0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}, |
| width, height); |
| |
| // Post buffer. |
| int ready_fence_fd = -1; |
| ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta, |
| ready_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to post the buffer."; |
| |
| sleep(5); // For visual check on the device under test. |
| // Should observe three primary colors on the screen center. |
| } |
| |
| TEST_F(DvrDisplayTest, DisplayWithDoubleBuffering) { |
| // Create a direct surface. |
| std::vector<DvrSurfaceAttribute> direct_surface_attributes = { |
| {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, |
| .value.int32_value = 10}, |
| {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| }; |
| int ret = |
| api_.SurfaceCreate(direct_surface_attributes.data(), |
| direct_surface_attributes.size(), &direct_surface_); |
| ASSERT_EQ(ret, 0) << "Failed to create direct surface."; |
| |
| // Create a buffer queue with the direct surface. |
| constexpr size_t kCapacity = 2; |
| uint32_t width = display_metrics_.display_width; |
| uint32_t height = display_metrics_.display_height; |
| ret = api_.SurfaceCreateWriteBufferQueue( |
| direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity, |
| kMetadataSize, &write_queue_); |
| EXPECT_EQ(0, ret) << "Failed to create buffer queue."; |
| ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null."; |
| |
| int num_display_cycles_in_5s = 5 / (display_metrics_.vsync_period_ns / 1e9); |
| ALOGD("The number of display cycles: %d", num_display_cycles_in_5s); |
| int bufferhub_id_prev_write_buffer = -1; |
| for (int i = 0; i < num_display_cycles_in_5s; ++i) { |
| // Get a buffer from the WriteBufferQueue. |
| DvrWriteBuffer* write_buffer = nullptr; |
| DvrNativeBufferMetadata out_meta; |
| int out_fence_fd = -1; |
| ret = api_.WriteBufferQueueGainBuffer( |
| write_queue_, kTimeoutMs, &write_buffer, &out_meta, &out_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to get the a write buffer."; |
| ASSERT_NE(nullptr, write_buffer) << "The gained buffer should not be null."; |
| |
| int bufferhub_id = api_.WriteBufferGetId(write_buffer); |
| ALOGD("Display cycle: %d, bufferhub id of the write buffer: %d", i, |
| bufferhub_id); |
| EXPECT_NE(bufferhub_id_prev_write_buffer, bufferhub_id) |
| << "Double buffering should be using the two buffers in turns, not " |
| "reusing the same write buffer."; |
| bufferhub_id_prev_write_buffer = bufferhub_id; |
| |
| // Color the write buffer. |
| if (i % 2) { |
| FillWriteBuffer(write_buffer, {0xffff0000, 0xff00ff00, 0xff0000ff}, width, |
| height); |
| } else { |
| FillWriteBuffer(write_buffer, {0xff00ff00, 0xff0000ff, 0xffff0000}, width, |
| height); |
| } |
| |
| // Post the write buffer. |
| int ready_fence_fd = -1; |
| ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta, |
| ready_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to post the buffer."; |
| } |
| // Should observe blinking screen in secondary colors |
| // although it is actually displaying primary colors. |
| } |
| |
| TEST_F(DvrDisplayTest, DisplayWithTwoHardwareLayers) { |
| // Create the direct_surface_0 of z order 10 and direct_surface_1 of z |
| // order 11. |
| DvrSurface* direct_surface_0 = nullptr; |
| std::vector<DvrSurfaceAttribute> direct_surface_0_attributes = { |
| {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, |
| .value.int32_value = 10}, |
| {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| }; |
| int ret = |
| api_.SurfaceCreate(direct_surface_0_attributes.data(), |
| direct_surface_0_attributes.size(), &direct_surface_0); |
| EXPECT_EQ(ret, 0) << "Failed to create direct surface."; |
| |
| DvrSurface* direct_surface_1 = nullptr; |
| std::vector<DvrSurfaceAttribute> direct_surface_1_attributes = { |
| {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, |
| .value.int32_value = 11}, |
| {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, |
| .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, |
| .value.bool_value = true}, |
| }; |
| ret = |
| api_.SurfaceCreate(direct_surface_1_attributes.data(), |
| direct_surface_1_attributes.size(), &direct_surface_1); |
| EXPECT_EQ(ret, 0) << "Failed to create direct surface."; |
| |
| // Create a buffer queue for each of the direct surfaces. |
| constexpr size_t kCapacity = 1; |
| uint32_t width = display_metrics_.display_width; |
| uint32_t height = display_metrics_.display_height; |
| |
| DvrWriteBufferQueue* write_queue_0 = nullptr; |
| ret = api_.SurfaceCreateWriteBufferQueue( |
| direct_surface_0, width, height, kFormat, kLayerCount, kUsage, kCapacity, |
| kMetadataSize, &write_queue_0); |
| EXPECT_EQ(0, ret) << "Failed to create buffer queue."; |
| EXPECT_NE(nullptr, write_queue_0) << "Write buffer queue should not be null."; |
| |
| DvrWriteBufferQueue* write_queue_1 = nullptr; |
| ret = api_.SurfaceCreateWriteBufferQueue( |
| direct_surface_1, width, height, kFormat, kLayerCount, kUsage, kCapacity, |
| kMetadataSize, &write_queue_1); |
| EXPECT_EQ(0, ret) << "Failed to create buffer queue."; |
| EXPECT_NE(nullptr, write_queue_1) << "Write buffer queue should not be null."; |
| |
| // Get a buffer from each of the write buffer queues. |
| DvrWriteBuffer* write_buffer_0 = nullptr; |
| DvrNativeBufferMetadata out_meta_0; |
| int out_fence_fd = -1; |
| ret = api_.WriteBufferQueueGainBuffer( |
| write_queue_0, kTimeoutMs, &write_buffer_0, &out_meta_0, &out_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to get the buffer."; |
| EXPECT_NE(nullptr, write_buffer_0) << "Gained buffer should not be null."; |
| |
| DvrWriteBuffer* write_buffer_1 = nullptr; |
| DvrNativeBufferMetadata out_meta_1; |
| out_fence_fd = -1; |
| ret = api_.WriteBufferQueueGainBuffer( |
| write_queue_1, kTimeoutMs, &write_buffer_1, &out_meta_1, &out_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to get the buffer."; |
| EXPECT_NE(nullptr, write_buffer_1) << "Gained buffer should not be null."; |
| |
| // Color the write buffers. |
| FillWriteBuffer(write_buffer_0, {0xffff0000, 0xff00ff00, 0xff0000ff}, width, |
| height); |
| FillWriteBuffer(write_buffer_1, {0x7f00ff00, 0x7f0000ff, 0x7fff0000}, width, |
| height); |
| |
| // Post buffers. |
| int ready_fence_fd = -1; |
| ret = api_.WriteBufferQueuePostBuffer(write_queue_0, write_buffer_0, |
| &out_meta_0, ready_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to post the buffer."; |
| |
| ready_fence_fd = -1; |
| ret = api_.WriteBufferQueuePostBuffer(write_queue_1, write_buffer_1, |
| &out_meta_1, ready_fence_fd); |
| EXPECT_EQ(0, ret) << "Failed to post the buffer."; |
| |
| sleep(5); // For visual check on the device under test. |
| // Should observe three secondary colors. |
| |
| // Test finished. Clean up buffers and surfaces. |
| if (write_queue_0 != nullptr) { |
| api_.WriteBufferQueueDestroy(write_queue_0); |
| write_queue_0 = nullptr; |
| } |
| if (write_queue_1 != nullptr) { |
| api_.WriteBufferQueueDestroy(write_queue_1); |
| write_queue_1 = nullptr; |
| } |
| if (direct_surface_0 != nullptr) { |
| api_.SurfaceDestroy(direct_surface_0); |
| } |
| if (direct_surface_1 != nullptr) { |
| api_.SurfaceDestroy(direct_surface_1); |
| } |
| } |
| |
| void DvrDisplayTest::FillWriteBuffer( |
| DvrWriteBuffer* write_buffer, const std::vector<uint32_t>& color_textures, |
| uint32_t width, uint32_t height) { |
| uint32_t num_colors = color_textures.size(); |
| // Convert the first write buffer to an android hardware buffer. |
| AHardwareBuffer* ah_buffer = nullptr; |
| int ret = api_.WriteBufferGetAHardwareBuffer(write_buffer, &ah_buffer); |
| ASSERT_EQ(0, ret) << "Failed to get a hardware buffer from the write buffer."; |
| ASSERT_NE(nullptr, ah_buffer) << "AHardware buffer should not be null."; |
| AHardwareBuffer_Desc ah_buffer_describe; |
| AHardwareBuffer_describe(ah_buffer, &ah_buffer_describe); |
| ASSERT_EQ(ah_buffer_describe.format, kFormat) |
| << "The format of the android hardware buffer is wrong."; |
| ASSERT_EQ(ah_buffer_describe.layers, kLayerCount) |
| << "The obtained android hardware buffer should have 2 layers."; |
| ASSERT_EQ(ah_buffer_describe.width, width) |
| << "The obtained android hardware buffer width is wrong."; |
| ASSERT_EQ(ah_buffer_describe.height, height) |
| << "The obtained android hardware buffer height is wrong."; |
| // Change the content of the android hardware buffer. |
| void* buffer_data = nullptr; |
| int32_t fence = -1; |
| ret = AHardwareBuffer_lock(ah_buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, |
| fence, nullptr, &buffer_data); |
| ASSERT_EQ(0, ret) << "Failed to lock the hardware buffer."; |
| ASSERT_NE(nullptr, buffer_data) << "Buffer data should not be null."; |
| |
| uint32_t num_pixels = width * height / num_colors; |
| for (uint32_t color_index = 0; color_index < num_colors - 1; ++color_index) { |
| uint32_t color_texture = color_textures[color_index]; |
| for (uint32_t i = 0; i < num_pixels; ++i) { |
| memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) + |
| (i + num_pixels * color_index) * |
| sizeof(color_texture)), |
| &color_texture, sizeof(color_texture)); |
| } |
| } |
| uint32_t color_texture = color_textures[num_colors - 1]; |
| uint32_t num_colored_pixels = num_pixels * (num_colors - 1); |
| num_pixels = width * height - num_colored_pixels; |
| for (uint32_t i = 0; i < num_pixels; ++i) { |
| memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) + |
| (i + num_colored_pixels) * |
| sizeof(color_texture)), |
| &color_texture, sizeof(color_texture)); |
| } |
| fence = -1; |
| ret = AHardwareBuffer_unlock(ah_buffer, &fence); |
| EXPECT_EQ(0, ret) << "Failed to unlock the hardware buffer."; |
| |
| // Release the android hardware buffer. |
| AHardwareBuffer_release(ah_buffer); |
| } |