Loading services/surfaceflinger/tests/end2end/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -34,12 +34,18 @@ cc_test { "main.cpp", "test_framework/core/EdidBuilder.cpp", "test_framework/core/TestService.cpp", "test_framework/hwc3/DisplayVSyncEventService.cpp", "test_framework/hwc3/FakeComposer.cpp", "test_framework/hwc3/Hwc3Controller.cpp", "test_framework/hwc3/MultiDisplayRefreshEventGenerator.cpp", "test_framework/hwc3/SingleDisplayRefreshEventGenerator.cpp", "test_framework/hwc3/SingleDisplayRefreshSchedule.cpp", "test_framework/hwc3/TimeKeeperThread.cpp", "test_framework/surfaceflinger/SFController.cpp", // Internal tests "tests/internal/AsyncFunction_test.cpp", "tests/internal/DisplayRefresh_test.cpp", "tests/internal/EdidBuilder_test.cpp", // SurfaceFlinger tests Loading services/surfaceflinger/tests/end2end/test_framework/core/TimeInterval.h 0 → 100644 +51 −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 <chrono> #include <string> #include <fmt/chrono.h> // NOLINT(misc-include-cleaner) #include <fmt/format.h> namespace android::surfaceflinger::tests::end2end::test_framework::core { // Represents a span of time between two absolute points in time. // The interval is half-open, including the beginning point, but not the end point. // Note: "interval" is used to be consistent with the mathematical definition of the term. // NOLINTBEGIN(misc-non-private-member-variables-in-classes) struct TimeInterval final { using TimePoint = std::chrono::steady_clock::time_point; // The half-open interval [begin, end), where the end time is not included. TimePoint begin; TimePoint end; [[nodiscard]] auto empty() const -> bool { return begin == end; } friend auto operator==(const TimeInterval&, const TimeInterval&) -> bool = default; }; // NOLINTEND(misc-non-private-member-variables-in-classes) inline auto toString(const TimeInterval& value) -> std::string { return fmt::format("[{}, {})", value.begin.time_since_epoch(), value.end.time_since_epoch()); } inline auto format_as(const TimeInterval& event) -> std::string { return toString(event); } } // namespace android::surfaceflinger::tests::end2end::test_framework::core services/surfaceflinger/tests/end2end/test_framework/hwc3/DisplayVSyncEventService.cpp 0 → 100644 +106 −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 <memory> #include <string> #include <utility> #include <vector> #include <android-base/expected.h> #include <android-base/logging.h> #include <ftl/ignore.h> #include "test_framework/core/GuardedSharedState.h" #include "test_framework/hwc3/DisplayVSyncEventService.h" #include "test_framework/hwc3/MultiDisplayRefreshEventGenerator.h" #include "test_framework/hwc3/SingleDisplayRefreshSchedule.h" #include "test_framework/hwc3/TimeKeeperThread.h" #include "test_framework/hwc3/events/VSync.h" namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 { struct DisplayVSyncEventService::Passkey final {}; auto DisplayVSyncEventService::make() -> base::expected<std::shared_ptr<DisplayVSyncEventService>, std::string> { using namespace std::string_literals; auto service = std::make_unique<DisplayVSyncEventService>(Passkey{}); if (service == nullptr) { return base::unexpected("Failed to construct the DisplayVSyncEventService"s); } if (auto result = service->init(); !result) { return base::unexpected("Failed to init the DisplayVSyncEventService: "s + result.error()); } return service; } DisplayVSyncEventService::DisplayVSyncEventService(Passkey passkey) { ftl::ignore(passkey); } auto DisplayVSyncEventService::init() -> base::expected<void, std::string> { auto timekeeperMake = TimeKeeperThread::make(); if (!timekeeperMake) { return base::unexpected(std::move(timekeeperMake.error())); } mTimeKeeper = *std::move(timekeeperMake); mTimeKeeper->editCallbacks().onWakeup.set( [this](TimeInterval sleeping) -> TimePoint { return onWakeup(sleeping); })(); return {}; } auto DisplayVSyncEventService::editCallbacks() -> Callbacks& { return mCallbacks; } void DisplayVSyncEventService::addDisplay(DisplayId displayId, SingleDisplayRefreshSchedule schedule) { mGenerator.withExclusiveLock([displayId, schedule](auto& generator) { LOG(VERBOSE) << "addDisplay: " << displayId << " schedule: " << toString(schedule); generator.addDisplay(displayId, schedule); }); mTimeKeeper->wakeNow(); } void DisplayVSyncEventService::removeDisplay(DisplayId displayId) { mGenerator.withExclusiveLock([displayId](auto& generator) { LOG(VERBOSE) << "remove " << displayId; generator.removeDisplay(displayId); }); } auto DisplayVSyncEventService::onWakeup(TimeInterval elapsed) -> TimePoint { // Obtain all the events to emit. auto result = mGenerator.withExclusiveLock( [elapsed](auto& generator) { return generator.generateEventsFor(elapsed); }); // Emit them via the connector interface. LOG(VERBOSE) << "emitting " << result.events.size() << " events"; for (const auto& event : result.events) { LOG(VERBOSE) << "displayId: " << event.displayId << " timestamp: " << event.expectedAt.time_since_epoch() << " period: " << event.expectedPeriod; mCallbacks.onVSync(event); } LOG(VERBOSE) << "next wakeup " << result.nextRefreshAt.time_since_epoch(); return result.nextRefreshAt; } } // namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 services/surfaceflinger/tests/end2end/test_framework/hwc3/DisplayVSyncEventService.h 0 → 100644 +74 −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 <chrono> #include <memory> #include <string> #include <android-base/expected.h> #include "test_framework/core/DisplayConfiguration.h" #include "test_framework/core/GuardedSharedState.h" #include "test_framework/core/TimeInterval.h" #include "test_framework/hwc3/MultiDisplayRefreshEventGenerator.h" #include "test_framework/hwc3/SingleDisplayRefreshSchedule.h" #include "test_framework/hwc3/events/VSync.h" namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 { class TimeKeeperThread; class DisplayVSyncEventService final { struct Passkey; // Uses the passkey idiom to restrict construction. public: struct Callbacks final { events::VSync::AsyncConnector onVSync; }; using DisplayId = core::DisplayConfiguration::Id; [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<DisplayVSyncEventService>, std::string>; explicit DisplayVSyncEventService(Passkey passkey); [[nodiscard]] auto editCallbacks() -> Callbacks&; void addDisplay(DisplayId displayId, SingleDisplayRefreshSchedule schedule); void removeDisplay(DisplayId displayId); private: using TimePoint = std::chrono::steady_clock::time_point; using Duration = std::chrono::steady_clock::duration; using TimeInterval = core::TimeInterval; auto init() -> base::expected<void, std::string>; auto onWakeup(TimeInterval elapsed) -> TimePoint; core::GuardedSharedState<MultiDisplayRefreshEventGenerator> mGenerator; // No mutex needed. Callbacks mCallbacks; // Note: The time keeper thread should be destroyed first, so is declared last. std::unique_ptr<TimeKeeperThread> mTimeKeeper; }; } // namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 services/surfaceflinger/tests/end2end/test_framework/hwc3/FakeComposer.cpp +79 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <initializer_list> #include <memory> #include <optional> #include <ratio> #include <source_location> #include <string> #include <string_view> Loading Loading @@ -58,10 +59,13 @@ #include "test_framework/core/DisplayConfiguration.h" #include "test_framework/core/EdidBuilder.h" #include "test_framework/core/GuardedSharedState.h" #include "test_framework/hwc3/DisplayVSyncEventService.h" #include "test_framework/hwc3/FakeComposer.h" #include "test_framework/hwc3/FakeDisplayConfiguration.h" #include "test_framework/hwc3/SingleDisplayRefreshSchedule.h" #include "test_framework/hwc3/delegators/Composer.h" #include "test_framework/hwc3/delegators/ComposerClient.h" #include "test_framework/hwc3/events/VSync.h" namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 { Loading Loading @@ -248,6 +252,16 @@ struct ClientImpl final : public DefaultComposerClientImpl { }); } void sendVSync(events::VSync event) { mState.withSharedLock([event](const auto& state) { state.composerCallbacks->onVsync( event.displayId, event.expectedAt.time_since_epoch().count(), std::chrono::duration_cast<std::chrono::duration<int32_t, std::nano>>( event.expectedPeriod) .count()); }); } // begin IComposerClient overrides // NOLINTBEGIN(bugprone-easily-swappable-parameters) Loading Loading @@ -569,6 +583,19 @@ struct ClientImpl final : public DefaultComposerClientImpl { }; struct FakeComposer::ComposerImpl final : public DefaultComposerImpl { auto init() -> base::expected<void, std::string> { auto displayVsyncEventServiceResult = DisplayVSyncEventService::make(); if (!displayVsyncEventServiceResult) { return base::unexpected(std::move(displayVsyncEventServiceResult).error()); } mDisplayVSyncEventService = *std::move(displayVsyncEventServiceResult); mDisplayVSyncEventService->editCallbacks().onVSync.set( [this](auto event) { onVsyncEventGenerated(event); })(); return {}; } auto getComposer() -> std::shared_ptr<IComposer> { return ref<ComposerImpl>(); } void addDisplay(const core::DisplayConfiguration& display) { Loading Loading @@ -603,6 +630,8 @@ struct FakeComposer::ComposerImpl final : public DefaultComposerImpl { } void removeDisplay(core::DisplayConfiguration::Id displayId) { mDisplayVSyncEventService->removeDisplay(displayId); auto client = mState.withExclusiveLock([displayId](auto& state) { if (state.initialDisplayId == displayId) { state.initialDisplayId.reset(); Loading Loading @@ -687,6 +716,51 @@ struct FakeComposer::ComposerImpl final : public DefaultComposerImpl { }); } void onVsyncEventGenerated(events::VSync event) { if (auto client = mState.withSharedLock([](const auto& state) { return state.client; })) { client->sendVSync(event); } } void onVsyncEnabledChanged(int64_t displayId, bool enable) const { LOG(VERBOSE) << __func__; mState.withSharedLock([this, displayId, enable](const auto& state) { LOG(INFO) << "displayId: " << displayId << " enable: " << enable; if (!enable) { mDisplayVSyncEventService->removeDisplay(displayId); return; } auto found = state.configuredDisplays.find(displayId); if (found == state.configuredDisplays.end()) { LOG(WARNING) << "No display " << displayId << " to configure vsync generation."; return; } const auto& display = found->second; auto foundConfig = std::ranges::find_if( display.displayConfigs, [configId = display.activeConfig](const auto& config) { return config.configId == configId; }); if (foundConfig == display.displayConfigs.end()) { LOG(WARNING) << "No config " << display.activeConfig << " for display " << displayId << " to configure vsync generation."; return; } const auto& config = *foundConfig; using TimePoint = std::chrono::steady_clock::time_point; using Duration = std::chrono::steady_clock::duration; LOG(WARNING) << "Assuming a base of zero for vsync timing!"; const TimePoint base{Duration(0)}; const auto period = Duration(config.vsyncPeriod); mDisplayVSyncEventService->addDisplay( displayId, SingleDisplayRefreshSchedule{.base = base, .period = period}); }); } std::shared_ptr<DisplayVSyncEventService> mDisplayVSyncEventService; struct State { std::shared_ptr<ClientImpl> client; std::optional<int64_t> initialDisplayId; Loading Loading @@ -725,7 +799,11 @@ auto FakeComposer::init() -> base::expected<void, std::string> { auto impl = ndk::SharedRefBase::make<ComposerImpl>(); if (!impl) { return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s); return base::unexpected("Failed to construct the ComposerImpl instance."s); } if (auto result = impl->init(); !result) { return base::unexpected("Failed to init the ComposerImpl instance: "s + result.error()); } mImpl = std::move(impl); Loading Loading
services/surfaceflinger/tests/end2end/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -34,12 +34,18 @@ cc_test { "main.cpp", "test_framework/core/EdidBuilder.cpp", "test_framework/core/TestService.cpp", "test_framework/hwc3/DisplayVSyncEventService.cpp", "test_framework/hwc3/FakeComposer.cpp", "test_framework/hwc3/Hwc3Controller.cpp", "test_framework/hwc3/MultiDisplayRefreshEventGenerator.cpp", "test_framework/hwc3/SingleDisplayRefreshEventGenerator.cpp", "test_framework/hwc3/SingleDisplayRefreshSchedule.cpp", "test_framework/hwc3/TimeKeeperThread.cpp", "test_framework/surfaceflinger/SFController.cpp", // Internal tests "tests/internal/AsyncFunction_test.cpp", "tests/internal/DisplayRefresh_test.cpp", "tests/internal/EdidBuilder_test.cpp", // SurfaceFlinger tests Loading
services/surfaceflinger/tests/end2end/test_framework/core/TimeInterval.h 0 → 100644 +51 −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 <chrono> #include <string> #include <fmt/chrono.h> // NOLINT(misc-include-cleaner) #include <fmt/format.h> namespace android::surfaceflinger::tests::end2end::test_framework::core { // Represents a span of time between two absolute points in time. // The interval is half-open, including the beginning point, but not the end point. // Note: "interval" is used to be consistent with the mathematical definition of the term. // NOLINTBEGIN(misc-non-private-member-variables-in-classes) struct TimeInterval final { using TimePoint = std::chrono::steady_clock::time_point; // The half-open interval [begin, end), where the end time is not included. TimePoint begin; TimePoint end; [[nodiscard]] auto empty() const -> bool { return begin == end; } friend auto operator==(const TimeInterval&, const TimeInterval&) -> bool = default; }; // NOLINTEND(misc-non-private-member-variables-in-classes) inline auto toString(const TimeInterval& value) -> std::string { return fmt::format("[{}, {})", value.begin.time_since_epoch(), value.end.time_since_epoch()); } inline auto format_as(const TimeInterval& event) -> std::string { return toString(event); } } // namespace android::surfaceflinger::tests::end2end::test_framework::core
services/surfaceflinger/tests/end2end/test_framework/hwc3/DisplayVSyncEventService.cpp 0 → 100644 +106 −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 <memory> #include <string> #include <utility> #include <vector> #include <android-base/expected.h> #include <android-base/logging.h> #include <ftl/ignore.h> #include "test_framework/core/GuardedSharedState.h" #include "test_framework/hwc3/DisplayVSyncEventService.h" #include "test_framework/hwc3/MultiDisplayRefreshEventGenerator.h" #include "test_framework/hwc3/SingleDisplayRefreshSchedule.h" #include "test_framework/hwc3/TimeKeeperThread.h" #include "test_framework/hwc3/events/VSync.h" namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 { struct DisplayVSyncEventService::Passkey final {}; auto DisplayVSyncEventService::make() -> base::expected<std::shared_ptr<DisplayVSyncEventService>, std::string> { using namespace std::string_literals; auto service = std::make_unique<DisplayVSyncEventService>(Passkey{}); if (service == nullptr) { return base::unexpected("Failed to construct the DisplayVSyncEventService"s); } if (auto result = service->init(); !result) { return base::unexpected("Failed to init the DisplayVSyncEventService: "s + result.error()); } return service; } DisplayVSyncEventService::DisplayVSyncEventService(Passkey passkey) { ftl::ignore(passkey); } auto DisplayVSyncEventService::init() -> base::expected<void, std::string> { auto timekeeperMake = TimeKeeperThread::make(); if (!timekeeperMake) { return base::unexpected(std::move(timekeeperMake.error())); } mTimeKeeper = *std::move(timekeeperMake); mTimeKeeper->editCallbacks().onWakeup.set( [this](TimeInterval sleeping) -> TimePoint { return onWakeup(sleeping); })(); return {}; } auto DisplayVSyncEventService::editCallbacks() -> Callbacks& { return mCallbacks; } void DisplayVSyncEventService::addDisplay(DisplayId displayId, SingleDisplayRefreshSchedule schedule) { mGenerator.withExclusiveLock([displayId, schedule](auto& generator) { LOG(VERBOSE) << "addDisplay: " << displayId << " schedule: " << toString(schedule); generator.addDisplay(displayId, schedule); }); mTimeKeeper->wakeNow(); } void DisplayVSyncEventService::removeDisplay(DisplayId displayId) { mGenerator.withExclusiveLock([displayId](auto& generator) { LOG(VERBOSE) << "remove " << displayId; generator.removeDisplay(displayId); }); } auto DisplayVSyncEventService::onWakeup(TimeInterval elapsed) -> TimePoint { // Obtain all the events to emit. auto result = mGenerator.withExclusiveLock( [elapsed](auto& generator) { return generator.generateEventsFor(elapsed); }); // Emit them via the connector interface. LOG(VERBOSE) << "emitting " << result.events.size() << " events"; for (const auto& event : result.events) { LOG(VERBOSE) << "displayId: " << event.displayId << " timestamp: " << event.expectedAt.time_since_epoch() << " period: " << event.expectedPeriod; mCallbacks.onVSync(event); } LOG(VERBOSE) << "next wakeup " << result.nextRefreshAt.time_since_epoch(); return result.nextRefreshAt; } } // namespace android::surfaceflinger::tests::end2end::test_framework::hwc3
services/surfaceflinger/tests/end2end/test_framework/hwc3/DisplayVSyncEventService.h 0 → 100644 +74 −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 <chrono> #include <memory> #include <string> #include <android-base/expected.h> #include "test_framework/core/DisplayConfiguration.h" #include "test_framework/core/GuardedSharedState.h" #include "test_framework/core/TimeInterval.h" #include "test_framework/hwc3/MultiDisplayRefreshEventGenerator.h" #include "test_framework/hwc3/SingleDisplayRefreshSchedule.h" #include "test_framework/hwc3/events/VSync.h" namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 { class TimeKeeperThread; class DisplayVSyncEventService final { struct Passkey; // Uses the passkey idiom to restrict construction. public: struct Callbacks final { events::VSync::AsyncConnector onVSync; }; using DisplayId = core::DisplayConfiguration::Id; [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<DisplayVSyncEventService>, std::string>; explicit DisplayVSyncEventService(Passkey passkey); [[nodiscard]] auto editCallbacks() -> Callbacks&; void addDisplay(DisplayId displayId, SingleDisplayRefreshSchedule schedule); void removeDisplay(DisplayId displayId); private: using TimePoint = std::chrono::steady_clock::time_point; using Duration = std::chrono::steady_clock::duration; using TimeInterval = core::TimeInterval; auto init() -> base::expected<void, std::string>; auto onWakeup(TimeInterval elapsed) -> TimePoint; core::GuardedSharedState<MultiDisplayRefreshEventGenerator> mGenerator; // No mutex needed. Callbacks mCallbacks; // Note: The time keeper thread should be destroyed first, so is declared last. std::unique_ptr<TimeKeeperThread> mTimeKeeper; }; } // namespace android::surfaceflinger::tests::end2end::test_framework::hwc3
services/surfaceflinger/tests/end2end/test_framework/hwc3/FakeComposer.cpp +79 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <initializer_list> #include <memory> #include <optional> #include <ratio> #include <source_location> #include <string> #include <string_view> Loading Loading @@ -58,10 +59,13 @@ #include "test_framework/core/DisplayConfiguration.h" #include "test_framework/core/EdidBuilder.h" #include "test_framework/core/GuardedSharedState.h" #include "test_framework/hwc3/DisplayVSyncEventService.h" #include "test_framework/hwc3/FakeComposer.h" #include "test_framework/hwc3/FakeDisplayConfiguration.h" #include "test_framework/hwc3/SingleDisplayRefreshSchedule.h" #include "test_framework/hwc3/delegators/Composer.h" #include "test_framework/hwc3/delegators/ComposerClient.h" #include "test_framework/hwc3/events/VSync.h" namespace android::surfaceflinger::tests::end2end::test_framework::hwc3 { Loading Loading @@ -248,6 +252,16 @@ struct ClientImpl final : public DefaultComposerClientImpl { }); } void sendVSync(events::VSync event) { mState.withSharedLock([event](const auto& state) { state.composerCallbacks->onVsync( event.displayId, event.expectedAt.time_since_epoch().count(), std::chrono::duration_cast<std::chrono::duration<int32_t, std::nano>>( event.expectedPeriod) .count()); }); } // begin IComposerClient overrides // NOLINTBEGIN(bugprone-easily-swappable-parameters) Loading Loading @@ -569,6 +583,19 @@ struct ClientImpl final : public DefaultComposerClientImpl { }; struct FakeComposer::ComposerImpl final : public DefaultComposerImpl { auto init() -> base::expected<void, std::string> { auto displayVsyncEventServiceResult = DisplayVSyncEventService::make(); if (!displayVsyncEventServiceResult) { return base::unexpected(std::move(displayVsyncEventServiceResult).error()); } mDisplayVSyncEventService = *std::move(displayVsyncEventServiceResult); mDisplayVSyncEventService->editCallbacks().onVSync.set( [this](auto event) { onVsyncEventGenerated(event); })(); return {}; } auto getComposer() -> std::shared_ptr<IComposer> { return ref<ComposerImpl>(); } void addDisplay(const core::DisplayConfiguration& display) { Loading Loading @@ -603,6 +630,8 @@ struct FakeComposer::ComposerImpl final : public DefaultComposerImpl { } void removeDisplay(core::DisplayConfiguration::Id displayId) { mDisplayVSyncEventService->removeDisplay(displayId); auto client = mState.withExclusiveLock([displayId](auto& state) { if (state.initialDisplayId == displayId) { state.initialDisplayId.reset(); Loading Loading @@ -687,6 +716,51 @@ struct FakeComposer::ComposerImpl final : public DefaultComposerImpl { }); } void onVsyncEventGenerated(events::VSync event) { if (auto client = mState.withSharedLock([](const auto& state) { return state.client; })) { client->sendVSync(event); } } void onVsyncEnabledChanged(int64_t displayId, bool enable) const { LOG(VERBOSE) << __func__; mState.withSharedLock([this, displayId, enable](const auto& state) { LOG(INFO) << "displayId: " << displayId << " enable: " << enable; if (!enable) { mDisplayVSyncEventService->removeDisplay(displayId); return; } auto found = state.configuredDisplays.find(displayId); if (found == state.configuredDisplays.end()) { LOG(WARNING) << "No display " << displayId << " to configure vsync generation."; return; } const auto& display = found->second; auto foundConfig = std::ranges::find_if( display.displayConfigs, [configId = display.activeConfig](const auto& config) { return config.configId == configId; }); if (foundConfig == display.displayConfigs.end()) { LOG(WARNING) << "No config " << display.activeConfig << " for display " << displayId << " to configure vsync generation."; return; } const auto& config = *foundConfig; using TimePoint = std::chrono::steady_clock::time_point; using Duration = std::chrono::steady_clock::duration; LOG(WARNING) << "Assuming a base of zero for vsync timing!"; const TimePoint base{Duration(0)}; const auto period = Duration(config.vsyncPeriod); mDisplayVSyncEventService->addDisplay( displayId, SingleDisplayRefreshSchedule{.base = base, .period = period}); }); } std::shared_ptr<DisplayVSyncEventService> mDisplayVSyncEventService; struct State { std::shared_ptr<ClientImpl> client; std::optional<int64_t> initialDisplayId; Loading Loading @@ -725,7 +799,11 @@ auto FakeComposer::init() -> base::expected<void, std::string> { auto impl = ndk::SharedRefBase::make<ComposerImpl>(); if (!impl) { return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s); return base::unexpected("Failed to construct the ComposerImpl instance."s); } if (auto result = impl->init(); !result) { return base::unexpected("Failed to init the ComposerImpl instance: "s + result.error()); } mImpl = std::move(impl); Loading