blob: d42148fe4f2ba9f812ad4695efca0f6ad2fa9dba [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/graphics/display/drivers/goldfish-display/display-engine.h"
#include <fidl/fuchsia.hardware.goldfish.pipe/cpp/wire.h>
#include <fidl/fuchsia.hardware.goldfish/cpp/wire.h>
#include <fidl/fuchsia.sysmem2/cpp/wire.h>
#include <fidl/fuchsia.sysmem2/cpp/wire_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/loop.h>
#include <lib/driver/testing/cpp/driver_runtime.h>
#include <lib/driver/testing/cpp/scoped_global_logger.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <gtest/gtest.h>
#include "src/graphics/display/lib/api-protocols/cpp/display-engine-events-fidl.h"
#include "src/graphics/display/lib/api-types/cpp/color-conversion.h"
#include "src/graphics/display/lib/api-types/cpp/config-check-result.h"
#include "src/graphics/display/lib/api-types/cpp/display-id.h"
#include "src/graphics/display/lib/api-types/cpp/driver-buffer-collection-id.h"
#include "src/graphics/display/lib/api-types/cpp/driver-layer.h"
#include "src/graphics/display/lib/api-types/cpp/mode-id.h"
#include "src/lib/testing/predicates/status.h"
namespace goldfish {
namespace {
constexpr int32_t kDisplayWidthPx = 1024;
constexpr int32_t kDisplayHeightPx = 768;
constexpr int32_t kDisplayRefreshRateHz = 60;
constexpr size_t kMaxLayerCount = 3; // This is the max size of layer array.
} // namespace
// TODO(https://fxbug.dev/42072949): Consider creating and using a unified set of sysmem
// testing doubles instead of writing mocks for each display driver test.
class FakeAllocator : public fidl::testing::WireTestBase<fuchsia_sysmem2::Allocator> {
public:
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {}
};
class FakePipe : public fidl::WireServer<fuchsia_hardware_goldfish_pipe::GoldfishPipe> {};
class GoldfishDisplayEngineTest : public testing::Test {
public:
GoldfishDisplayEngineTest() : loop_(&kAsyncLoopConfigNeverAttachToThread) {}
void SetUp() override;
void TearDown() override;
protected:
fdf_testing::ScopedGlobalLogger logger_;
fdf_testing::DriverRuntime driver_runtime_;
fdf::UnownedSynchronizedDispatcher display_event_dispatcher_ =
driver_runtime_.StartBackgroundDispatcher();
display::DisplayEngineEventsFidl engine_events_fidl_;
std::unique_ptr<DisplayEngine> display_engine_;
std::optional<fidl::ServerBindingRef<fuchsia_hardware_goldfish_pipe::GoldfishPipe>> binding_;
std::optional<fidl::ServerBindingRef<fuchsia_sysmem2::Allocator>> allocator_binding_;
async::Loop loop_;
FakePipe* fake_pipe_;
FakeAllocator mock_allocator_;
};
void GoldfishDisplayEngineTest::SetUp() {
auto [control_client, control_server] =
fidl::Endpoints<fuchsia_hardware_goldfish::ControlDevice>::Create();
auto [pipe_client, pipe_server] =
fidl::Endpoints<fuchsia_hardware_goldfish_pipe::GoldfishPipe>::Create();
auto [sysmem_client, sysmem_server] = fidl::Endpoints<fuchsia_sysmem2::Allocator>::Create();
allocator_binding_ =
fidl::BindServer(loop_.dispatcher(), std::move(sysmem_server), &mock_allocator_);
display_engine_ = std::make_unique<DisplayEngine>(
std::move(control_client), std::move(pipe_client), std::move(sysmem_client),
std::make_unique<RenderControl>(), display_event_dispatcher_->async_dispatcher(),
&engine_events_fidl_);
// Call SetupPrimaryDisplayForTesting() so that we can set up the display
// devices without any dependency on proper driver binding.
display_engine_->SetupPrimaryDisplayForTesting(kDisplayWidthPx, kDisplayHeightPx,
kDisplayRefreshRateHz);
}
void GoldfishDisplayEngineTest::TearDown() { allocator_binding_->Unbind(); }
TEST_F(GoldfishDisplayEngineTest, CheckConfigMultiLayer) {
static constexpr display::Rectangle kDisplayArea = {
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}};
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
};
static_assert(std::size(kLayers) == kMaxLayerCount);
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerColor) {
static constexpr display::Rectangle kDisplayArea = {
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}};
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = kDisplayArea,
.image_source = display::Rectangle({.x = 0, .y = 0, .width = 0, .height = 0}),
.image_id = display::kInvalidDriverImageId,
.image_metadata = display::ImageMetadata(
{.width = 0, .height = 0, .tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerPrimary) {
static constexpr display::Rectangle kDisplayArea = {
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}};
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kOk, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerDestFrame) {
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = display::Rectangle(
{.x = 0, .y = 0, .width = kDisplayHeightPx, .height = kDisplayHeightPx}),
.image_source = display::Rectangle(
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}),
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerSrcFrame) {
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = display::Rectangle(
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}),
.image_source = display::Rectangle(
{.x = 0, .y = 0, .width = kDisplayHeightPx, .height = kDisplayHeightPx}),
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerAlpha) {
static constexpr display::Rectangle kDisplayArea = {
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}};
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kHwMultiply,
.image_source_transformation = display::CoordinateTransformation::kIdentity,
}},
};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerTransform) {
static constexpr display::Rectangle kDisplayArea = {
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}};
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kReflectX,
}},
};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), display::ColorConversion::kIdentity, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
}
TEST_F(GoldfishDisplayEngineTest, CheckConfigLayerColorConversionSupported) {
static constexpr display::Rectangle kDisplayArea = {
{.x = 0, .y = 0, .width = kDisplayWidthPx, .height = kDisplayHeightPx}};
static constexpr display::DriverLayer kLayers[] = {
{{
.display_destination = kDisplayArea,
.image_source = kDisplayArea,
.image_id = display::kInvalidDriverImageId,
.image_metadata =
display::ImageMetadata({.width = kDisplayWidthPx,
.height = kDisplayHeightPx,
.tiling_type = display::ImageTilingType::kLinear}),
.fallback_color = display::Color({.format = display::PixelFormat::kB8G8R8A8,
.bytes = {{0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0}}}),
.alpha_mode = display::AlphaMode::kDisable,
.image_source_transformation = display::CoordinateTransformation::kReflectX,
}},
};
const display::ColorConversion kColorConversion = {{
.preoffsets = {0.1f, 0.2f, 0.3f},
.coefficients =
{
std::array<float, 3>{1.0f, 2.0f, 3.0f},
std::array<float, 3>{4.0f, 5.0f, 6.0f},
std::array<float, 3>{7.0f, 8.0f, 9.0f},
},
.postoffsets = {0.4f, 0.5f, 0.6f},
}};
display::ConfigCheckResult res = display_engine_->CheckConfiguration(
display::DisplayId(1), display::ModeId(1), kColorConversion, kLayers);
EXPECT_EQ(display::ConfigCheckResult::kUnsupportedConfig, res);
// TODO(https://fxbug.dev/435550805): For now, driver will pretend it
// supports color conversion.
}
TEST_F(GoldfishDisplayEngineTest, ImportBufferCollection) {
zx::result token1_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token1_endpoints.is_ok());
zx::result token2_endpoints = fidl::CreateEndpoints<fuchsia_sysmem2::BufferCollectionToken>();
ASSERT_TRUE(token2_endpoints.is_ok());
// Test ImportBufferCollection().
static constexpr display::DriverBufferCollectionId kValidCollectionId(1);
EXPECT_OK(display_engine_->ImportBufferCollection(kValidCollectionId,
std::move(token1_endpoints->client)));
// `collection_id` must be unused.
EXPECT_EQ(display_engine_
->ImportBufferCollection(kValidCollectionId, std::move(token2_endpoints->client))
.status_value(),
ZX_ERR_ALREADY_EXISTS);
// Test ReleaseBufferCollection().
static constexpr display::DriverBufferCollectionId kInvalidCollectionId(2);
EXPECT_EQ(display_engine_->ReleaseBufferCollection(kInvalidCollectionId).status_value(),
ZX_ERR_NOT_FOUND);
EXPECT_OK(display_engine_->ReleaseBufferCollection(kValidCollectionId));
loop_.Shutdown();
}
// TODO(https://fxbug.dev/42073664): Implement a fake sysmem and a fake goldfish-pipe
// driver to test importing images using ImportImage().
} // namespace goldfish