blob: 9dcfbcfd58c00bac4f6d91a92643482887a92c11 [file] [log] [blame] [edit]
// 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/amlogic-display/amlogic-display.h"
#include <fidl/fuchsia.images2/cpp/wire.h>
#include <fidl/fuchsia.sysmem/cpp/wire_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/inspect/cpp/inspect.h>
#include <zircon/syscalls/object.h>
#include <gtest/gtest.h>
#include "src/graphics/display/drivers/amlogic-display/osd.h"
#include "src/graphics/display/lib/api-types-cpp/driver-buffer-collection-id.h"
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/testing/predicates/status.h"
namespace amlogic_display {
namespace {
namespace sysmem = fuchsia_sysmem;
// TODO(fxbug.dev/121924): Consider creating and using a unified set of sysmem
// testing doubles instead of writing mocks for each display driver test.
class MockBufferCollectionBase
: public fidl::testing::WireTestBase<fuchsia_sysmem::BufferCollection> {
public:
MockBufferCollectionBase() = default;
~MockBufferCollectionBase() override = default;
virtual void VerifyBufferCollectionConstraints(
const sysmem::wire::BufferCollectionConstraints& constraints) = 0;
virtual void VerifyName(const std::string& name) = 0;
void SetConstraints(SetConstraintsRequestView request,
SetConstraintsCompleter::Sync& completer) override {
if (!request->has_constraints) {
return;
}
VerifyBufferCollectionConstraints(request->constraints);
set_constraints_called_ = true;
}
void SetName(SetNameRequestView request, SetNameCompleter::Sync& completer) override {
EXPECT_EQ(10u, request->priority);
VerifyName(std::string(request->name.data(), request->name.size()));
set_name_called_ = true;
}
void CheckBuffersAllocated(CheckBuffersAllocatedCompleter::Sync& completer) override {
completer.Reply(ZX_OK);
}
void WaitForBuffersAllocated(WaitForBuffersAllocatedCompleter::Sync& completer) override {
sysmem::wire::BufferCollectionInfo2 collection;
collection.buffer_count = 1;
collection.settings.has_image_format_constraints = true;
collection.settings.image_format_constraints = image_format_constraints_;
EXPECT_EQ(ZX_OK, zx::vmo::create(ZX_PAGE_SIZE, 0u, &collection.buffers[0].vmo));
completer.Reply(ZX_OK, std::move(collection));
}
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
EXPECT_TRUE(false);
}
void set_allocated_image_format_constraints(
const sysmem::wire::ImageFormatConstraints& image_format_constraints) {
image_format_constraints_ = image_format_constraints;
}
bool set_constraints_called() const { return set_constraints_called_; }
bool set_name_called() const { return set_name_called_; }
private:
bool set_constraints_called_ = false;
bool set_name_called_ = false;
sysmem::wire::ImageFormatConstraints image_format_constraints_ = {};
};
class MockBufferCollection : public MockBufferCollectionBase {
public:
explicit MockBufferCollection(const std::vector<sysmem::wire::PixelFormatType>&
pixel_format_types = {sysmem::wire::PixelFormatType::kBgra32,
sysmem::wire::PixelFormatType::kR8G8B8A8})
: supported_pixel_format_types_(pixel_format_types) {
set_allocated_image_format_constraints(kDefaultImageFormatConstraints);
}
~MockBufferCollection() override = default;
void VerifyBufferCollectionConstraints(
const sysmem::wire::BufferCollectionConstraints& constraints) override {
EXPECT_TRUE(constraints.buffer_memory_constraints.inaccessible_domain_supported);
EXPECT_FALSE(constraints.buffer_memory_constraints.cpu_domain_supported);
EXPECT_EQ(64u, constraints.image_format_constraints[0].bytes_per_row_divisor);
size_t expected_format_constraints_count = 0u;
const cpp20::span<const sysmem::wire::ImageFormatConstraints> image_format_constraints(
constraints.image_format_constraints.data(), constraints.image_format_constraints_count);
const bool has_bgra =
std::find(supported_pixel_format_types_.begin(), supported_pixel_format_types_.end(),
sysmem::wire::PixelFormatType::kBgra32) != supported_pixel_format_types_.end();
if (has_bgra) {
expected_format_constraints_count += 2;
const bool image_constraints_contains_bgra32_and_linear = std::any_of(
image_format_constraints.begin(), image_format_constraints.end(),
[](const sysmem::wire::ImageFormatConstraints& format) {
return format.pixel_format.type == sysmem::wire::PixelFormatType::kBgra32 &&
format.pixel_format.format_modifier.value == sysmem::wire::kFormatModifierLinear;
});
EXPECT_TRUE(image_constraints_contains_bgra32_and_linear);
}
const bool has_rgba =
std::find(supported_pixel_format_types_.begin(), supported_pixel_format_types_.end(),
sysmem::wire::PixelFormatType::kR8G8B8A8) != supported_pixel_format_types_.end();
if (has_rgba) {
expected_format_constraints_count += 4;
const bool image_constraints_contains_rgba32_and_linear = std::any_of(
image_format_constraints.begin(), image_format_constraints.end(),
[](const sysmem::wire::ImageFormatConstraints& format) {
return format.pixel_format.type == sysmem::wire::PixelFormatType::kR8G8B8A8 &&
format.pixel_format.format_modifier.value == sysmem::wire::kFormatModifierLinear;
});
EXPECT_TRUE(image_constraints_contains_rgba32_and_linear);
const bool image_constraints_contains_rgba32_and_afbc_16x16 = std::any_of(
image_format_constraints.begin(), image_format_constraints.end(),
[](const sysmem::wire::ImageFormatConstraints& format) {
return format.pixel_format.type == sysmem::wire::PixelFormatType::kR8G8B8A8 &&
format.pixel_format.format_modifier.value ==
sysmem::wire::kFormatModifierArmAfbc16X16SplitBlockSparseYuv;
});
EXPECT_TRUE(image_constraints_contains_rgba32_and_afbc_16x16);
}
EXPECT_EQ(expected_format_constraints_count, constraints.image_format_constraints_count);
}
void VerifyName(const std::string& name) override { EXPECT_EQ(name, "Display"); }
private:
std::vector<sysmem::wire::PixelFormatType> supported_pixel_format_types_;
static constexpr sysmem::wire::ImageFormatConstraints kDefaultImageFormatConstraints = {
.pixel_format =
fuchsia_sysmem::wire::PixelFormat{
.type = sysmem::wire::PixelFormatType::kBgra32,
.has_format_modifier = true,
.format_modifier = {sysmem::wire::kFormatModifierLinear},
},
.min_coded_height = 4,
.min_bytes_per_row = 4096,
};
};
class MockBufferCollectionForCapture : public MockBufferCollectionBase {
public:
MockBufferCollectionForCapture() {
set_allocated_image_format_constraints(kDefaultImageFormatConstraints);
}
~MockBufferCollectionForCapture() override = default;
void VerifyBufferCollectionConstraints(
const sysmem::wire::BufferCollectionConstraints& constraints) override {
EXPECT_TRUE(constraints.buffer_memory_constraints.inaccessible_domain_supported);
EXPECT_FALSE(constraints.buffer_memory_constraints.cpu_domain_supported);
EXPECT_EQ(64u, constraints.image_format_constraints[0].bytes_per_row_divisor);
size_t expected_format_constraints_count = 1u;
EXPECT_EQ(expected_format_constraints_count, constraints.image_format_constraints_count);
const auto& image_format_constraints = constraints.image_format_constraints;
EXPECT_EQ(image_format_constraints[0].pixel_format.type,
fuchsia_sysmem::wire::PixelFormatType::kBgr24);
EXPECT_TRUE(image_format_constraints[0].pixel_format.has_format_modifier);
EXPECT_EQ(image_format_constraints[0].pixel_format.format_modifier.value,
fuchsia_sysmem::wire::kFormatModifierLinear);
}
void VerifyName(const std::string& name) override { EXPECT_EQ(name, "Display capture"); }
private:
static constexpr sysmem::wire::ImageFormatConstraints kDefaultImageFormatConstraints = {
.pixel_format =
fuchsia_sysmem::wire::PixelFormat{
.type = sysmem::wire::PixelFormatType::kBgr24,
.has_format_modifier = true,
.format_modifier = {sysmem::wire::kFormatModifierLinear},
},
.min_coded_height = 4,
.min_bytes_per_row = 4096,
};
};
class MockAllocator : public fidl::testing::WireTestBase<fuchsia_sysmem::Allocator> {
public:
using MockBufferCollectionBuilder =
fit::function<std::unique_ptr<MockBufferCollectionBase>(void)>;
explicit MockAllocator(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {
ZX_ASSERT(dispatcher_ != nullptr);
}
void set_mock_buffer_collection_builder(MockBufferCollectionBuilder builder) {
mock_buffer_collection_builder_ = std::move(builder);
}
void BindSharedCollection(BindSharedCollectionRequestView request,
BindSharedCollectionCompleter::Sync& completer) override {
ZX_ASSERT(mock_buffer_collection_builder_ != nullptr);
auto buffer_collection_id = next_buffer_collection_id_++;
active_buffer_collections_[buffer_collection_id] = {
.token_client = std::move(request->token),
.mock_buffer_collection = mock_buffer_collection_builder_(),
};
fidl::BindServer(
dispatcher_, std::move(request->buffer_collection_request),
active_buffer_collections_[buffer_collection_id].mock_buffer_collection.get(),
[this, buffer_collection_id](MockBufferCollectionBase*, fidl::UnbindInfo,
fidl::ServerEnd<fuchsia_sysmem::BufferCollection>) {
inactive_buffer_collection_tokens_.push_back(
std::move(active_buffer_collections_[buffer_collection_id].token_client));
active_buffer_collections_.erase(buffer_collection_id);
});
}
MockBufferCollectionBase* GetMostRecentBufferCollection() {
const display::DriverBufferCollectionId most_recent_collection_id(
next_buffer_collection_id_.value() - 1);
if (active_buffer_collections_.find(most_recent_collection_id) ==
active_buffer_collections_.end()) {
return nullptr;
}
return active_buffer_collections_.at(most_recent_collection_id).mock_buffer_collection.get();
}
std::vector<fidl::UnownedClientEnd<fuchsia_sysmem::BufferCollectionToken>>
GetActiveBufferCollectionTokenClients() const {
std::vector<fidl::UnownedClientEnd<fuchsia_sysmem::BufferCollectionToken>>
unowned_token_clients;
unowned_token_clients.reserve(active_buffer_collections_.size());
for (const auto& kv : active_buffer_collections_) {
unowned_token_clients.push_back(kv.second.token_client);
}
return unowned_token_clients;
}
std::vector<fidl::UnownedClientEnd<fuchsia_sysmem::BufferCollectionToken>>
GetInactiveBufferCollectionTokenClients() const {
std::vector<fidl::UnownedClientEnd<fuchsia_sysmem::BufferCollectionToken>>
unowned_token_clients;
unowned_token_clients.reserve(inactive_buffer_collection_tokens_.size());
for (const auto& token : inactive_buffer_collection_tokens_) {
unowned_token_clients.push_back(token);
}
return unowned_token_clients;
}
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
EXPECT_TRUE(false);
}
private:
struct BufferCollection {
fidl::ClientEnd<fuchsia_sysmem::BufferCollectionToken> token_client;
std::unique_ptr<MockBufferCollectionBase> mock_buffer_collection;
};
std::unordered_map<display::DriverBufferCollectionId, BufferCollection>
active_buffer_collections_;
std::vector<fidl::ClientEnd<fuchsia_sysmem::BufferCollectionToken>>
inactive_buffer_collection_tokens_;
display::DriverBufferCollectionId next_buffer_collection_id_ =
display::DriverBufferCollectionId(0);
MockBufferCollectionBuilder mock_buffer_collection_builder_ = nullptr;
async_dispatcher_t* dispatcher_ = nullptr;
};
class FakeCanvasProtocol : public fidl::WireServer<fuchsia_hardware_amlogiccanvas::Device> {
public:
explicit FakeCanvasProtocol(async_dispatcher_t* dispatcher = nullptr)
: dispatcher_(dispatcher ? dispatcher : async_get_default_dispatcher()) {}
void Serve(fidl::ServerEnd<fuchsia_hardware_amlogiccanvas::Device> server_end) {
binding_.emplace(dispatcher_, std::move(server_end), this,
std::mem_fn(&FakeCanvasProtocol::OnFidlClosed));
}
void OnFidlClosed(fidl::UnbindInfo info) {}
void Config(ConfigRequestView request, ConfigCompleter::Sync& completer) override {
for (size_t i = 0; i < std::size(in_use_); i++) {
ZX_DEBUG_ASSERT_MSG(i <= std::numeric_limits<uint8_t>::max(), "%zu", i);
if (!in_use_[i]) {
in_use_[i] = true;
completer.ReplySuccess(static_cast<uint8_t>(i));
return;
}
}
completer.ReplyError(ZX_ERR_NO_MEMORY);
}
void Free(FreeRequestView request, FreeCompleter::Sync& completer) override {
EXPECT_TRUE(in_use_[request->canvas_idx]);
in_use_[request->canvas_idx] = false;
completer.ReplySuccess();
}
void CheckThatNoEntriesInUse() {
for (uint32_t i = 0; i < std::size(in_use_); i++) {
EXPECT_FALSE(in_use_[i]);
}
}
private:
async_dispatcher_t* dispatcher_;
static constexpr uint32_t kCanvasEntries = 256;
bool in_use_[kCanvasEntries] = {};
std::optional<fidl::ServerBinding<fuchsia_hardware_amlogiccanvas::Device>> binding_;
};
class FakeSysmemTest : public testing::Test {
public:
FakeSysmemTest() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {}
void SetUp() override {
loop_.StartThread("sysmem-handler-loop");
zx::result<fidl::Endpoints<fuchsia_hardware_amlogiccanvas::Device>> endpoints =
fidl::CreateEndpoints<fuchsia_hardware_amlogiccanvas::Device>();
ASSERT_OK(endpoints.status_value());
canvas_.SyncCall(&FakeCanvasProtocol::Serve, std::move(endpoints.value().server));
display_ = std::make_unique<AmlogicDisplay>(/*parent=*/nullptr);
display_->SetFormatSupportCheck([](auto) { return true; });
display_->SetCanvasForTesting(std::move(endpoints.value().client));
auto vout = std::make_unique<Vout>();
zx::result<> init_dsi_result =
vout->InitDsiForTesting(/*panel_type=*/PANEL_TV070WSM_FT, /*width=*/1024, /*height=*/600);
ASSERT_OK(init_dsi_result.status_value());
display_->SetVoutForTesting(std::move(vout));
allocator_ = std::make_unique<MockAllocator>(loop_.dispatcher());
allocator_->set_mock_buffer_collection_builder([] {
// Allocate importable primary Image by default.
const std::vector<sysmem::wire::PixelFormatType> kPixelFormatTypes = {
sysmem::wire::PixelFormatType::kBgra32, sysmem::wire::PixelFormatType::kR8G8B8A8};
return std::make_unique<MockBufferCollection>(kPixelFormatTypes);
});
{
zx::result<fidl::Endpoints<sysmem::Allocator>> endpoints =
fidl::CreateEndpoints<sysmem::Allocator>();
ASSERT_OK(endpoints.status_value());
fidl::BindServer(loop_.dispatcher(), std::move(endpoints->server), allocator_.get());
display_->SetSysmemAllocatorForTesting(fidl::WireSyncClient(std::move(endpoints->client)));
}
}
void TearDown() override {
// Shutdown the loop before destroying the MockAllocator which may still have
// pending callbacks.
loop_.Shutdown();
loop_.JoinThreads();
}
protected:
async::Loop loop_;
std::unique_ptr<AmlogicDisplay> display_;
std::unique_ptr<MockAllocator> allocator_;
async_patterns::TestDispatcherBound<FakeCanvasProtocol> canvas_{loop_.dispatcher(),
std::in_place};
};
template <typename Lambda>
bool PollUntil(Lambda predicate, zx::duration poll_interval, int max_intervals) {
ZX_ASSERT(max_intervals >= 0);
for (int sleeps_left = max_intervals; sleeps_left > 0; --sleeps_left) {
if (predicate())
return true;
zx::nanosleep(zx::deadline_after(poll_interval));
}
return predicate();
}
TEST_F(FakeSysmemTest, ImportBufferCollection) {
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token1_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token1_endpoints.status_value());
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token2_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token2_endpoints.status_value());
// Test ImportBufferCollection().
constexpr display::DriverBufferCollectionId kValidBufferCollectionId(1);
constexpr uint64_t kBanjoValidBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kValidBufferCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(
kBanjoValidBufferCollectionId, token1_endpoints->client.TakeChannel()));
// `driver_buffer_collection_id` must be unused.
EXPECT_EQ(display_->DisplayControllerImplImportBufferCollection(
kBanjoValidBufferCollectionId, token2_endpoints->client.TakeChannel()),
ZX_ERR_ALREADY_EXISTS);
EXPECT_TRUE(
PollUntil([&]() { return !allocator_->GetActiveBufferCollectionTokenClients().empty(); },
zx::msec(5), 1000));
// Verify that the current buffer collection token is used (active).
{
auto active_buffer_token_clients = allocator_->GetActiveBufferCollectionTokenClients();
EXPECT_EQ(active_buffer_token_clients.size(), 1u);
auto inactive_buffer_token_clients = allocator_->GetInactiveBufferCollectionTokenClients();
EXPECT_EQ(inactive_buffer_token_clients.size(), 0u);
auto [client_koid, client_related_koid] =
fsl::GetKoids(active_buffer_token_clients[0].channel()->get());
auto [server_koid, server_related_koid] =
fsl::GetKoids(token1_endpoints->server.channel().get());
EXPECT_NE(client_koid, ZX_KOID_INVALID);
EXPECT_NE(client_related_koid, ZX_KOID_INVALID);
EXPECT_NE(server_koid, ZX_KOID_INVALID);
EXPECT_NE(server_related_koid, ZX_KOID_INVALID);
EXPECT_EQ(client_koid, server_related_koid);
EXPECT_EQ(server_koid, client_related_koid);
}
// Test ReleaseBufferCollection().
constexpr display::DriverBufferCollectionId kInvalidBufferCollectionId(2);
constexpr uint64_t kBanjoInvalidBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kInvalidBufferCollectionId);
EXPECT_EQ(display_->DisplayControllerImplReleaseBufferCollection(kBanjoInvalidBufferCollectionId),
ZX_ERR_NOT_FOUND);
EXPECT_OK(display_->DisplayControllerImplReleaseBufferCollection(kBanjoValidBufferCollectionId));
EXPECT_TRUE(
PollUntil([&]() { return allocator_->GetActiveBufferCollectionTokenClients().empty(); },
zx::msec(5), 1000));
// Verify that the current buffer collection token is released (inactive).
{
auto active_buffer_token_clients = allocator_->GetActiveBufferCollectionTokenClients();
EXPECT_EQ(active_buffer_token_clients.size(), 0u);
auto inactive_buffer_token_clients = allocator_->GetInactiveBufferCollectionTokenClients();
EXPECT_EQ(inactive_buffer_token_clients.size(), 1u);
auto [client_koid, client_related_koid] =
fsl::GetKoids(inactive_buffer_token_clients[0].channel()->get());
auto [server_koid, server_related_koid] =
fsl::GetKoids(token1_endpoints->server.channel().get());
EXPECT_NE(client_koid, ZX_KOID_INVALID);
EXPECT_NE(client_related_koid, ZX_KOID_INVALID);
EXPECT_NE(server_koid, ZX_KOID_INVALID);
EXPECT_NE(server_related_koid, ZX_KOID_INVALID);
EXPECT_EQ(client_koid, server_related_koid);
EXPECT_EQ(server_koid, client_related_koid);
}
}
TEST_F(FakeSysmemTest, ImportImage) {
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token_endpoints.status_value());
auto& [token_client, token_server] = token_endpoints.value();
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
constexpr uint64_t kBanjoBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kBufferCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(kBanjoBufferCollectionId,
token_client.TakeChannel()));
// Driver sets BufferCollection buffer memory constraints.
const image_t kDefaultConfig = {
.width = 1024,
.height = 768,
.type = IMAGE_TYPE_SIMPLE,
.handle = 0,
};
EXPECT_OK(display_->DisplayControllerImplSetBufferCollectionConstraints(
&kDefaultConfig, kBanjoBufferCollectionId));
constexpr display::DriverBufferCollectionId kInvalidBufferCollectionId(100);
constexpr uint64_t kBanjoInvalidBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kInvalidBufferCollectionId);
EXPECT_EQ(display_->DisplayControllerImplSetBufferCollectionConstraints(
&kDefaultConfig, kBanjoInvalidBufferCollectionId),
ZX_ERR_NOT_FOUND);
// Invalid import: Bad image type.
image_t invalid_config = kDefaultConfig;
invalid_config.type = IMAGE_TYPE_CAPTURE;
EXPECT_EQ(display_->DisplayControllerImplImportImage(&invalid_config, kBanjoBufferCollectionId,
/*index=*/0),
ZX_ERR_INVALID_ARGS);
// Invalid import: Invalid collection ID.
invalid_config = kDefaultConfig;
EXPECT_EQ(
display_->DisplayControllerImplImportImage(&invalid_config, kBanjoInvalidBufferCollectionId,
/*index=*/0),
ZX_ERR_NOT_FOUND);
// Invalid import: Invalid buffer collection index.
invalid_config = kDefaultConfig;
constexpr uint64_t kInvalidBufferCollectionIndex = 100u;
EXPECT_EQ(display_->DisplayControllerImplImportImage(&invalid_config, kBanjoBufferCollectionId,
kInvalidBufferCollectionIndex),
ZX_ERR_OUT_OF_RANGE);
// Valid import.
image_t valid_config = kDefaultConfig;
EXPECT_EQ(valid_config.handle, 0u);
EXPECT_OK(display_->DisplayControllerImplImportImage(&valid_config, kBanjoBufferCollectionId,
/*index=*/0));
EXPECT_NE(valid_config.handle, 0u);
// Release the image.
display_->DisplayControllerImplReleaseImage(&valid_config);
EXPECT_OK(display_->DisplayControllerImplReleaseBufferCollection(kBanjoBufferCollectionId));
}
TEST_F(FakeSysmemTest, ImportImageForCapture) {
allocator_->set_mock_buffer_collection_builder(
[] { return std::make_unique<MockBufferCollectionForCapture>(); });
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token_endpoints.status_value());
auto& [token_client, token_server] = token_endpoints.value();
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
constexpr uint64_t kBanjoBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kBufferCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(kBanjoBufferCollectionId,
token_client.TakeChannel()));
// Driver sets BufferCollection buffer memory constraints.
const image_t kDefaultConfig = {
.width = 1024,
.height = 768,
.type = IMAGE_TYPE_CAPTURE,
.handle = 0,
};
EXPECT_OK(display_->DisplayControllerImplSetBufferCollectionConstraints(
&kDefaultConfig, kBanjoBufferCollectionId));
// Invalid import: invalid buffer collection ID.
uint64_t capture_handle = 0;
const uint64_t kBanjoInvalidBufferCollectionId = 100;
EXPECT_EQ(display_->DisplayControllerImplImportImageForCapture(kBanjoInvalidBufferCollectionId,
/*index=*/0, &capture_handle),
ZX_ERR_NOT_FOUND);
// Invalid import: index out of range.
const uint64_t kInvalidIndex = 100;
EXPECT_EQ(display_->DisplayControllerImplImportImageForCapture(kBanjoBufferCollectionId,
kInvalidIndex, &capture_handle),
ZX_ERR_OUT_OF_RANGE);
// Valid import.
capture_handle = 0;
EXPECT_OK(display_->DisplayControllerImplImportImageForCapture(kBanjoBufferCollectionId,
/*index=*/0, &capture_handle));
EXPECT_NE(capture_handle, 0u);
// Release the image.
display_->DisplayControllerImplReleaseCapture(capture_handle);
EXPECT_OK(display_->DisplayControllerImplReleaseBufferCollection(kBanjoBufferCollectionId));
}
TEST_F(FakeSysmemTest, SysmemRequirements) {
MockBufferCollectionBase* collection = nullptr;
allocator_->set_mock_buffer_collection_builder([&collection] {
const std::vector<sysmem::wire::PixelFormatType> kPixelFormatTypes = {
sysmem::wire::PixelFormatType::kBgra32, sysmem::wire::PixelFormatType::kR8G8B8A8};
auto new_buffer_collection = std::make_unique<MockBufferCollection>(kPixelFormatTypes);
collection = new_buffer_collection.get();
return new_buffer_collection;
});
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token_endpoints.status_value());
auto& [token_client, token_server] = token_endpoints.value();
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
constexpr uint64_t kBanjoBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kBufferCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(kBanjoBufferCollectionId,
token_client.TakeChannel()));
EXPECT_TRUE(PollUntil([&] { return collection != nullptr; }, zx::msec(5), 1000));
image_t image = {};
EXPECT_OK(display_->DisplayControllerImplSetBufferCollectionConstraints(
&image, kBanjoBufferCollectionId));
EXPECT_TRUE(PollUntil([&] { return collection->set_constraints_called(); }, zx::msec(5), 1000));
EXPECT_TRUE(collection->set_name_called());
}
TEST_F(FakeSysmemTest, SysmemRequirements_BgraOnly) {
MockBufferCollectionBase* collection = nullptr;
allocator_->set_mock_buffer_collection_builder([&collection] {
const std::vector<sysmem::wire::PixelFormatType> kPixelFormatTypes = {
sysmem::wire::PixelFormatType::kBgra32,
};
auto new_buffer_collection = std::make_unique<MockBufferCollection>(kPixelFormatTypes);
collection = new_buffer_collection.get();
return new_buffer_collection;
});
display_->SetFormatSupportCheck([](fuchsia_images2::wire::PixelFormat format) {
return format == fuchsia_images2::wire::PixelFormat::kBgra32;
});
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token_endpoints.status_value());
auto& [token_client, token_server] = token_endpoints.value();
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
constexpr uint64_t kBanjoBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kBufferCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(kBanjoBufferCollectionId,
token_client.TakeChannel()));
EXPECT_TRUE(PollUntil([&] { return collection != nullptr; }, zx::msec(5), 1000));
image_t image = {};
EXPECT_OK(display_->DisplayControllerImplSetBufferCollectionConstraints(
&image, kBanjoBufferCollectionId));
EXPECT_TRUE(PollUntil([&] { return collection->set_constraints_called(); }, zx::msec(5), 1000));
EXPECT_TRUE(collection->set_name_called());
}
TEST(AmlogicDisplay, FloatToFix3_10) {
inspect::Inspector inspector;
EXPECT_EQ(0x0000u, Osd::FloatToFixed3_10(0.0f));
EXPECT_EQ(0x0066u, Osd::FloatToFixed3_10(0.1f));
EXPECT_EQ(0x1f9au, Osd::FloatToFixed3_10(-0.1f));
// Test for maximum positive (<4)
EXPECT_EQ(0x0FFFu, Osd::FloatToFixed3_10(4.0f));
EXPECT_EQ(0x0FFFu, Osd::FloatToFixed3_10(40.0f));
EXPECT_EQ(0x0FFFu, Osd::FloatToFixed3_10(3.9999f));
// Test for minimum negative (>= -4)
EXPECT_EQ(0x1000u, Osd::FloatToFixed3_10(-4.0f));
EXPECT_EQ(0x1000u, Osd::FloatToFixed3_10(-14.0f));
}
TEST(AmlogicDisplay, FloatToFixed2_10) {
inspect::Inspector inspector;
EXPECT_EQ(0x0000u, Osd::FloatToFixed2_10(0.0f));
EXPECT_EQ(0x0066u, Osd::FloatToFixed2_10(0.1f));
EXPECT_EQ(0x0f9au, Osd::FloatToFixed2_10(-0.1f));
// Test for maximum positive (<2)
EXPECT_EQ(0x07FFu, Osd::FloatToFixed2_10(2.0f));
EXPECT_EQ(0x07FFu, Osd::FloatToFixed2_10(20.0f));
EXPECT_EQ(0x07FFu, Osd::FloatToFixed2_10(1.9999f));
// Test for minimum negative (>= -2)
EXPECT_EQ(0x0800u, Osd::FloatToFixed2_10(-2.0f));
EXPECT_EQ(0x0800u, Osd::FloatToFixed2_10(-14.0f));
}
TEST_F(FakeSysmemTest, NoLeakCaptureCanvas) {
allocator_->set_mock_buffer_collection_builder(
[] { return std::make_unique<MockBufferCollectionForCapture>(); });
zx::result<fidl::Endpoints<sysmem::BufferCollectionToken>> token_endpoints =
fidl::CreateEndpoints<sysmem::BufferCollectionToken>();
ASSERT_OK(token_endpoints.status_value());
auto& [token_client, token_server] = token_endpoints.value();
constexpr display::DriverBufferCollectionId kBufferCollectionId(1);
constexpr uint64_t kBanjoBufferCollectionId =
display::ToBanjoDriverBufferCollectionId(kBufferCollectionId);
EXPECT_OK(display_->DisplayControllerImplImportBufferCollection(kBanjoBufferCollectionId,
token_client.TakeChannel()));
uint64_t capture_handle;
EXPECT_OK(display_->DisplayControllerImplImportImageForCapture(kBanjoBufferCollectionId,
/*index=*/0, &capture_handle));
EXPECT_OK(display_->DisplayControllerImplReleaseCapture(capture_handle));
canvas_.SyncCall(&FakeCanvasProtocol::CheckThatNoEntriesInUse);
}
} // namespace
} // namespace amlogic_display