| // 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/display-engine.h" |
| |
| #include <fidl/fuchsia.images2/cpp/wire.h> |
| #include <fidl/fuchsia.sysmem2/cpp/wire_test_base.h> |
| #include <fuchsia/hardware/display/controller/c/banjo.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 <fake-mmio-reg/fake-mmio-reg.h> |
| #include <gtest/gtest.h> |
| |
| #include "fidl/fuchsia.images2/cpp/wire_types.h" |
| #include "fidl/fuchsia.sysmem2/cpp/wire_types.h" |
| #include "lib/fidl/cpp/wire/arena.h" |
| #include "src/devices/testing/mock-ddk/mock-device.h" |
| #include "src/graphics/display/drivers/amlogic-display/pixel-grid-size2d.h" |
| #include "src/graphics/display/drivers/amlogic-display/video-input-unit.h" |
| #include "src/graphics/display/lib/api-types-cpp/driver-buffer-collection-id.h" |
| #include "src/graphics/display/lib/driver-framework-migration-utils/dispatcher/loop-backed-dispatcher-factory.h" |
| #include "src/graphics/display/lib/driver-framework-migration-utils/metadata/metadata-getter-dfv1.h" |
| #include "src/graphics/display/lib/driver-framework-migration-utils/metadata/metadata-getter.h" |
| #include "src/graphics/display/lib/driver-framework-migration-utils/namespace/namespace-dfv1.h" |
| #include "src/lib/fsl/handles/object_info.h" |
| #include "src/lib/testing/predicates/status.h" |
| |
| namespace amlogic_display { |
| |
| 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 MockBufferCollectionBase |
| : public fidl::testing::WireTestBase<fuchsia_sysmem2::BufferCollection> { |
| public: |
| MockBufferCollectionBase() = default; |
| ~MockBufferCollectionBase() override = default; |
| |
| virtual void VerifyBufferCollectionConstraints( |
| const fuchsia_sysmem2::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 CheckAllBuffersAllocated(CheckAllBuffersAllocatedCompleter::Sync& completer) override { |
| completer.Reply(fit::ok()); |
| } |
| |
| void WaitForAllBuffersAllocated(WaitForAllBuffersAllocatedCompleter::Sync& completer) override { |
| zx::vmo vmo; |
| EXPECT_EQ(ZX_OK, zx::vmo::create(ZX_PAGE_SIZE, 0u, &vmo)); |
| auto collection = fuchsia_sysmem2::wire::BufferCollectionInfo::Builder(arena_) |
| .buffers(std::vector{fuchsia_sysmem2::wire::VmoBuffer::Builder(arena_) |
| .vmo(std::move(vmo)) |
| .vmo_usable_start(0) |
| .Build()}) |
| .settings(fuchsia_sysmem2::wire::SingleBufferSettings::Builder(arena_) |
| .image_format_constraints(image_format_constraints_) |
| .Build()) |
| .Build(); |
| auto response = |
| fuchsia_sysmem2::wire::BufferCollectionWaitForAllBuffersAllocatedResponse::Builder(arena_) |
| .buffer_collection_info(collection) |
| .Build(); |
| completer.Reply(fit::ok(&response)); |
| } |
| |
| void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override { |
| EXPECT_TRUE(false); |
| } |
| |
| void set_allocated_image_format_constraints( |
| const fuchsia_sysmem2::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: |
| fidl::Arena<fidl::kDefaultArenaInitialCapacity> arena_; |
| bool set_constraints_called_ = false; |
| bool set_name_called_ = false; |
| fuchsia_sysmem2::wire::ImageFormatConstraints image_format_constraints_; |
| }; |
| |
| class MockBufferCollection : public MockBufferCollectionBase { |
| public: |
| explicit MockBufferCollection( |
| const std::vector<fuchsia_images2::wire::PixelFormat>& pixel_format_types = |
| {fuchsia_images2::wire::PixelFormat::kB8G8R8A8, |
| fuchsia_images2::wire::PixelFormat::kR8G8B8A8}) |
| : supported_pixel_format_types_(pixel_format_types) { |
| set_allocated_image_format_constraints( |
| fuchsia_sysmem2::wire::ImageFormatConstraints::Builder(arena_) |
| .pixel_format(fuchsia_images2::wire::PixelFormat::kB8G8R8A8) |
| .pixel_format_modifier(fuchsia_images2::wire::PixelFormatModifier::kLinear) |
| .min_size(fuchsia_math::wire::SizeU{.width = 1, .height = 4}) |
| .min_bytes_per_row(4096) |
| .Build()); |
| } |
| ~MockBufferCollection() override = default; |
| |
| void VerifyBufferCollectionConstraints( |
| const fuchsia_sysmem2::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().at(0).bytes_per_row_divisor()); |
| |
| size_t expected_format_constraints_count = 0u; |
| const cpp20::span<const fuchsia_sysmem2::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(), |
| fuchsia_images2::wire::PixelFormat::kB8G8R8A8) != |
| 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 fuchsia_sysmem2::wire::ImageFormatConstraints& format) { |
| return format.pixel_format() == fuchsia_images2::wire::PixelFormat::kB8G8R8A8 && |
| format.pixel_format_modifier() == |
| fuchsia_images2::wire::PixelFormatModifier::kLinear; |
| }); |
| EXPECT_TRUE(image_constraints_contains_bgra32_and_linear); |
| } |
| |
| const bool has_rgba = |
| std::find(supported_pixel_format_types_.begin(), supported_pixel_format_types_.end(), |
| fuchsia_images2::wire::PixelFormat::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 fuchsia_sysmem2::wire::ImageFormatConstraints& format) { |
| return format.pixel_format() == fuchsia_images2::wire::PixelFormat::kR8G8B8A8 && |
| format.pixel_format_modifier() == |
| fuchsia_images2::wire::PixelFormatModifier::kLinear; |
| }); |
| 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 fuchsia_sysmem2::wire::ImageFormatConstraints& format) { |
| return format.pixel_format() == fuchsia_images2::wire::PixelFormat::kR8G8B8A8 && |
| format.pixel_format_modifier() == |
| fuchsia_images2::wire::PixelFormatModifier::kArmAfbc16X16SplitBlockSparseYuv; |
| }); |
| 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: |
| fidl::Arena<fidl::kDefaultArenaInitialCapacity> arena_; |
| std::vector<fuchsia_images2::wire::PixelFormat> supported_pixel_format_types_; |
| }; |
| |
| class MockBufferCollectionForCapture : public MockBufferCollectionBase { |
| public: |
| MockBufferCollectionForCapture() { |
| set_allocated_image_format_constraints( |
| fuchsia_sysmem2::wire::ImageFormatConstraints::Builder(arena_) |
| .pixel_format(fuchsia_images2::wire::PixelFormat::kB8G8R8) |
| .pixel_format_modifier(fuchsia_images2::wire::PixelFormatModifier::kLinear) |
| .min_size(fuchsia_math::wire::SizeU{.width = 1, .height = 4}) |
| .min_bytes_per_row(4096) |
| .Build()); |
| } |
| ~MockBufferCollectionForCapture() override = default; |
| |
| void VerifyBufferCollectionConstraints( |
| const fuchsia_sysmem2::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().at(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.at(0).pixel_format(), |
| fuchsia_images2::wire::PixelFormat::kB8G8R8); |
| EXPECT_TRUE(image_format_constraints.at(0).has_pixel_format_modifier()); |
| EXPECT_EQ(image_format_constraints.at(0).pixel_format_modifier(), |
| fuchsia_images2::wire::PixelFormatModifier::kLinear); |
| } |
| |
| void VerifyName(const std::string& name) override { EXPECT_EQ(name, "Display capture"); } |
| |
| private: |
| fidl::Arena<fidl::kDefaultArenaInitialCapacity> arena_; |
| }; |
| |
| class MockAllocator : public fidl::testing::WireTestBase<fuchsia_sysmem2::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_sysmem2::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_sysmem2::BufferCollectionToken>> |
| GetActiveBufferCollectionTokenClients() const { |
| std::vector<fidl::UnownedClientEnd<fuchsia_sysmem2::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_sysmem2::BufferCollectionToken>> |
| GetInactiveBufferCollectionTokenClients() const { |
| std::vector<fidl::UnownedClientEnd<fuchsia_sysmem2::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_sysmem2::BufferCollectionToken> token_client; |
| std::unique_ptr<MockBufferCollectionBase> mock_buffer_collection; |
| }; |
| |
| std::unordered_map<display::DriverBufferCollectionId, BufferCollection> |
| active_buffer_collections_; |
| std::vector<fidl::ClientEnd<fuchsia_sysmem2::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: |
| static constexpr int kWidth = 1024; |
| static constexpr int kHeight = 600; |
| |
| FakeSysmemTest() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {} |
| |
| void SetUp() override { |
| mock_root_ = MockDevice::FakeRootParent(); |
| |
| loop_.StartThread("sysmem-handler-loop"); |
| auto endpoints = fidl::Endpoints<fuchsia_hardware_amlogiccanvas::Device>::Create(); |
| canvas_.SyncCall(&FakeCanvasProtocol::Serve, std::move(endpoints.server)); |
| |
| zx::result<std::unique_ptr<display::Namespace>> create_incoming_result = |
| display::NamespaceDfv1::Create(mock_root_.get()); |
| ASSERT_OK(create_incoming_result.status_value()); |
| incoming_ = std::move(create_incoming_result).value(); |
| |
| zx::result<std::unique_ptr<display::MetadataGetter>> create_metadata_getter_result = |
| display::MetadataGetterDfv1::Create(mock_root_.get()); |
| ASSERT_OK(create_metadata_getter_result.status_value()); |
| metadata_getter_ = std::move(create_metadata_getter_result).value(); |
| |
| zx::result<std::unique_ptr<display::DispatcherFactory>> create_dispatcher_factory_result = |
| display::LoopBackedDispatcherFactory::Create(mock_root_.get()); |
| ASSERT_OK(create_dispatcher_factory_result.status_value()); |
| dispatcher_factory_ = std::move(create_dispatcher_factory_result).value(); |
| |
| display_engine_ = std::make_unique<DisplayEngine>(incoming_.get(), metadata_getter_.get(), |
| dispatcher_factory_.get()); |
| display_engine_->SetFormatSupportCheck([](auto) { return true; }); |
| display_engine_->SetCanvasForTesting(std::move(endpoints.client)); |
| |
| zx::result<std::unique_ptr<Vout>> create_dsi_vout_result = Vout::CreateDsiVoutForTesting( |
| /*panel_type=*/PANEL_BOE_TV070WSM_FITIPOWER_JD9364_ASTRO, /*width=*/kWidth, |
| /*height=*/kHeight); |
| ASSERT_OK(create_dsi_vout_result.status_value()); |
| display_engine_->SetVoutForTesting(std::move(create_dsi_vout_result).value()); |
| |
| PixelGridSize2D layer_image_size = { |
| .width = kWidth, |
| .height = kHeight, |
| }; |
| PixelGridSize2D display_contents_size = { |
| .width = kWidth, |
| .height = kHeight, |
| }; |
| zx::result<std::unique_ptr<VideoInputUnit>> video_input_unit_result = |
| VideoInputUnit::CreateForTesting(vpu_mmio_.GetMmioBuffer(), /*rdma=*/nullptr, |
| layer_image_size, display_contents_size); |
| ASSERT_OK(video_input_unit_result.status_value()); |
| display_engine_->SetVideoInputUnitForTesting(std::move(video_input_unit_result).value()); |
| |
| allocator_ = std::make_unique<MockAllocator>(loop_.dispatcher()); |
| allocator_->set_mock_buffer_collection_builder([] { |
| // Allocate importable primary Image by default. |
| const std::vector<fuchsia_images2::wire::PixelFormat> kPixelFormats = { |
| fuchsia_images2::wire::PixelFormat::kB8G8R8A8, |
| fuchsia_images2::wire::PixelFormat::kR8G8B8A8}; |
| return std::make_unique<MockBufferCollection>(kPixelFormats); |
| }); |
| |
| { |
| auto endpoints = fidl::Endpoints<fuchsia_sysmem2::Allocator>::Create(); |
| fidl::BindServer(loop_.dispatcher(), std::move(endpoints.server), allocator_.get()); |
| display_engine_->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: |
| std::shared_ptr<MockDevice> mock_root_; |
| async::Loop loop_; |
| |
| std::unique_ptr<display::Namespace> incoming_; |
| std::unique_ptr<display::MetadataGetter> metadata_getter_; |
| std::unique_ptr<display::DispatcherFactory> dispatcher_factory_; |
| |
| ddk_fake::FakeMmioRegRegion vpu_mmio_ = |
| ddk_fake::FakeMmioRegRegion(/*reg_size=*/4, /*reg_count=*/0x10000); |
| std::unique_ptr<DisplayEngine> display_engine_; |
| 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<fuchsia_sysmem2::BufferCollectionToken>> token1_endpoints = |
| fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>(); |
| ASSERT_OK(token1_endpoints.status_value()); |
| zx::result<fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>> token2_endpoints = |
| fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>(); |
| ASSERT_OK(token2_endpoints.status_value()); |
| |
| // Test ImportBufferCollection(). |
| constexpr display::DriverBufferCollectionId kValidBufferCollectionId(1); |
| constexpr uint64_t kBanjoValidBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kValidBufferCollectionId); |
| EXPECT_OK(display_engine_->DisplayControllerImplImportBufferCollection( |
| kBanjoValidBufferCollectionId, token1_endpoints->client.TakeChannel())); |
| |
| // `driver_buffer_collection_id` must be unused. |
| EXPECT_EQ(display_engine_->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_engine_->DisplayControllerImplReleaseBufferCollection( |
| kBanjoInvalidBufferCollectionId), |
| ZX_ERR_NOT_FOUND); |
| EXPECT_OK( |
| display_engine_->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) { |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| constexpr uint64_t kBanjoBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kBufferCollectionId); |
| EXPECT_OK(display_engine_->DisplayControllerImplImportBufferCollection( |
| kBanjoBufferCollectionId, token_client.TakeChannel())); |
| |
| static constexpr image_buffer_usage_t kDisplayUsage = { |
| .tiling_type = IMAGE_TILING_TYPE_LINEAR, |
| }; |
| EXPECT_OK(display_engine_->DisplayControllerImplSetBufferCollectionConstraints( |
| &kDisplayUsage, kBanjoBufferCollectionId)); |
| |
| constexpr display::DriverBufferCollectionId kInvalidBufferCollectionId(100); |
| constexpr uint64_t kBanjoInvalidBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kInvalidBufferCollectionId); |
| EXPECT_EQ(display_engine_->DisplayControllerImplSetBufferCollectionConstraints( |
| &kDisplayUsage, kBanjoInvalidBufferCollectionId), |
| ZX_ERR_NOT_FOUND); |
| |
| // Invalid import: Bad image type. |
| static constexpr image_metadata_t kInvalidTilingMetadata = { |
| .width = 1024, |
| .height = 768, |
| .tiling_type = IMAGE_TILING_TYPE_CAPTURE, |
| }; |
| uint64_t image_handle = 0; |
| EXPECT_EQ(display_engine_->DisplayControllerImplImportImage(&kInvalidTilingMetadata, |
| kBanjoBufferCollectionId, |
| /*index=*/0, &image_handle), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Invalid import: Invalid collection ID. |
| static constexpr image_metadata_t kDisplayImageMetadata = { |
| .width = 1024, |
| .height = 768, |
| .tiling_type = IMAGE_TILING_TYPE_LINEAR, |
| }; |
| EXPECT_EQ(display_engine_->DisplayControllerImplImportImage(&kDisplayImageMetadata, |
| kBanjoInvalidBufferCollectionId, |
| /*index=*/0, &image_handle), |
| ZX_ERR_NOT_FOUND); |
| |
| // Invalid import: Invalid buffer collection index. |
| constexpr uint64_t kInvalidBufferCollectionIndex = 100u; |
| image_handle = 0; |
| EXPECT_EQ(display_engine_->DisplayControllerImplImportImage( |
| &kDisplayImageMetadata, kBanjoBufferCollectionId, kInvalidBufferCollectionIndex, |
| &image_handle), |
| ZX_ERR_OUT_OF_RANGE); |
| |
| // Valid import. |
| EXPECT_OK(display_engine_->DisplayControllerImplImportImage(&kDisplayImageMetadata, |
| kBanjoBufferCollectionId, |
| /*index=*/0, &image_handle)); |
| EXPECT_NE(image_handle, 0u); |
| |
| // Release the image. |
| display_engine_->DisplayControllerImplReleaseImage(image_handle); |
| |
| EXPECT_OK( |
| display_engine_->DisplayControllerImplReleaseBufferCollection(kBanjoBufferCollectionId)); |
| } |
| |
| TEST_F(FakeSysmemTest, ImportImageForCapture) { |
| allocator_->set_mock_buffer_collection_builder( |
| [] { return std::make_unique<MockBufferCollectionForCapture>(); }); |
| |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| constexpr uint64_t kBanjoBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kBufferCollectionId); |
| EXPECT_OK(display_engine_->DisplayControllerImplImportBufferCollection( |
| kBanjoBufferCollectionId, token_client.TakeChannel())); |
| |
| // Driver sets BufferCollection buffer memory constraints. |
| static constexpr image_buffer_usage_t kCaptureUsage = { |
| .tiling_type = IMAGE_TILING_TYPE_CAPTURE, |
| }; |
| EXPECT_OK(display_engine_->DisplayControllerImplSetBufferCollectionConstraints( |
| &kCaptureUsage, kBanjoBufferCollectionId)); |
| |
| // Invalid import: invalid buffer collection ID. |
| uint64_t capture_handle = 0; |
| const uint64_t kBanjoInvalidBufferCollectionId = 100; |
| EXPECT_EQ( |
| display_engine_->DisplayControllerImplImportImageForCapture(kBanjoInvalidBufferCollectionId, |
| /*index=*/0, &capture_handle), |
| ZX_ERR_NOT_FOUND); |
| |
| // Invalid import: index out of range. |
| const uint64_t kInvalidIndex = 100; |
| EXPECT_EQ(display_engine_->DisplayControllerImplImportImageForCapture( |
| kBanjoBufferCollectionId, kInvalidIndex, &capture_handle), |
| ZX_ERR_OUT_OF_RANGE); |
| |
| // Valid import. |
| capture_handle = 0; |
| EXPECT_OK(display_engine_->DisplayControllerImplImportImageForCapture(kBanjoBufferCollectionId, |
| /*index=*/0, |
| &capture_handle)); |
| EXPECT_NE(capture_handle, 0u); |
| |
| // Release the image. |
| display_engine_->DisplayControllerImplReleaseCapture(capture_handle); |
| |
| EXPECT_OK( |
| display_engine_->DisplayControllerImplReleaseBufferCollection(kBanjoBufferCollectionId)); |
| } |
| |
| TEST_F(FakeSysmemTest, SysmemRequirements) { |
| MockBufferCollectionBase* collection = nullptr; |
| allocator_->set_mock_buffer_collection_builder([&collection] { |
| const std::vector<fuchsia_images2::wire::PixelFormat> kPixelFormats = { |
| fuchsia_images2::wire::PixelFormat::kB8G8R8A8, |
| fuchsia_images2::wire::PixelFormat::kR8G8B8A8}; |
| auto new_buffer_collection = std::make_unique<MockBufferCollection>(kPixelFormats); |
| collection = new_buffer_collection.get(); |
| return new_buffer_collection; |
| }); |
| |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| constexpr uint64_t kBanjoBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kBufferCollectionId); |
| EXPECT_OK(display_engine_->DisplayControllerImplImportBufferCollection( |
| kBanjoBufferCollectionId, token_client.TakeChannel())); |
| |
| EXPECT_TRUE(PollUntil([&] { return collection != nullptr; }, zx::msec(5), 1000)); |
| |
| static constexpr image_buffer_usage_t kDisplayUsage = { |
| .tiling_type = IMAGE_TILING_TYPE_LINEAR, |
| }; |
| EXPECT_OK(display_engine_->DisplayControllerImplSetBufferCollectionConstraints( |
| &kDisplayUsage, 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<fuchsia_images2::wire::PixelFormat> kPixelFormats = { |
| fuchsia_images2::wire::PixelFormat::kB8G8R8A8, |
| }; |
| auto new_buffer_collection = std::make_unique<MockBufferCollection>(kPixelFormats); |
| collection = new_buffer_collection.get(); |
| return new_buffer_collection; |
| }); |
| display_engine_->SetFormatSupportCheck([](fuchsia_images2::wire::PixelFormat format) { |
| return format == fuchsia_images2::wire::PixelFormat::kB8G8R8A8; |
| }); |
| |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| constexpr uint64_t kBanjoBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kBufferCollectionId); |
| EXPECT_OK(display_engine_->DisplayControllerImplImportBufferCollection( |
| kBanjoBufferCollectionId, token_client.TakeChannel())); |
| |
| EXPECT_TRUE(PollUntil([&] { return collection != nullptr; }, zx::msec(5), 1000)); |
| |
| static constexpr image_buffer_usage_t kDisplayUsage = { |
| .tiling_type = IMAGE_TILING_TYPE_LINEAR, |
| }; |
| EXPECT_OK(display_engine_->DisplayControllerImplSetBufferCollectionConstraints( |
| &kDisplayUsage, 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, VideoInputUnit::FloatToFixed3_10(0.0f)); |
| EXPECT_EQ(0x0066u, VideoInputUnit::FloatToFixed3_10(0.1f)); |
| EXPECT_EQ(0x1f9au, VideoInputUnit::FloatToFixed3_10(-0.1f)); |
| // Test for maximum positive (<4) |
| EXPECT_EQ(0x0FFFu, VideoInputUnit::FloatToFixed3_10(4.0f)); |
| EXPECT_EQ(0x0FFFu, VideoInputUnit::FloatToFixed3_10(40.0f)); |
| EXPECT_EQ(0x0FFFu, VideoInputUnit::FloatToFixed3_10(3.9999f)); |
| // Test for minimum negative (>= -4) |
| EXPECT_EQ(0x1000u, VideoInputUnit::FloatToFixed3_10(-4.0f)); |
| EXPECT_EQ(0x1000u, VideoInputUnit::FloatToFixed3_10(-14.0f)); |
| } |
| |
| TEST(AmlogicDisplay, FloatToFixed2_10) { |
| inspect::Inspector inspector; |
| EXPECT_EQ(0x0000u, VideoInputUnit::FloatToFixed2_10(0.0f)); |
| EXPECT_EQ(0x0066u, VideoInputUnit::FloatToFixed2_10(0.1f)); |
| EXPECT_EQ(0x0f9au, VideoInputUnit::FloatToFixed2_10(-0.1f)); |
| // Test for maximum positive (<2) |
| EXPECT_EQ(0x07FFu, VideoInputUnit::FloatToFixed2_10(2.0f)); |
| EXPECT_EQ(0x07FFu, VideoInputUnit::FloatToFixed2_10(20.0f)); |
| EXPECT_EQ(0x07FFu, VideoInputUnit::FloatToFixed2_10(1.9999f)); |
| // Test for minimum negative (>= -2) |
| EXPECT_EQ(0x0800u, VideoInputUnit::FloatToFixed2_10(-2.0f)); |
| EXPECT_EQ(0x0800u, VideoInputUnit::FloatToFixed2_10(-14.0f)); |
| } |
| |
| TEST_F(FakeSysmemTest, NoLeakCaptureCanvas) { |
| allocator_->set_mock_buffer_collection_builder( |
| [] { return std::make_unique<MockBufferCollectionForCapture>(); }); |
| |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| constexpr uint64_t kBanjoBufferCollectionId = |
| display::ToBanjoDriverBufferCollectionId(kBufferCollectionId); |
| EXPECT_OK(display_engine_->DisplayControllerImplImportBufferCollection( |
| kBanjoBufferCollectionId, token_client.TakeChannel())); |
| |
| uint64_t capture_handle; |
| EXPECT_OK(display_engine_->DisplayControllerImplImportImageForCapture(kBanjoBufferCollectionId, |
| /*index=*/0, |
| &capture_handle)); |
| EXPECT_OK(display_engine_->DisplayControllerImplReleaseCapture(capture_handle)); |
| |
| canvas_.SyncCall(&FakeCanvasProtocol::CheckThatNoEntriesInUse); |
| } |
| |
| } // namespace |
| |
| } // namespace amlogic_display |