blob: e3c2e958818a636100eecc4498ee0f0b71925f42 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/graphics/display/drivers/goldfish-display/display.h"
#include <fidl/fuchsia.sysmem/cpp/wire_test_base.h>
#include <fuchsia/hardware/display/controller/c/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/loop.h>
#include <lib/ddk/device.h>
#include <array>
#include <cstdio>
#include <memory>
#include <fbl/alloc_checker.h>
#include <fbl/array.h>
#include <fbl/vector.h>
#include <gtest/gtest.h>
#include "src/graphics/display/lib/api-types-cpp/driver-buffer-collection-id.h"
#include "src/lib/testing/predicates/status.h"
namespace goldfish {
namespace {
constexpr int32_t kDisplayWidthPx = 1024;
constexpr int32_t kDisplayHeightPx = 768;
constexpr int32_t kDisplayRefreshRateHz = 60;
constexpr size_t kDisplayCount = 1;
constexpr size_t kMaxLayerCount = 3; // This is the max size of layer array.
} // namespace
// TODO(https://fxbug.dev/42072949): Consider creating and using a unified set of sysmem
// testing doubles instead of writing mocks for each display driver test.
class FakeAllocator : public fidl::testing::WireTestBase<fuchsia_sysmem::Allocator> {
public:
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {}
};
class FakePipe : public fidl::WireServer<fuchsia_hardware_goldfish_pipe::GoldfishPipe> {};
class GoldfishDisplayTest : public testing::Test {
public:
GoldfishDisplayTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override;
void TearDown() override;
std::array<std::array<layer_t, kMaxLayerCount>, kDisplayCount> layer_ = {};
std::array<const layer_t*, kDisplayCount> layer_ptrs = {};
std::array<display_config_t, kDisplayCount> configs_ = {};
std::array<display_config_t*, kDisplayCount> configs_ptrs_ = {};
std::array<client_composition_opcode_t, kMaxLayerCount * kDisplayCount> results_ = {};
std::unique_ptr<Display> display_ = {};
std::optional<fidl::ServerBindingRef<fuchsia_hardware_goldfish_pipe::GoldfishPipe>> binding_;
std::optional<fidl::ServerBindingRef<fuchsia_sysmem::Allocator>> allocator_binding_;
async::Loop loop_;
FakePipe* fake_pipe_;
FakeAllocator mock_allocator_;
};
void GoldfishDisplayTest::SetUp() {
display_ = std::make_unique<Display>(nullptr);
for (size_t i = 0; i < kDisplayCount; i++) {
configs_ptrs_[i] = &configs_[i];
layer_ptrs[i] = layer_[i].data();
configs_[i].display_id = i + 1;
configs_[i].layer_list = &layer_ptrs[i];
configs_[i].layer_count = 1;
}
// Call SetupPrimaryDisplayForTesting() so that we can set up the display
// devices without any dependency on proper driver binding.
display_->SetupPrimaryDisplayForTesting(kDisplayWidthPx, kDisplayHeightPx, kDisplayRefreshRateHz);
auto endpoints = fidl::Endpoints<fuchsia_sysmem::Allocator>::Create();
allocator_binding_ =
fidl::BindServer(loop_.dispatcher(), std::move(endpoints.server), &mock_allocator_);
display_->SetSysmemAllocatorForTesting(fidl::WireSyncClient(std::move(endpoints.client)));
}
void GoldfishDisplayTest::TearDown() { allocator_binding_->Unbind(); }
TEST_F(GoldfishDisplayTest, CheckConfigNoDisplay) {
// Test No display
size_t client_composition_opcodes_actual = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), 0, results_.data(),
results_.size(), &client_composition_opcodes_actual);
EXPECT_OK(res);
}
TEST_F(GoldfishDisplayTest, CheckConfigMultiLayer) {
// ensure we fail correctly if layers more than 1
for (size_t i = 0; i < kDisplayCount; i++) {
configs_[i].layer_count = kMaxLayerCount;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kMaxLayerCount);
int result_cfg_offset = 0;
for (size_t j = 0; j < kDisplayCount; j++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_MERGE_BASE,
results_[result_cfg_offset] & CLIENT_COMPOSITION_OPCODE_MERGE_BASE);
for (unsigned i = 1; i < kMaxLayerCount; i++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_MERGE_SRC, results_[result_cfg_offset + i]);
}
result_cfg_offset += kMaxLayerCount;
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerColor) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].type = LAYER_TYPE_COLOR;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_USE_PRIMARY,
results_[i] & CLIENT_COMPOSITION_OPCODE_USE_PRIMARY);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerPrimary) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
layer_[i][0].cfg.primary.alpha_mode = 0;
layer_[i][0].cfg.primary.transform_mode = 0;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
EXPECT_EQ(0u, results_[i]);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerDestFrame) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 768,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_FRAME_SCALE, results_[i]);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerSrcFrame) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 768,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_SRC_FRAME, results_[i]);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerAlpha) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
layer_[i][0].cfg.primary.alpha_mode = ALPHA_HW_MULTIPLY;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_ALPHA, results_[i]);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerTransform) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
layer_[i][0].cfg.primary.transform_mode = FRAME_TRANSFORM_REFLECT_X;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_TRANSFORM, results_[i]);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigLayerColorCoversion) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 1024,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
configs_[i].cc_flags = COLOR_CONVERSION_POSTOFFSET;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
// TODO(payamm): For now, driver will pretend it supports color conversion.
// It should return CLIENT_COMPOSITION_OPCODE_COLOR_CONVERSION instead.
EXPECT_EQ(0u, results_[i]);
}
}
TEST_F(GoldfishDisplayTest, CheckConfigAllFeatures) {
constexpr int kNumLayersPerDisplay = 1;
// First create layer for each device
frame_t dest_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 768,
.height = 768,
};
frame_t src_frame = {
.x_pos = 0,
.y_pos = 0,
.width = 768,
.height = 768,
};
for (size_t i = 0; i < kDisplayCount; i++) {
layer_[i][0].cfg.primary.dest_frame = dest_frame;
layer_[i][0].cfg.primary.src_frame = src_frame;
layer_[i][0].cfg.primary.image.width = 1024;
layer_[i][0].cfg.primary.image.height = 768;
layer_[i][0].cfg.primary.alpha_mode = ALPHA_HW_MULTIPLY;
layer_[i][0].cfg.primary.transform_mode = FRAME_TRANSFORM_ROT_180;
configs_[i].cc_flags = COLOR_CONVERSION_POSTOFFSET;
}
size_t actual_result_size = 0;
config_check_result_t res = display_->DisplayControllerImplCheckConfiguration(
const_cast<const display_config_t**>(configs_ptrs_.data()), kDisplayCount, results_.data(),
results_.size(), &actual_result_size);
EXPECT_OK(res);
EXPECT_EQ(actual_result_size, kDisplayCount * kNumLayersPerDisplay);
for (size_t i = 0; i < kDisplayCount; i++) {
// TODO(https://fxbug.dev/42080897): Driver will pretend it supports color conversion
// for now. Instead this should contain
// CLIENT_COMPOSITION_OPCODE_COLOR_CONVERSION bit.
EXPECT_EQ(CLIENT_COMPOSITION_OPCODE_FRAME_SCALE | CLIENT_COMPOSITION_OPCODE_SRC_FRAME |
CLIENT_COMPOSITION_OPCODE_ALPHA | CLIENT_COMPOSITION_OPCODE_TRANSFORM,
results_[i]);
}
}
TEST_F(GoldfishDisplayTest, ImportBufferCollection) {
zx::result token1_endpoints = fidl::CreateEndpoints<fuchsia_sysmem::BufferCollectionToken>();
ASSERT_TRUE(token1_endpoints.is_ok());
zx::result token2_endpoints = fidl::CreateEndpoints<fuchsia_sysmem::BufferCollectionToken>();
ASSERT_TRUE(token2_endpoints.is_ok());
// Test ImportBufferCollection().
constexpr display::DriverBufferCollectionId kValidCollectionId(1);
constexpr uint64_t kBanjoValidCollectionId =
display::ToBanjoDriverBufferCollectionId(kValidCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(
kBanjoValidCollectionId, token1_endpoints->client.TakeChannel()));
// `collection_id` must be unused.
EXPECT_EQ(display_->DisplayControllerImplImportBufferCollection(
kBanjoValidCollectionId, token2_endpoints->client.TakeChannel()),
ZX_ERR_ALREADY_EXISTS);
// Test ReleaseBufferCollection().
constexpr display::DriverBufferCollectionId kInvalidCollectionId(2);
constexpr uint64_t kBanjoInvalidCollectionId =
display::ToBanjoDriverBufferCollectionId(kInvalidCollectionId);
EXPECT_EQ(display_->DisplayControllerImplReleaseBufferCollection(kBanjoInvalidCollectionId),
ZX_ERR_NOT_FOUND);
EXPECT_OK(display_->DisplayControllerImplReleaseBufferCollection(kBanjoValidCollectionId));
loop_.Shutdown();
}
// TODO(https://fxbug.dev/42073664): Implement a fake sysmem and a fake goldfish-pipe
// driver to test importing images using ImportImage().
} // namespace goldfish