| // Copyright 2024 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/intel-display/intel-display-driver.h" |
| |
| #include <fidl/fuchsia.kernel/cpp/test_base.h> |
| #include <fidl/fuchsia.sysmem2/cpp/wire.h> |
| #include <fidl/fuchsia.system.state/cpp/test_base.h> |
| #include <fuchsia/hardware/intelgpucore/c/banjo.h> |
| #include <fuchsia/hardware/intelgpucore/cpp/banjo.h> |
| #include <lib/async_patterns/testing/cpp/dispatcher_bound.h> |
| #include <lib/component/outgoing/cpp/outgoing_directory.h> |
| #include <lib/ddk/driver.h> |
| #include <lib/driver/logging/cpp/logger.h> |
| #include <lib/driver/testing/cpp/driver_runtime.h> |
| #include <lib/driver/testing/cpp/internal/driver_lifecycle.h> |
| #include <lib/driver/testing/cpp/internal/test_environment.h> |
| #include <lib/driver/testing/cpp/test_node.h> |
| #include <lib/fake-resource/resource.h> |
| #include <lib/fdf/cpp/dispatcher.h> |
| #include <lib/zbi-format/graphics.h> |
| #include <lib/zircon-internal/align.h> |
| #include <lib/zx/resource.h> |
| #include <lib/zx/result.h> |
| #include <limits.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/resource.h> |
| #include <zircon/types.h> |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstdint> |
| #include <limits> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <bind/fuchsia/cpp/bind.h> |
| #include <bind/fuchsia/display/cpp/bind.h> |
| #include <bind/fuchsia/intel/platform/gpucore/cpp/bind.h> |
| #include <ddktl/device.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/devices/pci/testing/pci_protocol_fake.h" |
| #include "src/graphics/display/drivers/intel-display/gtt.h" |
| #include "src/graphics/display/drivers/intel-display/intel-display.h" |
| #include "src/graphics/display/drivers/intel-display/pci-ids.h" |
| #include "src/graphics/display/drivers/intel-display/registers.h" |
| #include "src/graphics/display/drivers/intel-display/testing/fake-buffer-collection.h" |
| #include "src/graphics/display/drivers/intel-display/testing/fake-framebuffer.h" |
| #include "src/graphics/display/drivers/intel-display/testing/mock-allocator.h" |
| #include "src/graphics/display/lib/api-types/cpp/driver-buffer-collection-id.h" |
| #include "src/graphics/display/lib/api-types/cpp/driver-image-id.h" |
| #include "src/graphics/display/lib/api-types/cpp/image-metadata.h" |
| #include "src/graphics/display/lib/api-types/cpp/image-tiling-type.h" |
| #include "src/lib/testing/predicates/status.h" |
| |
| namespace { |
| |
| constexpr uint32_t kBytesPerRowDivisor = 1024; |
| |
| } // namespace |
| |
| namespace intel_display { |
| |
| namespace { |
| |
| zx::resource CreateFakeRootResource() { |
| zx::resource root; |
| zx_status_t status = fake_root_resource_create(root.reset_and_get_address()); |
| ZX_ASSERT(status == ZX_OK); |
| return root; |
| } |
| |
| class FakeSystemStateTransition |
| : public fidl::testing::TestBase<fuchsia_system_state::SystemStateTransition> { |
| public: |
| FakeSystemStateTransition() = default; |
| ~FakeSystemStateTransition() override = default; |
| |
| // fidl::testing::TestBase: |
| void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override { |
| ZX_PANIC("Not implemented: %s", name.c_str()); |
| } |
| |
| // fuchsia_system_state::SystemStateTransition: |
| void GetTerminationSystemState(GetTerminationSystemStateCompleter::Sync& completer) override { |
| completer.Reply({termination_system_state_}); |
| } |
| |
| void SetTerminationSystemState(fuchsia_system_state::SystemPowerState termination_system_state) { |
| termination_system_state_ = termination_system_state; |
| } |
| |
| private: |
| fuchsia_system_state::SystemPowerState termination_system_state_ = |
| fuchsia_system_state::SystemPowerState::kFullyOn; |
| }; |
| |
| class FakeMmioResource : public fidl::testing::TestBase<fuchsia_kernel::MmioResource> { |
| public: |
| // `root_resource` must outlive `FakeFramebufferResource`. |
| explicit FakeMmioResource(zx::unowned_resource root_resource) |
| : root_resource_(root_resource->borrow()) {} |
| ~FakeMmioResource() override = default; |
| |
| // fidl::testing::TestBase: |
| void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override { |
| ZX_PANIC("Not implemented: %s", name.c_str()); |
| } |
| |
| // fuchsia_kernel::FramebufferResource: |
| void Get(GetCompleter::Sync& completer) override { |
| zx::resource mmio_child; |
| std::array<char, ZX_MAX_NAME_LEN> child_name = {"child"}; |
| zx_status_t status = |
| zx_resource_create(root_resource_->get(), ZX_RSRC_KIND_MMIO, 16, 32, child_name.data(), |
| child_name.size(), mmio_child.reset_and_get_address()); |
| ZX_ASSERT(status == ZX_OK); |
| completer.Reply(std::move(mmio_child)); |
| } |
| |
| private: |
| zx::unowned_resource root_resource_; |
| }; |
| |
| class FakeIoportResource : public fidl::testing::TestBase<fuchsia_kernel::IoportResource> { |
| public: |
| // `root_resource` must outlive `FakeFramebufferResource`. |
| explicit FakeIoportResource(zx::unowned_resource root_resource) |
| : root_resource_(root_resource->borrow()) {} |
| ~FakeIoportResource() override = default; |
| |
| // fidl::testing::TestBase: |
| void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override { |
| ZX_PANIC("Not implemented: %s", name.c_str()); |
| } |
| |
| // fuchsia_kernel::FramebufferResource: |
| void Get(GetCompleter::Sync& completer) override { |
| zx::resource ioport_child; |
| std::array<char, ZX_MAX_NAME_LEN> child_name = {"child"}; |
| zx_status_t status = |
| zx_resource_create(root_resource_->get(), ZX_RSRC_KIND_IOPORT, 32, 64, child_name.data(), |
| child_name.size(), ioport_child.reset_and_get_address()); |
| ZX_ASSERT(status == ZX_OK); |
| completer.Reply(std::move(ioport_child)); |
| } |
| |
| private: |
| zx::unowned_resource root_resource_; |
| }; |
| |
| // WARNING: Don't use this test as a template for new tests as it uses the old driver testing |
| // library. |
| class IntegrationTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| SetUpFakeDevices(); |
| SetUpEnvironment(); |
| } |
| |
| void TearDown() override { |
| driver_.reset(); |
| test_environment_.reset(); |
| node_server_.reset(); |
| driver_runtime_.ShutdownAllDispatchers(nullptr); |
| } |
| |
| MockAllocator* sysmem() { return &sysmem_; } |
| fake_framebuffer::FakeBootItems* boot_items() { return &boot_items_; } |
| |
| fidl::ClientEnd<fuchsia_io::Directory> ConnectToDriverSvcDir(); |
| |
| protected: |
| void SetUpFakeFramebuffer(); |
| void SetUpFakeSysmem(); |
| void SetUpFakePci(); |
| void SetUpFakeDevices(); |
| void SetUpEnvironment(); |
| |
| fdf_testing::DriverRuntime driver_runtime_; |
| fdf::UnownedSynchronizedDispatcher pci_dispatcher_ = driver_runtime_.StartBackgroundDispatcher(); |
| fdf::UnownedSynchronizedDispatcher env_dispatcher_ = driver_runtime_.StartBackgroundDispatcher(); |
| fdf::UnownedSynchronizedDispatcher display_dispatcher_ = |
| driver_runtime_.StartBackgroundDispatcher(); |
| |
| async_patterns::TestDispatcherBound<fdf_testing::TestNode> node_server_{ |
| env_dispatcher_->async_dispatcher(), std::in_place, std::string("root")}; |
| async_patterns::TestDispatcherBound<fdf_testing::internal::TestEnvironment> test_environment_{ |
| env_dispatcher_->async_dispatcher(), std::in_place}; |
| async_patterns::TestDispatcherBound<fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>> |
| driver_{display_dispatcher_->async_dispatcher(), std::in_place}; |
| |
| pci::FakePciProtocol pci_; |
| MockAllocator sysmem_{env_dispatcher_->async_dispatcher()}; |
| fake_framebuffer::FakeBootItems boot_items_; |
| |
| zx::resource fake_root_resource_ = CreateFakeRootResource(); |
| FakeMmioResource fake_mmio_resource_{fake_root_resource_.borrow()}; |
| FakeIoportResource fake_ioport_resource_{fake_root_resource_.borrow()}; |
| FakeSystemStateTransition fake_system_state_transition_; |
| |
| fuchsia_driver_framework::DriverStartArgs start_args_; |
| fidl::ClientEnd<fuchsia_io::Directory> driver_outgoing_; |
| }; |
| |
| void IntegrationTest::SetUpFakeFramebuffer() { boot_items_.SetFramebuffer({}); } |
| |
| void IntegrationTest::SetUpFakeSysmem() { |
| sysmem_.SetNewBufferCollectionConfig({ |
| .cpu_domain_supported = false, |
| .ram_domain_supported = true, |
| .inaccessible_domain_supported = false, |
| .bytes_per_row_divisor = kBytesPerRowDivisor, |
| .format_modifier = fuchsia_images2::wire::PixelFormatModifier::kLinear, |
| }); |
| } |
| |
| void IntegrationTest::SetUpFakePci() { |
| pci_.CreateBar(0u, std::numeric_limits<uint32_t>::max(), /*is_mmio=*/true); |
| pci_.AddLegacyInterrupt(); |
| |
| // This configures the "GMCH Graphics Control" register to report 2MB for the available GTT |
| // Graphics Memory. All other bits of this register are set to zero and should get populated as |
| // required for the tests below. |
| pci_.PciWriteConfig16(registers::GmchGfxControl::kAddr, 0x40); |
| |
| constexpr uint16_t kIntelVendorId = 0x8086; |
| pci_.SetDeviceInfo({ |
| .vendor_id = kIntelVendorId, |
| .device_id = kTestDeviceDid, |
| }); |
| } |
| |
| void IntegrationTest::SetUpFakeDevices() { |
| SetUpFakeFramebuffer(); |
| SetUpFakeSysmem(); |
| SetUpFakePci(); |
| } |
| |
| fidl::ClientEnd<fuchsia_io::Directory> IntegrationTest::ConnectToDriverSvcDir() { |
| // Open the svc directory in the driver's outgoing, and store a client to it. |
| auto svc_endpoints = fidl::Endpoints<fuchsia_io::Directory>::Create(); |
| |
| zx_status_t status = fdio_open3_at( |
| driver_outgoing_.handle()->get(), "/svc", |
| static_cast<uint64_t>(fuchsia_io::kPermReadable | fuchsia_io::Flags::kProtocolDirectory), |
| svc_endpoints.server.TakeChannel().release()); |
| EXPECT_OK(status); |
| return std::move(svc_endpoints.client); |
| } |
| |
| void IntegrationTest::SetUpEnvironment() { |
| zx::result create_start_args_zx_result = |
| node_server_.SyncCall(&fdf_testing::TestNode::CreateStartArgsAndServe); |
| ASSERT_TRUE(create_start_args_zx_result.is_ok()); |
| |
| auto [start_args, incoming_directory_server, outgoing_directory_client] = |
| std::move(create_start_args_zx_result).value(); |
| start_args_ = std::move(start_args); |
| driver_outgoing_ = std::move(outgoing_directory_client); |
| |
| zx::result<> add_sysmem_result = |
| test_environment_.SyncCall([&](fdf_testing::internal::TestEnvironment* env) { |
| return env->incoming_directory() |
| .component() |
| .AddUnmanagedProtocol<fuchsia_sysmem2::Allocator>( |
| sysmem_.bind_handler(env_dispatcher_->async_dispatcher())); |
| }); |
| ASSERT_OK(add_sysmem_result); |
| |
| zx::result<> add_boot_items_result = |
| test_environment_.SyncCall([&](fdf_testing::internal::TestEnvironment* env) { |
| return env->incoming_directory().component().AddUnmanagedProtocol<fuchsia_boot::Items>( |
| boot_items_.CreateHandler(env_dispatcher_->async_dispatcher())); |
| }); |
| ASSERT_OK(add_boot_items_result); |
| |
| zx::result<> add_pci_result = |
| test_environment_.SyncCall([&](fdf_testing::internal::TestEnvironment* env) { |
| return env->incoming_directory().AddService<fuchsia_hardware_pci::Service>( |
| fuchsia_hardware_pci::Service::InstanceHandler( |
| {.device = pci_.bind_handler(pci_dispatcher_->async_dispatcher())}), |
| "pci"); |
| }); |
| ASSERT_OK(add_pci_result); |
| |
| zx::result<> add_mmio_resource_result = |
| test_environment_.SyncCall([&](fdf_testing::internal::TestEnvironment* env) { |
| return env->incoming_directory() |
| .component() |
| .AddUnmanagedProtocol<fuchsia_kernel::MmioResource>( |
| fake_mmio_resource_.bind_handler(env_dispatcher_->async_dispatcher())); |
| }); |
| ASSERT_OK(add_mmio_resource_result); |
| |
| zx::result<> add_ioport_resource_result = |
| test_environment_.SyncCall([&](fdf_testing::internal::TestEnvironment* env) { |
| return env->incoming_directory() |
| .component() |
| .AddUnmanagedProtocol<fuchsia_kernel::IoportResource>( |
| fake_ioport_resource_.bind_handler(env_dispatcher_->async_dispatcher())); |
| }); |
| ASSERT_OK(add_ioport_resource_result); |
| |
| zx::result<> add_system_state_transition_result = |
| test_environment_.SyncCall([&](fdf_testing::internal::TestEnvironment* env) { |
| return env->incoming_directory() |
| .component() |
| .AddUnmanagedProtocol<fuchsia_system_state::SystemStateTransition>( |
| fake_system_state_transition_.bind_handler(env_dispatcher_->async_dispatcher())); |
| }); |
| ASSERT_OK(add_system_state_transition_result); |
| |
| zx::result<> serve_result = test_environment_.SyncCall( |
| &fdf_testing::internal::TestEnvironment::Initialize, std::move(incoming_directory_server)); |
| ASSERT_OK(serve_result); |
| } |
| |
| struct DeviceNode { |
| std::string name; |
| std::vector<fuchsia_driver_framework::NodeProperty2> properties; |
| }; |
| |
| bool IsDisplayEngineBanjoNode(const DeviceNode& node) { |
| const std::vector<fuchsia_driver_framework::NodeProperty2>& properties = node.properties; |
| return properties.end() != |
| std::find_if(properties.begin(), properties.end(), |
| [](const fuchsia_driver_framework::NodeProperty2& property) { |
| if (!property.value().int_value().has_value()) |
| return false; |
| return property.key() == bind_fuchsia::PROTOCOL && |
| property.value().int_value().value() == |
| bind_fuchsia_display::BIND_PROTOCOL_ENGINE; |
| }); |
| } |
| |
| bool IsIntelGpuCoreNode(const DeviceNode& node) { |
| const std::vector<fuchsia_driver_framework::NodeProperty2>& properties = node.properties; |
| return properties.end() != |
| std::find_if(properties.begin(), properties.end(), |
| [](const fuchsia_driver_framework::NodeProperty2& property) { |
| if (!property.value().int_value().has_value()) |
| return false; |
| return property.key() == bind_fuchsia::PROTOCOL && |
| property.value().int_value().value() == |
| bind_fuchsia_intel_platform_gpucore::BIND_PROTOCOL_DEVICE; |
| }); |
| } |
| |
| TEST_F(IntegrationTest, BindAndInit) { |
| zx::result<> start_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::Start, |
| (std::move(start_args_)))); |
| ASSERT_OK(start_result); |
| |
| std::vector<DeviceNode> nodes = node_server_.SyncCall([&](fdf_testing::TestNode* root) { |
| std::vector<DeviceNode> nodes; |
| for (auto& [name, node] : root->children()) { |
| nodes.push_back({ |
| .name = name, |
| .properties = node.GetProperties(), |
| }); |
| } |
| return nodes; |
| }); |
| |
| // There should be two published node: one "intel-display-controller" node |
| // and a child "intel-gpu-core" node. |
| ASSERT_EQ(nodes.size(), 2u); |
| |
| auto display_engine_banjo_node_it = |
| std::find_if(nodes.begin(), nodes.end(), IsDisplayEngineBanjoNode); |
| ASSERT_NE(display_engine_banjo_node_it, nodes.end()); |
| fdf::info("Display controller banjo node is: {}", display_engine_banjo_node_it->name); |
| |
| auto intel_gpu_core_node_it = std::find_if(nodes.begin(), nodes.end(), IsIntelGpuCoreNode); |
| ASSERT_NE(intel_gpu_core_node_it, nodes.end()); |
| fdf::info("Intel GPU node is: {}", display_engine_banjo_node_it->name); |
| |
| ASSERT_NE(display_engine_banjo_node_it, intel_gpu_core_node_it); |
| |
| zx::result<> stop_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::PrepareStop)); |
| EXPECT_OK(stop_result); |
| } |
| |
| // Tests that the device can initialize even if bootloader framebuffer information is not available |
| // and global GTT allocations start at offset 0. |
| TEST_F(IntegrationTest, InitFailsIfBootloaderGetInfoFails) { |
| boot_items()->SetFramebuffer({.status = ZX_ERR_INVALID_ARGS}); |
| |
| zx::result<> start_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::Start, |
| (std::move(start_args_)))); |
| ASSERT_OK(start_result); |
| |
| zx::result<ddk::AnyProtocol> gpu_protocol_result = |
| driver_.SyncCall([&](fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>* driver) { |
| return (*driver)->GetProtocol(ZX_PROTOCOL_INTEL_GPU_CORE); |
| }); |
| ASSERT_OK(gpu_protocol_result); |
| ddk::AnyProtocol gpu_protocol = std::move(gpu_protocol_result).value(); |
| ddk::IntelGpuCoreProtocolClient gpu( |
| reinterpret_cast<const intel_gpu_core_protocol_t*>(&gpu_protocol)); |
| |
| uint64_t addr; |
| |
| zx_status_t alloc_status = gpu.GttAlloc(1, &addr); |
| EXPECT_OK(alloc_status); |
| EXPECT_EQ(0u, addr); |
| |
| zx::result<> stop_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::PrepareStop)); |
| EXPECT_OK(stop_result); |
| } |
| |
| // TODO(https://fxbug.dev/42166779): Add tests for DisplayPort display enumeration, |
| // covering the following cases: |
| // - Display found during start up but not already powered. |
| // - Display found during start up but already powered up. |
| // - Display added and removed in a hotplug event. |
| // TODO(https://fxbug.dev/42167311): Add test for HDMI display enumeration. |
| // TODO(https://fxbug.dev/42167312): Add test for DVI display enumeration. |
| |
| TEST_F(IntegrationTest, GttAllocationDoesNotOverlapBootloaderFramebuffer) { |
| constexpr uint32_t kStride = 1920; |
| constexpr uint32_t kHeight = 1080; |
| boot_items()->SetFramebuffer({ |
| .format = ZBI_PIXEL_FORMAT_RGB_888, |
| .width = kStride, |
| .height = kHeight, |
| .stride = kStride, |
| }); |
| |
| zx::result<> start_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::Start, |
| (std::move(start_args_)))); |
| ASSERT_OK(start_result); |
| |
| zx::result<ddk::AnyProtocol> gpu_protocol_result = |
| driver_.SyncCall([&](fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>* driver) { |
| return (*driver)->GetProtocol(ZX_PROTOCOL_INTEL_GPU_CORE); |
| }); |
| ASSERT_OK(gpu_protocol_result); |
| ddk::AnyProtocol gpu_protocol = std::move(gpu_protocol_result).value(); |
| ddk::IntelGpuCoreProtocolClient gpu( |
| reinterpret_cast<const intel_gpu_core_protocol_t*>(&gpu_protocol)); |
| |
| uint64_t addr; |
| zx_status_t alloc_status = gpu.GttAlloc(1, &addr); |
| EXPECT_OK(alloc_status); |
| EXPECT_EQ(ZX_ROUNDUP(kHeight * kStride * 3, PAGE_SIZE), addr); |
| |
| zx::result<> stop_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::PrepareStop)); |
| EXPECT_OK(stop_result); |
| } |
| |
| TEST_F(IntegrationTest, SysmemImport) { |
| static constexpr int kImageWidth = 128; |
| static constexpr int kImageHeight = 32; |
| sysmem_.SetNewBufferCollectionConfig({ |
| .cpu_domain_supported = false, |
| .ram_domain_supported = true, |
| .inaccessible_domain_supported = false, |
| |
| .width_fallback_px = kImageWidth, |
| .height_fallback_px = kImageHeight, |
| .bytes_per_row_divisor = kBytesPerRowDivisor, |
| .format_modifier = fuchsia_images2::wire::PixelFormatModifier::kLinear, |
| }); |
| |
| zx::result<> start_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::Start, |
| (std::move(start_args_)))); |
| ASSERT_OK(start_result); |
| |
| // TODO(https://fxbug.dev/435890974): Instead of using the internal |
| // DriverTransportConnect() method, use the testing framework in |
| // //sdk/lib/driver/testing/cpp/driver_test.h . |
| zx::result<fdf::ClientEnd<fuchsia_hardware_display_engine::Engine>> connect_engine_result = |
| fdf::internal::DriverTransportConnect<fuchsia_hardware_display_engine::Service::Engine>( |
| ConnectToDriverSvcDir(), component::kDefaultInstance); |
| ASSERT_OK(connect_engine_result); |
| |
| fdf::WireSyncClient display(std::move(connect_engine_result).value()); |
| |
| // Import buffer collection. |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| fdf::Arena arena('TEST'); |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::ImportBufferCollection> |
| import_fidl_transport_result = display.buffer(arena)->ImportBufferCollection( |
| kBufferCollectionId.ToFidl(), std::move(token_client)); |
| ASSERT_TRUE(import_fidl_transport_result.ok()); |
| |
| fit::result<zx_status_t> import_fidl_domain_result = import_fidl_transport_result.value(); |
| ASSERT_TRUE(import_fidl_domain_result.is_ok()); |
| |
| static constexpr display::ImageBufferUsage kDisplayUsage({ |
| .tiling_type = display::ImageTilingType::kLinear, |
| }); |
| |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::SetBufferCollectionConstraints> |
| set_constraints_fidl_transport_result = display.buffer(arena)->SetBufferCollectionConstraints( |
| kDisplayUsage.ToFidl(), kBufferCollectionId.ToFidl()); |
| ASSERT_TRUE(set_constraints_fidl_transport_result.ok()); |
| |
| fit::result<zx_status_t> set_constraints_fidl_domain_result = |
| set_constraints_fidl_transport_result.value(); |
| ASSERT_TRUE(set_constraints_fidl_domain_result.is_ok()); |
| |
| MockAllocator& allocator = *sysmem(); |
| driver_runtime_.RunUntil([&] { |
| FakeBufferCollection* collection = allocator.GetMostRecentBufferCollection(); |
| return collection && collection->HasConstraints(); |
| }); |
| |
| static constexpr display::ImageMetadata kDisplayImageMetadata({ |
| .width = kImageWidth, |
| .height = kImageHeight, |
| .tiling_type = display::ImageTilingType::kLinear, |
| }); |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::ImportImage> |
| import_image_fidl_transport_result = display.buffer(arena)->ImportImage( |
| kDisplayImageMetadata.ToFidl(), kBufferCollectionId.ToFidl(), /*index=*/0); |
| ASSERT_TRUE(import_image_fidl_transport_result.ok()); |
| |
| fit::result import_image_fidl_domain_result = import_image_fidl_transport_result.value(); |
| ASSERT_TRUE(import_image_fidl_domain_result.is_ok()); |
| display::DriverImageId image_id(import_image_fidl_domain_result->image_id); |
| |
| uint64_t bytes_per_row = |
| driver_.SyncCall([&](fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>* driver) { |
| const GttRegion& region = (*driver)->controller()->SetupGttImage( |
| kDisplayImageMetadata, image_id, display::CoordinateTransformation::kIdentity); |
| return region.bytes_per_row(); |
| }); |
| EXPECT_LT(kDisplayImageMetadata.dimensions().width() * 4, int32_t{kBytesPerRowDivisor}); |
| EXPECT_EQ(kBytesPerRowDivisor, bytes_per_row); |
| |
| fidl::OneWayStatus release_image_fidl_transport_result = |
| display.buffer(arena)->ReleaseImage(image_id.ToFidl()); |
| ASSERT_TRUE(release_image_fidl_transport_result.ok()); |
| |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::ReleaseBufferCollection> |
| release_collection_fidl_transport_result = |
| display.buffer(arena)->ReleaseBufferCollection(kBufferCollectionId.ToFidl()); |
| ASSERT_TRUE(release_collection_fidl_transport_result.ok()); |
| |
| fit::result<zx_status_t> release_collection_fidl_domain_result = |
| release_collection_fidl_transport_result.value(); |
| ASSERT_TRUE(release_collection_fidl_domain_result.is_ok()); |
| |
| zx::result<> stop_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::PrepareStop)); |
| EXPECT_OK(stop_result); |
| } |
| |
| TEST_F(IntegrationTest, SysmemRotated) { |
| static constexpr int kImageWidth = 128; |
| static constexpr int kImageHeight = 32; |
| sysmem_.SetNewBufferCollectionConfig({ |
| .cpu_domain_supported = false, |
| .ram_domain_supported = true, |
| .inaccessible_domain_supported = false, |
| |
| .width_fallback_px = kImageWidth, |
| .height_fallback_px = kImageHeight, |
| .bytes_per_row_divisor = kBytesPerRowDivisor, |
| .format_modifier = fuchsia_images2::wire::PixelFormatModifier::kIntelI915YTiled, |
| }); |
| |
| zx::result<> start_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::Start, |
| (std::move(start_args_)))); |
| ASSERT_OK(start_result); |
| |
| // TODO(https://fxbug.dev/435890974): Instead of using the internal |
| // DriverTransportConnect() method, use the testing framework in |
| // //sdk/lib/driver/testing/cpp/driver_test.h . |
| zx::result<fdf::ClientEnd<fuchsia_hardware_display_engine::Engine>> connect_engine_result = |
| fdf::internal::DriverTransportConnect<fuchsia_hardware_display_engine::Service::Engine>( |
| ConnectToDriverSvcDir(), component::kDefaultInstance); |
| ASSERT_OK(connect_engine_result); |
| |
| fdf::WireSyncClient display(std::move(connect_engine_result).value()); |
| |
| // Import buffer collection. |
| constexpr display::DriverBufferCollectionId kBufferCollectionId(1); |
| auto [token_client, token_server] = |
| fidl::Endpoints<fuchsia_sysmem2::BufferCollectionToken>::Create(); |
| |
| fdf::Arena arena('TEST'); |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::ImportBufferCollection> |
| import_fidl_transport_result = display.buffer(arena)->ImportBufferCollection( |
| kBufferCollectionId.ToFidl(), std::move(token_client)); |
| ASSERT_TRUE(import_fidl_transport_result.ok()); |
| |
| fit::result<zx_status_t> import_fidl_domain_result = import_fidl_transport_result.value(); |
| ASSERT_TRUE(import_fidl_domain_result.is_ok()); |
| |
| static constexpr display::ImageBufferUsage kDisplayUsage({ |
| // Must be y or yf tiled so rotation is allowed. |
| .tiling_type = display::ImageTilingType(IMAGE_TILING_TYPE_Y_LEGACY_TILED), |
| }); |
| |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::SetBufferCollectionConstraints> |
| set_constraints_fidl_transport_result = display.buffer(arena)->SetBufferCollectionConstraints( |
| kDisplayUsage.ToFidl(), kBufferCollectionId.ToFidl()); |
| ASSERT_TRUE(set_constraints_fidl_transport_result.ok()); |
| |
| fit::result<zx_status_t> set_constraints_fidl_domain_result = |
| set_constraints_fidl_transport_result.value(); |
| ASSERT_TRUE(set_constraints_fidl_domain_result.is_ok()); |
| |
| MockAllocator& allocator = *sysmem(); |
| driver_runtime_.RunUntil([&] { |
| FakeBufferCollection* collection = allocator.GetMostRecentBufferCollection(); |
| return collection && collection->HasConstraints(); |
| }); |
| |
| static constexpr display::ImageMetadata kDisplayImageMetadata({ |
| .width = kImageWidth, |
| .height = kImageHeight, |
| .tiling_type = display::ImageTilingType(IMAGE_TILING_TYPE_Y_LEGACY_TILED), |
| }); |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::ImportImage> |
| import_image_fidl_transport_result = display.buffer(arena)->ImportImage( |
| kDisplayImageMetadata.ToFidl(), kBufferCollectionId.ToFidl(), /*index=*/0); |
| ASSERT_TRUE(import_image_fidl_transport_result.ok()); |
| |
| fit::result import_image_fidl_domain_result = import_image_fidl_transport_result.value(); |
| ASSERT_TRUE(import_image_fidl_domain_result.is_ok()); |
| display::DriverImageId image_id(import_image_fidl_domain_result->image_id); |
| |
| // Check that rotating the image doesn't hang. |
| uint64_t bytes_per_row = |
| driver_.SyncCall([&](fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>* driver) { |
| const GttRegion& region = (*driver)->controller()->SetupGttImage( |
| kDisplayImageMetadata, image_id, display::CoordinateTransformation::kRotateCcw90); |
| return region.bytes_per_row(); |
| }); |
| EXPECT_LT(kDisplayImageMetadata.dimensions().width() * 4, int32_t{kBytesPerRowDivisor}); |
| EXPECT_EQ(kBytesPerRowDivisor, bytes_per_row); |
| |
| fidl::OneWayStatus release_image_fidl_transport_result = |
| display.buffer(arena)->ReleaseImage(image_id.ToFidl()); |
| ASSERT_TRUE(release_image_fidl_transport_result.ok()); |
| |
| fdf::WireUnownedResult<fuchsia_hardware_display_engine::Engine::ReleaseBufferCollection> |
| release_collection_fidl_transport_result = |
| display.buffer(arena)->ReleaseBufferCollection(kBufferCollectionId.ToFidl()); |
| ASSERT_TRUE(release_collection_fidl_transport_result.ok()); |
| |
| fit::result<zx_status_t> release_collection_fidl_domain_result = |
| release_collection_fidl_transport_result.value(); |
| ASSERT_TRUE(release_collection_fidl_domain_result.is_ok()); |
| |
| zx::result<> stop_result = driver_runtime_.RunToCompletion( |
| driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<IntelDisplayDriver>::PrepareStop)); |
| EXPECT_OK(stop_result); |
| } |
| |
| } // namespace |
| |
| } // namespace intel_display |