Loading services/surfaceflinger/tests/end2end/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ cc_test { ], srcs: [ "main.cpp", "test_framework/core/EdidBuilder.cpp", "test_framework/core/TestService.cpp", "test_framework/fake_hwc3/Hwc3Composer.cpp", "test_framework/fake_hwc3/Hwc3Controller.cpp", Loading @@ -39,6 +41,7 @@ cc_test { // Internal tests "tests/internal/AsyncFunction_test.cpp", "tests/internal/EdidBuilder_test.cpp", // SurfaceFlinger tests "tests/Placeholder_test.cpp", Loading services/surfaceflinger/tests/end2end/test_framework/core/EdidBuilder.cpp 0 → 100644 +736 −0 File added.Preview size limit exceeded, changes collapsed. Show changes services/surfaceflinger/tests/end2end/test_framework/core/EdidBuilder.h 0 → 100644 +240 −0 Original line number Diff line number Diff line /* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <array> #include <cstddef> #include <cstdint> #include <string_view> #include <ui/Size.h> namespace android::surfaceflinger::tests::end2end::test_framework::core { /** * Builds a 128 byte binary EDID 1.4 blob from a more human readable set of arguments. * * Note: Definitions here are based on the "E-EDID A.2_revised_2020.pdf", dated December 31, 2020, * as obtained from the download link for this and a few other public standards at * https://vesa.org/vesa-standards/. * * The introduction page for the revised version just notes some minor changes from the original * "Release A, Revision 2" document from September 25, 2006 (which can be found from other * unofficial sources as "ESA-EEDID-A2.pdf"), namely: * * - Rewording the language used in one sentence in section 2.2.4 about VESA maintaining the list * of extension block tag numbers. * - Updating contact information. */ struct EdidBuilder final { static constexpr size_t kEdidByteCount = 128; static constexpr size_t kMaxProductNameStringLength = 13; using EdidBytes = std::array<uint8_t, kEdidByteCount>; // A prebuilt EDID useful as a default value. static const EdidBytes kDefaultEdid; public: // Ref: "3.4 Vendor and Product Id: 10 Bytes" struct Vendor final { static constexpr uint8_t kYearIsModelYear = 255; static constexpr int16_t kDefaultYear = 2025; std::array<char, 3> manufacturerId = {'G', 'G', 'L'}; uint16_t manufacturerProductCode = 1; uint32_t manufacturerSerialNumber = 1; // 0 = unused, 1...54 = week of the year, 255 = Year is model year. uint8_t manufacturedWeek = kYearIsModelYear; // 1990...2245 int16_t manufacturedYear = kDefaultYear; }; enum class ColorBitDepth : uint8_t { Undefined = 0, Six = 1, Eight = 2, Ten = 3, Twelve = 4, Fourteen = 5, Sixteen = 6, }; enum class DigitalInterface : uint8_t { Undefined = 0, DVI = 1, HDMIa = 2, HDMIb = 3, MDDI = 4, DisplayPort = 5, }; enum class Supported : uint8_t { No = 0, Yes = 1, }; enum class DigitalSupportedColorEncodingFormats : uint8_t { RGB444 = 0, RGB444PlusYCrCb444 = 1, RGB444PlusYCrCb422 = 2, RGB444PlusYCrCb444PlusYCrCb422 = 3, }; // Ref: "3.5 EDID Struct Version & Revision: 2 Bytes" struct EdidVersion final { uint8_t version; uint8_t revision; }; // Ref: "3.6.4 Feature Support: 1 Byte", // Note: Variant of the structure for a digital display struct DigitalDisplayFeatureSupport final { Supported standby = Supported::No; Supported suspend = Supported::No; Supported activeOff = Supported::No; DigitalSupportedColorEncodingFormats digitalFormats = DigitalSupportedColorEncodingFormats::RGB444; Supported srgb = Supported::No; Supported preferredIsNative = Supported::Yes; Supported continuousFrequency = Supported::No; }; // Ref: "3.6 Basic Display Parameters and Features: 5 Bytes" // Note: Variant of the structure for a digital display struct BasicDigitalDisplayParametersAndFeatures final { static constexpr auto kDefaultGamma = 2.3F; static constexpr auto kDefaultHorizontalSizeCm = 64; static constexpr auto kDefaultVerticalSizeCm = 36; ColorBitDepth depth = ColorBitDepth::Ten; DigitalInterface interface = DigitalInterface::Undefined; uint8_t horizontalSizeCm = kDefaultHorizontalSizeCm; uint8_t verticalSizeCm = kDefaultVerticalSizeCm; float gamma = kDefaultGamma; DigitalDisplayFeatureSupport features; }; enum class SignalInterlace : uint8_t { NotInterlaced = 0, Interlaced = 1, }; enum class StereoMode : uint8_t { NoStereo = 0b000, // Low bit is a don't care. NoStereoAlt = 0b001, // FieldSequentialRight = 0b010, FieldSequentialLeft = 0b100, Interleaved2WayRight = 0b011, Interleaved2WayLeft = 0b101, Interleaved4Way = 0b110, SideBySide = 0b111, }; enum class SyncPolarity : uint8_t { Negative = 0, Positive = 1, }; // Ref: "3.10.2 Detailed Timing Descriptor: 18 bytes" // Note: Variant of the structure for a digital display struct DigitalSeparateDetailedTimingDescriptor final { struct Timing { struct InnerTiming { uint16_t addressable = 0; uint8_t border = 0; uint16_t frontPorch = 0; uint16_t syncPulse = 0; uint16_t backPorch = 0; SyncPolarity syncPolarity = SyncPolarity::Positive; }; uint32_t pixelClockMhz = 0; InnerTiming horizontal; InnerTiming vertical; // This function synthesizes a valid DTD for a given pixel size and refresh rate, for // use with injecting fake/simulated displays with vkms. // // IMPORTANT: This is not part of the EDID standard, and further is not intended to be // used to generate the timing values for a real display. static auto synthesize(ui::Size displayed, int refreshRate) -> Timing; }; // These timing values are the standard timings for a 1920x1080 panel running at 60Hz. static constexpr Timing k1920x1080x60HzStandard = { .pixelClockMhz = 148'500'000, // 148.5 Mhz .horizontal = { .addressable = 1920, .border = 0, .frontPorch = 88, .syncPulse = 44, .backPorch = 148, .syncPolarity = SyncPolarity::Positive, }, .vertical = { .addressable = 1080, .border = 0, .frontPorch = 4, .syncPulse = 5, .backPorch = 36, .syncPolarity = SyncPolarity::Positive, }, }; static constexpr uint16_t kDefaultHorizontalImageSizeMm = 640; static constexpr uint16_t kDefaultVerticalImageSizeMm = 360; Timing timing = k1920x1080x60HzStandard; uint16_t horizontalImageSizeMm = kDefaultHorizontalImageSizeMm; uint16_t verticalImageSizeMm = kDefaultVerticalImageSizeMm; SignalInterlace signalInterlace = SignalInterlace::NotInterlaced; StereoMode stereoMode = StereoMode::NoStereo; }; // The required portion of the base 1.4 EDID, lacking only the three optional descriptors. struct Version1r4Required { Vendor vendor; BasicDigitalDisplayParametersAndFeatures parametersAndFeatures; DigitalSeparateDetailedTimingDescriptor preferred; }; EdidBuilder(); // NOLINTBEGIN(readability-convert-member-functions-to-static) // clang-tidy incorrectly shows this error for C++23 "deducing this" methods. auto set(this EdidBuilder&& self, const Version1r4Required& values) -> EdidBuilder&&; // Note: The string value is truncated if more than kMaxProductNameStringLength characters are // given. auto addDisplayProductNameStringDescriptor(this EdidBuilder&& self, std::string_view value) -> EdidBuilder&&; auto build(this EdidBuilder&& self) -> EdidBytes; // NOLINTEND(readability-convert-member-functions-to-static) private: EdidBytes edid; // Descriptor1 must be a Detailed Timing Descriptor. Any additional descriptors start after. size_t nextDescriptorOffset; }; } // namespace android::surfaceflinger::tests::end2end::test_framework::core services/surfaceflinger/tests/end2end/tests/internal/EdidBuilder_test.cpp 0 → 100644 +188 −0 Original line number Diff line number Diff line /* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <algorithm> #include <array> #include <iterator> #include <string_view> #include <fmt/format.h> #include <gtest/gtest.h> #include <ui/DisplayIdentification.h> #include <ui/Size.h> #include "test_framework/core/EdidBuilder.h" namespace android::surfaceflinger::tests::end2end { namespace { using namespace std::string_view_literals; using EdidBuilder = end2end::test_framework::core::EdidBuilder; TEST(EdidBuilderSynthesizeTiming, for1920x1080x60Hz) { using Timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing; const auto result = Timing::synthesize({1920, 1080}, 60); EXPECT_EQ(result.pixelClockMhz, 162'000'000); EXPECT_EQ(result.horizontal.addressable, 1920); EXPECT_EQ(result.horizontal.border, 0); EXPECT_EQ(result.horizontal.frontPorch, 88); EXPECT_EQ(result.horizontal.syncPulse, 44); EXPECT_EQ(result.horizontal.backPorch, 348); EXPECT_EQ(result.horizontal.syncPolarity, EdidBuilder::SyncPolarity::Positive); EXPECT_EQ(result.vertical.addressable, 1080); EXPECT_EQ(result.vertical.border, 0); EXPECT_EQ(result.vertical.frontPorch, 4); EXPECT_EQ(result.vertical.syncPulse, 5); EXPECT_EQ(result.vertical.backPorch, 36); EXPECT_EQ(result.vertical.syncPolarity, EdidBuilder::SyncPolarity::Positive); } TEST(EdidBuilderSynthesizeTiming, for1920x1080x144Hz) { using Timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing; const auto result = Timing::synthesize({1920, 1080}, 144); EXPECT_EQ(result.pixelClockMhz, 388'800'000); EXPECT_EQ(result.horizontal.addressable, 1920); EXPECT_EQ(result.horizontal.border, 0); EXPECT_EQ(result.horizontal.frontPorch, 88); EXPECT_EQ(result.horizontal.syncPulse, 44); EXPECT_EQ(result.horizontal.backPorch, 348); EXPECT_EQ(result.horizontal.syncPolarity, EdidBuilder::SyncPolarity::Positive); EXPECT_EQ(result.vertical.addressable, 1080); EXPECT_EQ(result.vertical.border, 0); EXPECT_EQ(result.vertical.frontPorch, 4); EXPECT_EQ(result.vertical.syncPulse, 5); EXPECT_EQ(result.vertical.backPorch, 36); EXPECT_EQ(result.vertical.syncPolarity, EdidBuilder::SyncPolarity::Positive); } TEST(EdidBuilderSynthesizeTiming, for3840x2160x60Hz) { using Timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing; const auto result = Timing::synthesize({3840, 2160}, 60); EXPECT_EQ(result.pixelClockMhz, 580'800'000); EXPECT_EQ(result.horizontal.addressable, 3840); EXPECT_EQ(result.horizontal.border, 0); EXPECT_EQ(result.horizontal.frontPorch, 88); EXPECT_EQ(result.horizontal.syncPulse, 44); EXPECT_EQ(result.horizontal.backPorch, 428); EXPECT_EQ(result.horizontal.syncPolarity, EdidBuilder::SyncPolarity::Positive); EXPECT_EQ(result.vertical.addressable, 2160); EXPECT_EQ(result.vertical.border, 0); EXPECT_EQ(result.vertical.frontPorch, 4); EXPECT_EQ(result.vertical.syncPulse, 5); EXPECT_EQ(result.vertical.backPorch, 31); EXPECT_EQ(result.vertical.syncPolarity, EdidBuilder::SyncPolarity::Positive); } TEST(EdidBuilder, DefaultPrebuiltMatchesDefaultWhenBuilt) { const auto prebuilt = EdidBuilder::kDefaultEdid; const auto built = EdidBuilder().set(EdidBuilder::Version1r4Required{}).build(); const auto result = std::ranges::mismatch(prebuilt, built); const auto prebuiltIndex = std::distance(prebuilt.begin(), result.in1); const auto builtIndex = std::distance(built.begin(), result.in2); // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index) EXPECT_EQ(prebuilt, built) << fmt::format( "Difference at prebuilt index {} with byte 0x{:x} vs built index {} with byte 0x{:x} ", prebuiltIndex, prebuiltIndex < prebuilt.size() ? prebuilt[prebuiltIndex] : 0, builtIndex, builtIndex < built.size() ? built[builtIndex] : 0); // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index) } TEST(EdidBuilder, ParsedDefault) { constexpr auto kProductName = "test-product"sv; const auto built = EdidBuilder() .set(EdidBuilder::Version1r4Required{}) .addDisplayProductNameStringDescriptor(kProductName) .build(); const DisplayIdentificationData edid{built.begin(), built.end()}; EXPECT_TRUE(isEdid(edid)); const auto parsed = parseEdid(edid); ASSERT_TRUE(parsed); EXPECT_EQ(parsed->productId, EdidBuilder::Vendor{}.manufacturerProductCode); EXPECT_EQ(std::string_view(parsed->pnpId.data(), 3), std::string_view(EdidBuilder::Vendor{}.manufacturerId.data(), 3)); EXPECT_EQ(parsed->displayName, kProductName); EXPECT_EQ( parsed->physicalSizeInCm, ui::Size( EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultHorizontalSizeCm, EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultVerticalSizeCm)); ASSERT_TRUE(parsed->preferredDetailedTimingDescriptor); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->pixelSizeCount, ui::Size(EdidBuilder::DigitalSeparateDetailedTimingDescriptor::k1920x1080x60HzStandard .horizontal.addressable, EdidBuilder::DigitalSeparateDetailedTimingDescriptor::k1920x1080x60HzStandard .vertical.addressable)); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->physicalSizeInMm, ui::Size(EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultHorizontalImageSizeMm, EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultVerticalImageSizeMm)); } TEST(EdidBuilder, Parsed1080p144hz) { constexpr auto kProductName = "test-gaming"sv; const auto timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing::synthesize( {1920, 1080}, 144); const auto built = EdidBuilder() .set(EdidBuilder::Version1r4Required{ .preferred = { .timing = timing, }, }) .addDisplayProductNameStringDescriptor(kProductName) .build(); const DisplayIdentificationData edid{built.begin(), built.end()}; EXPECT_TRUE(isEdid(edid)); const auto parsed = parseEdid(edid); ASSERT_TRUE(parsed); EXPECT_EQ(parsed->productId, EdidBuilder::Vendor{}.manufacturerProductCode); EXPECT_EQ(std::string_view(parsed->pnpId.data(), 3), std::string_view(EdidBuilder::Vendor{}.manufacturerId.data(), 3)); EXPECT_EQ(parsed->displayName, kProductName); EXPECT_EQ( parsed->physicalSizeInCm, ui::Size( EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultHorizontalSizeCm, EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultVerticalSizeCm)); ASSERT_TRUE(parsed->preferredDetailedTimingDescriptor); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->pixelSizeCount, ui::Size(1920, 1080)); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->physicalSizeInMm, ui::Size(EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultHorizontalImageSizeMm, EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultVerticalImageSizeMm)); } } // namespace } // namespace android::surfaceflinger::tests::end2end Loading
services/surfaceflinger/tests/end2end/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ cc_test { ], srcs: [ "main.cpp", "test_framework/core/EdidBuilder.cpp", "test_framework/core/TestService.cpp", "test_framework/fake_hwc3/Hwc3Composer.cpp", "test_framework/fake_hwc3/Hwc3Controller.cpp", Loading @@ -39,6 +41,7 @@ cc_test { // Internal tests "tests/internal/AsyncFunction_test.cpp", "tests/internal/EdidBuilder_test.cpp", // SurfaceFlinger tests "tests/Placeholder_test.cpp", Loading
services/surfaceflinger/tests/end2end/test_framework/core/EdidBuilder.cpp 0 → 100644 +736 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
services/surfaceflinger/tests/end2end/test_framework/core/EdidBuilder.h 0 → 100644 +240 −0 Original line number Diff line number Diff line /* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <array> #include <cstddef> #include <cstdint> #include <string_view> #include <ui/Size.h> namespace android::surfaceflinger::tests::end2end::test_framework::core { /** * Builds a 128 byte binary EDID 1.4 blob from a more human readable set of arguments. * * Note: Definitions here are based on the "E-EDID A.2_revised_2020.pdf", dated December 31, 2020, * as obtained from the download link for this and a few other public standards at * https://vesa.org/vesa-standards/. * * The introduction page for the revised version just notes some minor changes from the original * "Release A, Revision 2" document from September 25, 2006 (which can be found from other * unofficial sources as "ESA-EEDID-A2.pdf"), namely: * * - Rewording the language used in one sentence in section 2.2.4 about VESA maintaining the list * of extension block tag numbers. * - Updating contact information. */ struct EdidBuilder final { static constexpr size_t kEdidByteCount = 128; static constexpr size_t kMaxProductNameStringLength = 13; using EdidBytes = std::array<uint8_t, kEdidByteCount>; // A prebuilt EDID useful as a default value. static const EdidBytes kDefaultEdid; public: // Ref: "3.4 Vendor and Product Id: 10 Bytes" struct Vendor final { static constexpr uint8_t kYearIsModelYear = 255; static constexpr int16_t kDefaultYear = 2025; std::array<char, 3> manufacturerId = {'G', 'G', 'L'}; uint16_t manufacturerProductCode = 1; uint32_t manufacturerSerialNumber = 1; // 0 = unused, 1...54 = week of the year, 255 = Year is model year. uint8_t manufacturedWeek = kYearIsModelYear; // 1990...2245 int16_t manufacturedYear = kDefaultYear; }; enum class ColorBitDepth : uint8_t { Undefined = 0, Six = 1, Eight = 2, Ten = 3, Twelve = 4, Fourteen = 5, Sixteen = 6, }; enum class DigitalInterface : uint8_t { Undefined = 0, DVI = 1, HDMIa = 2, HDMIb = 3, MDDI = 4, DisplayPort = 5, }; enum class Supported : uint8_t { No = 0, Yes = 1, }; enum class DigitalSupportedColorEncodingFormats : uint8_t { RGB444 = 0, RGB444PlusYCrCb444 = 1, RGB444PlusYCrCb422 = 2, RGB444PlusYCrCb444PlusYCrCb422 = 3, }; // Ref: "3.5 EDID Struct Version & Revision: 2 Bytes" struct EdidVersion final { uint8_t version; uint8_t revision; }; // Ref: "3.6.4 Feature Support: 1 Byte", // Note: Variant of the structure for a digital display struct DigitalDisplayFeatureSupport final { Supported standby = Supported::No; Supported suspend = Supported::No; Supported activeOff = Supported::No; DigitalSupportedColorEncodingFormats digitalFormats = DigitalSupportedColorEncodingFormats::RGB444; Supported srgb = Supported::No; Supported preferredIsNative = Supported::Yes; Supported continuousFrequency = Supported::No; }; // Ref: "3.6 Basic Display Parameters and Features: 5 Bytes" // Note: Variant of the structure for a digital display struct BasicDigitalDisplayParametersAndFeatures final { static constexpr auto kDefaultGamma = 2.3F; static constexpr auto kDefaultHorizontalSizeCm = 64; static constexpr auto kDefaultVerticalSizeCm = 36; ColorBitDepth depth = ColorBitDepth::Ten; DigitalInterface interface = DigitalInterface::Undefined; uint8_t horizontalSizeCm = kDefaultHorizontalSizeCm; uint8_t verticalSizeCm = kDefaultVerticalSizeCm; float gamma = kDefaultGamma; DigitalDisplayFeatureSupport features; }; enum class SignalInterlace : uint8_t { NotInterlaced = 0, Interlaced = 1, }; enum class StereoMode : uint8_t { NoStereo = 0b000, // Low bit is a don't care. NoStereoAlt = 0b001, // FieldSequentialRight = 0b010, FieldSequentialLeft = 0b100, Interleaved2WayRight = 0b011, Interleaved2WayLeft = 0b101, Interleaved4Way = 0b110, SideBySide = 0b111, }; enum class SyncPolarity : uint8_t { Negative = 0, Positive = 1, }; // Ref: "3.10.2 Detailed Timing Descriptor: 18 bytes" // Note: Variant of the structure for a digital display struct DigitalSeparateDetailedTimingDescriptor final { struct Timing { struct InnerTiming { uint16_t addressable = 0; uint8_t border = 0; uint16_t frontPorch = 0; uint16_t syncPulse = 0; uint16_t backPorch = 0; SyncPolarity syncPolarity = SyncPolarity::Positive; }; uint32_t pixelClockMhz = 0; InnerTiming horizontal; InnerTiming vertical; // This function synthesizes a valid DTD for a given pixel size and refresh rate, for // use with injecting fake/simulated displays with vkms. // // IMPORTANT: This is not part of the EDID standard, and further is not intended to be // used to generate the timing values for a real display. static auto synthesize(ui::Size displayed, int refreshRate) -> Timing; }; // These timing values are the standard timings for a 1920x1080 panel running at 60Hz. static constexpr Timing k1920x1080x60HzStandard = { .pixelClockMhz = 148'500'000, // 148.5 Mhz .horizontal = { .addressable = 1920, .border = 0, .frontPorch = 88, .syncPulse = 44, .backPorch = 148, .syncPolarity = SyncPolarity::Positive, }, .vertical = { .addressable = 1080, .border = 0, .frontPorch = 4, .syncPulse = 5, .backPorch = 36, .syncPolarity = SyncPolarity::Positive, }, }; static constexpr uint16_t kDefaultHorizontalImageSizeMm = 640; static constexpr uint16_t kDefaultVerticalImageSizeMm = 360; Timing timing = k1920x1080x60HzStandard; uint16_t horizontalImageSizeMm = kDefaultHorizontalImageSizeMm; uint16_t verticalImageSizeMm = kDefaultVerticalImageSizeMm; SignalInterlace signalInterlace = SignalInterlace::NotInterlaced; StereoMode stereoMode = StereoMode::NoStereo; }; // The required portion of the base 1.4 EDID, lacking only the three optional descriptors. struct Version1r4Required { Vendor vendor; BasicDigitalDisplayParametersAndFeatures parametersAndFeatures; DigitalSeparateDetailedTimingDescriptor preferred; }; EdidBuilder(); // NOLINTBEGIN(readability-convert-member-functions-to-static) // clang-tidy incorrectly shows this error for C++23 "deducing this" methods. auto set(this EdidBuilder&& self, const Version1r4Required& values) -> EdidBuilder&&; // Note: The string value is truncated if more than kMaxProductNameStringLength characters are // given. auto addDisplayProductNameStringDescriptor(this EdidBuilder&& self, std::string_view value) -> EdidBuilder&&; auto build(this EdidBuilder&& self) -> EdidBytes; // NOLINTEND(readability-convert-member-functions-to-static) private: EdidBytes edid; // Descriptor1 must be a Detailed Timing Descriptor. Any additional descriptors start after. size_t nextDescriptorOffset; }; } // namespace android::surfaceflinger::tests::end2end::test_framework::core
services/surfaceflinger/tests/end2end/tests/internal/EdidBuilder_test.cpp 0 → 100644 +188 −0 Original line number Diff line number Diff line /* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <algorithm> #include <array> #include <iterator> #include <string_view> #include <fmt/format.h> #include <gtest/gtest.h> #include <ui/DisplayIdentification.h> #include <ui/Size.h> #include "test_framework/core/EdidBuilder.h" namespace android::surfaceflinger::tests::end2end { namespace { using namespace std::string_view_literals; using EdidBuilder = end2end::test_framework::core::EdidBuilder; TEST(EdidBuilderSynthesizeTiming, for1920x1080x60Hz) { using Timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing; const auto result = Timing::synthesize({1920, 1080}, 60); EXPECT_EQ(result.pixelClockMhz, 162'000'000); EXPECT_EQ(result.horizontal.addressable, 1920); EXPECT_EQ(result.horizontal.border, 0); EXPECT_EQ(result.horizontal.frontPorch, 88); EXPECT_EQ(result.horizontal.syncPulse, 44); EXPECT_EQ(result.horizontal.backPorch, 348); EXPECT_EQ(result.horizontal.syncPolarity, EdidBuilder::SyncPolarity::Positive); EXPECT_EQ(result.vertical.addressable, 1080); EXPECT_EQ(result.vertical.border, 0); EXPECT_EQ(result.vertical.frontPorch, 4); EXPECT_EQ(result.vertical.syncPulse, 5); EXPECT_EQ(result.vertical.backPorch, 36); EXPECT_EQ(result.vertical.syncPolarity, EdidBuilder::SyncPolarity::Positive); } TEST(EdidBuilderSynthesizeTiming, for1920x1080x144Hz) { using Timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing; const auto result = Timing::synthesize({1920, 1080}, 144); EXPECT_EQ(result.pixelClockMhz, 388'800'000); EXPECT_EQ(result.horizontal.addressable, 1920); EXPECT_EQ(result.horizontal.border, 0); EXPECT_EQ(result.horizontal.frontPorch, 88); EXPECT_EQ(result.horizontal.syncPulse, 44); EXPECT_EQ(result.horizontal.backPorch, 348); EXPECT_EQ(result.horizontal.syncPolarity, EdidBuilder::SyncPolarity::Positive); EXPECT_EQ(result.vertical.addressable, 1080); EXPECT_EQ(result.vertical.border, 0); EXPECT_EQ(result.vertical.frontPorch, 4); EXPECT_EQ(result.vertical.syncPulse, 5); EXPECT_EQ(result.vertical.backPorch, 36); EXPECT_EQ(result.vertical.syncPolarity, EdidBuilder::SyncPolarity::Positive); } TEST(EdidBuilderSynthesizeTiming, for3840x2160x60Hz) { using Timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing; const auto result = Timing::synthesize({3840, 2160}, 60); EXPECT_EQ(result.pixelClockMhz, 580'800'000); EXPECT_EQ(result.horizontal.addressable, 3840); EXPECT_EQ(result.horizontal.border, 0); EXPECT_EQ(result.horizontal.frontPorch, 88); EXPECT_EQ(result.horizontal.syncPulse, 44); EXPECT_EQ(result.horizontal.backPorch, 428); EXPECT_EQ(result.horizontal.syncPolarity, EdidBuilder::SyncPolarity::Positive); EXPECT_EQ(result.vertical.addressable, 2160); EXPECT_EQ(result.vertical.border, 0); EXPECT_EQ(result.vertical.frontPorch, 4); EXPECT_EQ(result.vertical.syncPulse, 5); EXPECT_EQ(result.vertical.backPorch, 31); EXPECT_EQ(result.vertical.syncPolarity, EdidBuilder::SyncPolarity::Positive); } TEST(EdidBuilder, DefaultPrebuiltMatchesDefaultWhenBuilt) { const auto prebuilt = EdidBuilder::kDefaultEdid; const auto built = EdidBuilder().set(EdidBuilder::Version1r4Required{}).build(); const auto result = std::ranges::mismatch(prebuilt, built); const auto prebuiltIndex = std::distance(prebuilt.begin(), result.in1); const auto builtIndex = std::distance(built.begin(), result.in2); // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index) EXPECT_EQ(prebuilt, built) << fmt::format( "Difference at prebuilt index {} with byte 0x{:x} vs built index {} with byte 0x{:x} ", prebuiltIndex, prebuiltIndex < prebuilt.size() ? prebuilt[prebuiltIndex] : 0, builtIndex, builtIndex < built.size() ? built[builtIndex] : 0); // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index) } TEST(EdidBuilder, ParsedDefault) { constexpr auto kProductName = "test-product"sv; const auto built = EdidBuilder() .set(EdidBuilder::Version1r4Required{}) .addDisplayProductNameStringDescriptor(kProductName) .build(); const DisplayIdentificationData edid{built.begin(), built.end()}; EXPECT_TRUE(isEdid(edid)); const auto parsed = parseEdid(edid); ASSERT_TRUE(parsed); EXPECT_EQ(parsed->productId, EdidBuilder::Vendor{}.manufacturerProductCode); EXPECT_EQ(std::string_view(parsed->pnpId.data(), 3), std::string_view(EdidBuilder::Vendor{}.manufacturerId.data(), 3)); EXPECT_EQ(parsed->displayName, kProductName); EXPECT_EQ( parsed->physicalSizeInCm, ui::Size( EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultHorizontalSizeCm, EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultVerticalSizeCm)); ASSERT_TRUE(parsed->preferredDetailedTimingDescriptor); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->pixelSizeCount, ui::Size(EdidBuilder::DigitalSeparateDetailedTimingDescriptor::k1920x1080x60HzStandard .horizontal.addressable, EdidBuilder::DigitalSeparateDetailedTimingDescriptor::k1920x1080x60HzStandard .vertical.addressable)); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->physicalSizeInMm, ui::Size(EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultHorizontalImageSizeMm, EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultVerticalImageSizeMm)); } TEST(EdidBuilder, Parsed1080p144hz) { constexpr auto kProductName = "test-gaming"sv; const auto timing = EdidBuilder::DigitalSeparateDetailedTimingDescriptor::Timing::synthesize( {1920, 1080}, 144); const auto built = EdidBuilder() .set(EdidBuilder::Version1r4Required{ .preferred = { .timing = timing, }, }) .addDisplayProductNameStringDescriptor(kProductName) .build(); const DisplayIdentificationData edid{built.begin(), built.end()}; EXPECT_TRUE(isEdid(edid)); const auto parsed = parseEdid(edid); ASSERT_TRUE(parsed); EXPECT_EQ(parsed->productId, EdidBuilder::Vendor{}.manufacturerProductCode); EXPECT_EQ(std::string_view(parsed->pnpId.data(), 3), std::string_view(EdidBuilder::Vendor{}.manufacturerId.data(), 3)); EXPECT_EQ(parsed->displayName, kProductName); EXPECT_EQ( parsed->physicalSizeInCm, ui::Size( EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultHorizontalSizeCm, EdidBuilder::BasicDigitalDisplayParametersAndFeatures::kDefaultVerticalSizeCm)); ASSERT_TRUE(parsed->preferredDetailedTimingDescriptor); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->pixelSizeCount, ui::Size(1920, 1080)); EXPECT_EQ(parsed->preferredDetailedTimingDescriptor->physicalSizeInMm, ui::Size(EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultHorizontalImageSizeMm, EdidBuilder::DigitalSeparateDetailedTimingDescriptor:: kDefaultVerticalImageSizeMm)); } } // namespace } // namespace android::surfaceflinger::tests::end2end