blob: c72f94035bd3ffabad368bd4e40cbfcbb7f5aebf [file] [log] [blame]
#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);
}