blob: 721df4a95044049ebc97846098ece7870863b1e8 [file] [log] [blame]
// Copyright 2021 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/ui/scenic/lib/display/display_manager.h"
#include <fidl/fuchsia.hardware.display/cpp/fidl.h>
#include <fidl/fuchsia.hardware.display/cpp/hlcpp_conversion.h>
#include <fidl/fuchsia.images2/cpp/fidl.h>
#include <fuchsia/hardware/display/cpp/fidl.h>
#include <fuchsia/hardware/display/types/cpp/fidl.h>
#include <fuchsia/images2/cpp/fidl.h>
#include <lib/async-testing/test_loop.h>
#include <lib/async/default.h>
#include <lib/async/time.h>
#include <lib/fidl/cpp/hlcpp_conversion.h>
#include <unordered_set>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/ui/scenic/lib/display/tests/mock_display_coordinator.h"
#include "src/ui/scenic/lib/utils/range_inclusive.h"
namespace scenic_impl {
namespace gfx {
namespace test {
namespace {
fidl::Endpoints<fuchsia_hardware_display::Coordinator> CreateCoordinatorEndpoints() {
zx::result<fidl::Endpoints<fuchsia_hardware_display::Coordinator>> endpoints_result =
fidl::CreateEndpoints<fuchsia_hardware_display::Coordinator>();
FX_CHECK(endpoints_result.is_ok())
<< "Failed to create endpoints: " << endpoints_result.status_string();
return std::move(endpoints_result.value());
}
} // namespace
class DisplayManagerMockTest : public gtest::TestLoopFixture {
public:
// |testing::Test|
void SetUp() override {
TestLoopFixture::SetUp();
async_set_default_dispatcher(dispatcher());
display_manager_ = std::make_unique<display::DisplayManager>([]() {});
}
// |testing::Test|
void TearDown() override {
display_manager_.reset();
TestLoopFixture::TearDown();
}
display::DisplayManager* display_manager() { return display_manager_.get(); }
display::Display* display() { return display_manager()->default_display(); }
private:
std::unique_ptr<display::DisplayManager> display_manager_;
};
TEST_F(DisplayManagerMockTest, DisplayVsyncCallback) {
constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
const uint32_t kDisplayWidth = 1024;
const uint32_t kDisplayHeight = 768;
const size_t kTotalVsync = 10;
const size_t kAcknowledgeRate = 5;
std::unordered_set<uint64_t> cookies_sent;
size_t num_vsync_display_received = 0;
size_t num_vsync_acknowledgement = 0;
auto coordinator_channel = CreateCoordinatorEndpoints();
display_manager()->BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
display_manager()->SetDefaultDisplayForTests(
std::make_shared<display::Display>(kDisplayId, kDisplayWidth, kDisplayHeight));
display::test::MockDisplayCoordinator mock_display_coordinator(
fuchsia::hardware::display::Info{});
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
mock_display_coordinator.set_acknowledge_vsync_fn(
[&cookies_sent, &num_vsync_acknowledgement](uint64_t cookie) {
ASSERT_TRUE(cookies_sent.find(cookie) != cookies_sent.end());
++num_vsync_acknowledgement;
});
display_manager()->default_display()->SetVsyncCallback(
[&num_vsync_display_received](zx::time timestamp,
fuchsia::hardware::display::types::ConfigStamp stamp) {
++num_vsync_display_received;
});
for (size_t vsync_id = 1; vsync_id <= kTotalVsync; vsync_id++) {
// We only require acknowledgement for every |kAcknowledgeRate| Vsync IDs.
uint64_t cookie = (vsync_id % kAcknowledgeRate == 0) ? vsync_id : 0;
test_loop().AdvanceTimeByEpsilon();
mock_display_coordinator.events().OnVsync(kDisplayId, /* timestamp */ test_loop().Now().get(),
{.value = 1u}, cookie);
if (cookie) {
cookies_sent.insert(cookie);
}
// Display coordinator should handle the incoming Vsync message.
EXPECT_TRUE(RunLoopUntilIdle());
}
EXPECT_EQ(num_vsync_display_received, kTotalVsync);
EXPECT_EQ(num_vsync_acknowledgement, kTotalVsync / kAcknowledgeRate);
}
TEST_F(DisplayManagerMockTest, OnDisplayAdded) {
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr int kDisplayWidth = 1024;
static constexpr int kDisplayHeight = 768;
static constexpr int kDisplayRefreshRateHz = 60;
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display_manager()->BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
fuchsia::hardware::display::Mode{
.horizontal_resolution = kDisplayWidth,
.vertical_resolution = kDisplayHeight,
.refresh_rate_e2 = kDisplayRefreshRateHz * 100,
},
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(RunLoopUntilIdle());
const display::Display* default_display = display_manager()->default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), static_cast<uint32_t>(kDisplayWidth));
EXPECT_EQ(default_display->height_in_px(), static_cast<uint32_t>(kDisplayHeight));
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
static_cast<uint32_t>(kDisplayRefreshRateHz * 1'000));
EXPECT_THAT(default_display->pixel_formats(),
testing::ElementsAreArray(kNaturalSupportedPixelFormats));
}
TEST_F(DisplayManagerMockTest, SelectPreferredMode) {
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr fuchsia::hardware::display::Mode kPreferredMode = {
.horizontal_resolution = 1024,
.vertical_resolution = 768,
.refresh_rate_e2 = 60'00,
};
static constexpr fuchsia::hardware::display::Mode kNonPreferredMode = {
.horizontal_resolution = 800,
.vertical_resolution = 600,
.refresh_rate_e2 = 30'00,
};
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display_manager()->BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
kPreferredMode,
kNonPreferredMode,
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(RunLoopUntilIdle());
const display::Display* default_display = display_manager()->default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), kPreferredMode.horizontal_resolution);
EXPECT_EQ(default_display->height_in_px(), kPreferredMode.vertical_resolution);
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
kPreferredMode.refresh_rate_e2 * 10);
}
TEST(DisplayManager, ICanHazDisplayMode) {
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr fuchsia::hardware::display::Mode kPreferredMode = {
.horizontal_resolution = 1024,
.vertical_resolution = 768,
.refresh_rate_e2 = 60'00,
};
static constexpr fuchsia::hardware::display::Mode kNonPreferredButSelectedMode = {
.horizontal_resolution = 800,
.vertical_resolution = 600,
.refresh_rate_e2 = 30'00,
};
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
async::TestLoop loop;
async_set_default_dispatcher(loop.dispatcher());
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
kPreferredMode,
kNonPreferredButSelectedMode,
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
display::DisplayManager display_manager(/*i_can_haz_display_id=*/std::nullopt,
/*display_mode_index_override=*/std::make_optional(1),
display::DisplayModeConstraints{},
/*display_available_cb=*/[]() {});
display_manager.BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(loop.RunUntilIdle());
const display::Display* default_display = display_manager.default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), kNonPreferredButSelectedMode.horizontal_resolution);
EXPECT_EQ(default_display->height_in_px(), kNonPreferredButSelectedMode.vertical_resolution);
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
kNonPreferredButSelectedMode.refresh_rate_e2 * 10);
}
TEST(DisplayManager, DisplayModeConstraintsHorizontalResolution) {
static const display::DisplayModeConstraints kDisplayModeConstraints = {
.width_px_range = utils::RangeInclusive(700, 900),
};
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr fuchsia::hardware::display::Mode kModeNotSatisfyingConstraints = {
.horizontal_resolution = 1024,
.vertical_resolution = 768,
.refresh_rate_e2 = 60'00,
};
static constexpr fuchsia::hardware::display::Mode kModeSatisfyingConstraints = {
.horizontal_resolution = 800,
.vertical_resolution = 600,
.refresh_rate_e2 = 30'00,
};
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
async::TestLoop loop;
async_set_default_dispatcher(loop.dispatcher());
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
kModeNotSatisfyingConstraints,
kModeSatisfyingConstraints,
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
display::DisplayManager display_manager(/*i_can_haz_display_id=*/std::nullopt,
/*display_mode_index_override=*/std::nullopt,
kDisplayModeConstraints,
/*display_available_cb=*/[]() {});
display_manager.BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(loop.RunUntilIdle());
const display::Display* default_display = display_manager.default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), kModeSatisfyingConstraints.horizontal_resolution);
EXPECT_EQ(default_display->height_in_px(), kModeSatisfyingConstraints.vertical_resolution);
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
kModeSatisfyingConstraints.refresh_rate_e2 * 10);
}
TEST(DisplayManager, DisplayModeConstraintsVerticalResolution) {
static const display::DisplayModeConstraints kDisplayModeConstraints = {
.height_px_range = utils::RangeInclusive(500, 700),
};
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr fuchsia::hardware::display::Mode kModeNotSatisfyingConstraints = {
.horizontal_resolution = 1024,
.vertical_resolution = 768,
.refresh_rate_e2 = 60'00,
};
static constexpr fuchsia::hardware::display::Mode kModeSatisfyingConstraints = {
.horizontal_resolution = 800,
.vertical_resolution = 600,
.refresh_rate_e2 = 30'00,
};
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
async::TestLoop loop;
async_set_default_dispatcher(loop.dispatcher());
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
kModeNotSatisfyingConstraints,
kModeSatisfyingConstraints,
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
display::DisplayManager display_manager(/*i_can_haz_display_id=*/std::nullopt,
/*display_mode_index_override=*/std::nullopt,
kDisplayModeConstraints,
/*display_available_cb=*/[]() {});
display_manager.BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(loop.RunUntilIdle());
const display::Display* default_display = display_manager.default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), kModeSatisfyingConstraints.horizontal_resolution);
EXPECT_EQ(default_display->height_in_px(), kModeSatisfyingConstraints.vertical_resolution);
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
kModeSatisfyingConstraints.refresh_rate_e2 * 10);
}
TEST(DisplayManager, DisplayModeConstraintsRefreshRateLimit) {
static const display::DisplayModeConstraints kDisplayModeConstraints = {
.refresh_rate_millihertz_range = utils::RangeInclusive(20'000, 50'000),
};
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr fuchsia::hardware::display::Mode kModeNotSatisfyingConstraints = {
.horizontal_resolution = 1024,
.vertical_resolution = 768,
.refresh_rate_e2 = 60'00,
};
static constexpr fuchsia::hardware::display::Mode kModeSatisfyingConstraints = {
.horizontal_resolution = 800,
.vertical_resolution = 600,
.refresh_rate_e2 = 30'00,
};
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
async::TestLoop loop;
async_set_default_dispatcher(loop.dispatcher());
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
kModeNotSatisfyingConstraints,
kModeSatisfyingConstraints,
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
display::DisplayManager display_manager(/*i_can_haz_display_id=*/std::nullopt,
/*display_mode_index_override=*/std::nullopt,
kDisplayModeConstraints,
/*display_available_cb=*/[]() {});
display_manager.BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(loop.RunUntilIdle());
const display::Display* default_display = display_manager.default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), kModeSatisfyingConstraints.horizontal_resolution);
EXPECT_EQ(default_display->height_in_px(), kModeSatisfyingConstraints.vertical_resolution);
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
kModeSatisfyingConstraints.refresh_rate_e2 * 10);
}
TEST(DisplayManager, DisplayModeConstraintsOverriddenByModeIndex) {
static const display::DisplayModeConstraints kDisplayModeConstraints = {
.width_px_range = utils::RangeInclusive(700, 900),
};
static constexpr fuchsia::hardware::display::types::DisplayId kDisplayId = {.value = 1};
static constexpr fuchsia::hardware::display::Mode kModeNotSatisfyingConstraints = {
.horizontal_resolution = 1024,
.vertical_resolution = 768,
.refresh_rate_e2 = 60'00,
};
static constexpr fuchsia::hardware::display::Mode kModeSatisfyingConstraints = {
.horizontal_resolution = 800,
.vertical_resolution = 600,
.refresh_rate_e2 = 30'00,
};
static constexpr fuchsia::hardware::display::Mode kModeOverridden = {
.horizontal_resolution = 1280,
.vertical_resolution = 960,
.refresh_rate_e2 = 30'00,
};
static const std::vector<fuchsia::images2::PixelFormat> kHlcppSupportedPixelFormats = {
fuchsia::images2::PixelFormat::R8G8B8A8,
};
static const std::vector<fuchsia_images2::PixelFormat> kNaturalSupportedPixelFormats = {
fuchsia_images2::PixelFormat::kR8G8B8A8,
};
async::TestLoop loop;
async_set_default_dispatcher(loop.dispatcher());
const fuchsia::hardware::display::Info kDisplayInfo = {
.id = kDisplayId,
.modes =
{
kModeNotSatisfyingConstraints,
kModeSatisfyingConstraints,
kModeOverridden,
},
.pixel_format = kHlcppSupportedPixelFormats,
.manufacturer_name = "manufacturer",
.monitor_name = "model",
.monitor_serial = "0001",
.horizontal_size_mm = 120,
.vertical_size_mm = 100,
.using_fallback_size = false,
};
auto coordinator_channel = CreateCoordinatorEndpoints();
display::test::MockDisplayCoordinator mock_display_coordinator(kDisplayInfo);
mock_display_coordinator.Bind(coordinator_channel.server.TakeChannel());
display::DisplayManager display_manager(/*i_can_haz_display_id=*/std::nullopt,
/*display_mode_index_override=*/std::make_optional(2),
kDisplayModeConstraints,
/*display_available_cb=*/[]() {});
display_manager.BindDefaultDisplayCoordinator(std::move(coordinator_channel.client));
mock_display_coordinator.SendOnDisplayChangedEvent();
EXPECT_TRUE(loop.RunUntilIdle());
const display::Display* default_display = display_manager.default_display();
ASSERT_TRUE(default_display != nullptr);
EXPECT_EQ(default_display->width_in_px(), kModeOverridden.horizontal_resolution);
EXPECT_EQ(default_display->height_in_px(), kModeOverridden.vertical_resolution);
EXPECT_EQ(default_display->maximum_refresh_rate_in_millihertz(),
kModeOverridden.refresh_rate_e2 * 10);
}
} // namespace test
} // namespace gfx
} // namespace scenic_impl