diff options
278 files changed, 10735 insertions, 10799 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 33dceffa9f..69a1df27b2 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -194,6 +194,8 @@ static const std::string TOMBSTONE_DIR = "/data/tombstones/"; static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_"; static const std::string ANR_DIR = "/data/anr/"; static const std::string ANR_FILE_PREFIX = "anr_"; +static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/"; +static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-"; // TODO: temporary variables and functions used during C++ refactoring @@ -1110,6 +1112,16 @@ static void DumpIpTablesAsRoot() { RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"}); } +static void DumpShutdownCheckpoints() { + const bool shutdown_checkpoints_dumped = AddDumps( + ds.shutdown_checkpoints_.begin(), ds.shutdown_checkpoints_.end(), + "SHUTDOWN CHECKPOINTS", false /* add_to_zip */); + if (!shutdown_checkpoints_dumped) { + printf("*** NO SHUTDOWN CHECKPOINTS to dump in %s\n\n", + SHUTDOWN_CHECKPOINTS_DIR.c_str()); + } +} + static void DumpDynamicPartitionInfo() { if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) { return; @@ -1704,6 +1716,8 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { DoKmsg(); + DumpShutdownCheckpoints(); + DumpIpAddrAndRules(); dump_route_tables(); @@ -1861,6 +1875,8 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { if (!PropertiesHelper::IsDryRun()) { ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX); ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX); + ds.shutdown_checkpoints_ = GetDumpFds( + SHUTDOWN_CHECKPOINTS_DIR, SHUTDOWN_CHECKPOINTS_FILE_PREFIX); } ds.AddDir(RECOVERY_DIR, true); @@ -2915,6 +2931,7 @@ void Dumpstate::Cancel() { } tombstone_data_.clear(); anr_data_.clear(); + shutdown_checkpoints_.clear(); // Instead of shutdown the pool, we delete temporary files directly since // shutdown blocking the call. @@ -3202,6 +3219,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, tombstone_data_.clear(); anr_data_.clear(); + shutdown_checkpoints_.clear(); return (consent_callback_ != nullptr && consent_callback_->getResult() == UserConsentResult::UNAVAILABLE) diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 5f3acfdc5b..9f894b5079 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -514,6 +514,9 @@ class Dumpstate { // List of open ANR dump files. std::vector<DumpData> anr_data_; + // List of open shutdown checkpoint files. + std::vector<DumpData> shutdown_checkpoints_; + // A thread pool to execute dump tasks simultaneously if the parallel run is enabled. std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_; diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index fd879c6c34..1386660eb6 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -48,14 +48,6 @@ cc_binary { } cc_binary { - name: "servicemanager.microdroid", - defaults: ["servicemanager_defaults"], - init_rc: ["servicemanager.microdroid.rc"], - srcs: ["main.cpp"], - bootstrap: true, -} - -cc_binary { name: "servicemanager.recovery", stem: "servicemanager", recovery: true, diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 2ae61b9603..cc038ae0d4 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -136,6 +136,7 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name) updatableViaApex = manifestInstance.updatableViaApex(); return false; // break (libvintf uses opposite convention) }); + if (updatableViaApex.has_value()) return true; // break (found match) return false; // continue }); @@ -154,7 +155,7 @@ static std::vector<std::string> getVintfUpdatableInstances(const std::string& ap manifestInstance.interface() + "/" + manifestInstance.instance(); instances.push_back(aname); } - return false; // continue + return true; // continue (libvintf uses opposite convention) }); return false; // continue }); diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc deleted file mode 100644 index 8819e1e8bf..0000000000 --- a/cmds/servicemanager/servicemanager.microdroid.rc +++ /dev/null @@ -1,8 +0,0 @@ -service servicemanager /system/bin/servicemanager.microdroid - class core - user system - group system readproc - critical - onrestart setprop servicemanager.ready false - onrestart restart apexd - shutdown critical diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index 5d5a75e174..0fd8d8ee2a 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -14,13 +14,15 @@ * limitations under the License. */ +#include <android-base/properties.h> +#include <android-base/strings.h> #include <android/os/BnServiceCallback.h> #include <binder/Binder.h> -#include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <cutils/android_filesystem_config.h> -#include <gtest/gtest.h> #include <gmock/gmock.h> +#include <gtest/gtest.h> #include "Access.h" #include "ServiceManager.h" @@ -75,6 +77,11 @@ static sp<ServiceManager> getPermissiveServiceManager() { return sm; } +static bool isCuttlefish() { + return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""), + "vsoc_"); +} + TEST(AddService, HappyHappy) { auto sm = getPermissiveServiceManager(); EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, @@ -306,6 +313,49 @@ TEST(ListServices, CriticalServices) { EXPECT_THAT(out, ElementsAre("sa")); } +TEST(Vintf, UpdatableViaApex) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::optional<std::string> updatableViaApex; + EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider/internal/0", + &updatableViaApex) + .isOk()); + EXPECT_EQ(std::make_optional<std::string>("com.google.emulated.camera.provider.hal"), + updatableViaApex); +} + +TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::optional<std::string> updatableViaApex; + EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider", + &updatableViaApex) + .isOk()); // missing instance name + EXPECT_EQ(std::nullopt, updatableViaApex); +} + +TEST(Vintf, GetUpdatableNames) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::vector<std::string> names; + EXPECT_TRUE(sm->getUpdatableNames("com.google.emulated.camera.provider.hal", &names).isOk()); + EXPECT_EQ(std::vector< + std::string>{"android.hardware.camera.provider.ICameraProvider/internal/0"}, + names); +} + +TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) { + if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + + auto sm = getPermissiveServiceManager(); + std::vector<std::string> names; + EXPECT_TRUE(sm->getUpdatableNames("non.existing.apex.name", &names).isOk()); + EXPECT_EQ(std::vector<std::string>{}, names); +} + class CallbackHistorian : public BnServiceCallback { Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { registrations.push_back(name); diff --git a/include/android/surface_control_jni.h b/include/android/surface_control_jni.h index a0a1fdb372..840f6e724b 100644 --- a/include/android/surface_control_jni.h +++ b/include/android/surface_control_jni.h @@ -44,7 +44,7 @@ __BEGIN_DECLS * * Available since API level 34. */ -ASurfaceControl* _Nonnull ASurfaceControl_fromSurfaceControl(JNIEnv* _Nonnull env, +ASurfaceControl* _Nonnull ASurfaceControl_fromJava(JNIEnv* _Nonnull env, jobject _Nonnull surfaceControlObj) __INTRODUCED_IN(__ANDROID_API_U__); /** @@ -59,7 +59,7 @@ ASurfaceControl* _Nonnull ASurfaceControl_fromSurfaceControl(JNIEnv* _Nonnull en * * Available since API level 34. */ -ASurfaceTransaction* _Nonnull ASurfaceTransaction_fromTransaction(JNIEnv* _Nonnull env, +ASurfaceTransaction* _Nonnull ASurfaceTransaction_fromJava(JNIEnv* _Nonnull env, jobject _Nonnull transactionObj) __INTRODUCED_IN(__ANDROID_API_U__); __END_DECLS diff --git a/include/ftl/details/mixins.h b/include/ftl/details/mixins.h new file mode 100644 index 0000000000..9ab9e083ae --- /dev/null +++ b/include/ftl/details/mixins.h @@ -0,0 +1,30 @@ +/* + * Copyright 2022 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 + +namespace android::ftl::details { + +template <typename Self, template <typename> class> +class Mixin { + protected: + constexpr Self& self() { return *static_cast<Self*>(this); } + constexpr const Self& self() const { return *static_cast<const Self*>(this); } + + constexpr auto& mut() { return self().value_; } +}; + +} // namespace android::ftl::details diff --git a/include/ftl/enum.h b/include/ftl/enum.h index 82af1d6cf8..075d12bd17 100644 --- a/include/ftl/enum.h +++ b/include/ftl/enum.h @@ -92,7 +92,7 @@ inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; // enum class E { A, B, C }; // static_assert(ftl::to_underlying(E::B) == 1); // -template <typename E> +template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>> constexpr auto to_underlying(E v) { return static_cast<std::underlying_type_t<E>>(v); } diff --git a/include/ftl/mixins.h b/include/ftl/mixins.h new file mode 100644 index 0000000000..0e1d2004a3 --- /dev/null +++ b/include/ftl/mixins.h @@ -0,0 +1,148 @@ +/* + * Copyright 2022 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 <ftl/details/mixins.h> + +namespace android::ftl { + +// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common +// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must +// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic, +// etc.) are enabled through inheritance: +// +// struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> { +// using Constructible::Constructible; +// }; +// +// static_assert(!std::is_default_constructible_v<Id>); +// +// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is +// zero-initialized unless specified: +// +// struct Color : ftl::DefaultConstructible<Color, std::uint8_t>, +// ftl::Equatable<Color>, +// ftl::Orderable<Color> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// static_assert(Color() == Color(0u)); +// static_assert(ftl::to_underlying(Color(-1)) == 255u); +// static_assert(Color(1u) < Color(2u)); +// +// struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>, +// ftl::Equatable<Sequence>, +// ftl::Orderable<Sequence>, +// ftl::Incrementable<Sequence> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// static_assert(Sequence() == Sequence(-1)); +// +// The underlying type need not be a fundamental type: +// +// struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>, +// ftl::Equatable<Timeout>, +// ftl::Addable<Timeout> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// using namespace std::chrono_literals; +// static_assert(Timeout() + Timeout(5s) == Timeout(15s)); +// +template <typename Self, typename T> +struct Constructible { + explicit constexpr Constructible(T value) : value_(value) {} + + explicit constexpr operator const T&() const { return value_; } + + private: + template <typename, template <typename> class> + friend class details::Mixin; + + T value_; +}; + +template <typename Self, typename T, auto kDefault = T{}> +struct DefaultConstructible : Constructible<Self, T> { + using Constructible<Self, T>::Constructible; + constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {} +}; + +// Shorthand for casting a type-safe wrapper to its underlying value. +template <typename Self, typename T> +constexpr const T& to_underlying(const Constructible<Self, T>& c) { + return static_cast<const T&>(c); +} + +// Comparison operators for equality. +template <typename Self> +struct Equatable : details::Mixin<Self, Equatable> { + constexpr bool operator==(const Self& other) const { + return to_underlying(this->self()) == to_underlying(other); + } + + constexpr bool operator!=(const Self& other) const { return !(*this == other); } +}; + +// Comparison operators for ordering. +template <typename Self> +struct Orderable : details::Mixin<Self, Orderable> { + constexpr bool operator<(const Self& other) const { + return to_underlying(this->self()) < to_underlying(other); + } + + constexpr bool operator>(const Self& other) const { return other < this->self(); } + constexpr bool operator>=(const Self& other) const { return !(*this < other); } + constexpr bool operator<=(const Self& other) const { return !(*this > other); } +}; + +// Pre-increment and post-increment operators. +template <typename Self> +struct Incrementable : details::Mixin<Self, Incrementable> { + constexpr Self& operator++() { + ++this->mut(); + return this->self(); + } + + constexpr Self operator++(int) { + const Self tmp = this->self(); + operator++(); + return tmp; + } +}; + +// Additive operators, including incrementing. +template <typename Self> +struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> { + constexpr Self& operator+=(const Self& other) { + this->mut() += to_underlying(other); + return this->self(); + } + + constexpr Self operator+(const Self& other) const { + Self tmp = this->self(); + return tmp += other; + } + + private: + using Base = details::Mixin<Self, Addable>; + using Base::mut; + using Base::self; +}; + +} // namespace android::ftl diff --git a/include/ftl/optional.h b/include/ftl/optional.h index 626507fd8f..7b02bac340 100644 --- a/include/ftl/optional.h +++ b/include/ftl/optional.h @@ -97,6 +97,16 @@ struct Optional final : std::optional<T> { } }; +template <typename T, typename U> +constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) { + return static_cast<std::optional<T>>(lhs) == static_cast<std::optional<U>>(rhs); +} + +template <typename T, typename U> +constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) { + return !(lhs == rhs); +} + // Deduction guides. template <typename T> Optional(T) -> Optional<T>; diff --git a/include/input/Input.h b/include/input/Input.h index dd74a51e5e..d298d817f5 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -209,6 +209,8 @@ std::string inputEventSourceToString(int32_t source); bool isFromSource(uint32_t source, uint32_t test); +bool isStylusToolType(uint32_t toolType); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h index f72a1bdded..f3c201e7c4 100644 --- a/include/input/VelocityControl.h +++ b/include/input/VelocityControl.h @@ -16,10 +16,13 @@ #pragma once +#include <android-base/stringprintf.h> #include <input/Input.h> #include <input/VelocityTracker.h> #include <utils/Timers.h> +using android::base::StringPrintf; + namespace android { /* @@ -69,6 +72,12 @@ struct VelocityControlParameters { scale(scale), lowThreshold(lowThreshold), highThreshold(highThreshold), acceleration(acceleration) { } + + std::string dump() const { + return StringPrintf("scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, " + "acceleration=%0.3f\n", + scale, lowThreshold, highThreshold, acceleration); + } }; /* @@ -78,6 +87,9 @@ class VelocityControl { public: VelocityControl(); + /* Gets the various parameters. */ + VelocityControlParameters& getParameters(); + /* Sets the various parameters. */ void setParameters(const VelocityControlParameters& parameters); diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 11c8e5dcea..77703749a1 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -45,11 +45,11 @@ #define IF_LOG_TRANSACTIONS() if (false) #define IF_LOG_COMMANDS() if (false) -#define LOG_REMOTEREFS(...) +#define LOG_REMOTEREFS(...) #define IF_LOG_REMOTEREFS() if (false) -#define LOG_THREADPOOL(...) -#define LOG_ONEWAY(...) +#define LOG_THREADPOOL(...) +#define LOG_ONEWAY(...) #else @@ -394,14 +394,92 @@ void IPCThreadState::checkContextIsBinderForUse(const char* use) const { // context, so we don't abort } +constexpr uint32_t encodeExplicitIdentity(bool hasExplicitIdentity, pid_t callingPid) { + uint32_t as_unsigned = static_cast<uint32_t>(callingPid); + if (hasExplicitIdentity) { + return as_unsigned | (1 << 30); + } else { + return as_unsigned & ~(1 << 30); + } +} + +constexpr int64_t packCallingIdentity(bool hasExplicitIdentity, uid_t callingUid, + pid_t callingPid) { + // Calling PID is a 32-bit signed integer, but doesn't consume the entire 32 bit space. + // To future-proof this and because we have extra capacity, we decided to also support -1, + // since this constant is used to represent invalid UID in other places of the system. + // Thus, we pack hasExplicitIdentity into the 2nd bit from the left. This allows us to + // preserve the (left-most) bit for the sign while also encoding the value of + // hasExplicitIdentity. + // 32b | 1b | 1b | 30b + // token = [ calling uid | calling pid(sign) | has explicit identity | calling pid(rest) ] + uint64_t token = (static_cast<uint64_t>(callingUid) << 32) | + encodeExplicitIdentity(hasExplicitIdentity, callingPid); + return static_cast<int64_t>(token); +} + +constexpr bool unpackHasExplicitIdentity(int64_t token) { + return static_cast<int32_t>(token) & (1 << 30); +} + +constexpr uid_t unpackCallingUid(int64_t token) { + return static_cast<uid_t>(token >> 32); +} + +constexpr pid_t unpackCallingPid(int64_t token) { + int32_t encodedPid = static_cast<int32_t>(token); + if (encodedPid & (1 << 31)) { + return encodedPid | (1 << 30); + } else { + return encodedPid & ~(1 << 30); + } +} + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, 9999)) == true, + "pack true hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(true, 1000, 9999)) == 1000, "pack true uid"); + +static_assert(unpackCallingPid(packCallingIdentity(true, 1000, 9999)) == 9999, "pack true pid"); + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, 9999)) == false, + "pack false hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(false, 1000, 9999)) == 1000, "pack false uid"); + +static_assert(unpackCallingPid(packCallingIdentity(false, 1000, 9999)) == 9999, "pack false pid"); + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, -1)) == true, + "pack true (negative) hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(true, 1000, -1)) == 1000, + "pack true (negative) uid"); + +static_assert(unpackCallingPid(packCallingIdentity(true, 1000, -1)) == -1, + "pack true (negative) pid"); + +static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, -1)) == false, + "pack false (negative) hasExplicit"); + +static_assert(unpackCallingUid(packCallingIdentity(false, 1000, -1)) == 1000, + "pack false (negative) uid"); + +static_assert(unpackCallingPid(packCallingIdentity(false, 1000, -1)) == -1, + "pack false (negative) pid"); + int64_t IPCThreadState::clearCallingIdentity() { // ignore mCallingSid for legacy reasons - int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; + int64_t token = packCallingIdentity(mHasExplicitIdentity, mCallingUid, mCallingPid); clearCaller(); + mHasExplicitIdentity = true; return token; } +bool IPCThreadState::hasExplicitIdentity() { + return mHasExplicitIdentity; +} + void IPCThreadState::setStrictModePolicy(int32_t policy) { mStrictModePolicy = policy; @@ -474,9 +552,10 @@ ProcessState::CallRestriction IPCThreadState::getCallRestriction() const { void IPCThreadState::restoreCallingIdentity(int64_t token) { - mCallingUid = (int)(token>>32); + mCallingUid = unpackCallingUid(token); mCallingSid = nullptr; // not enough data to restore - mCallingPid = (int)token; + mCallingPid = unpackCallingPid(token); + mHasExplicitIdentity = unpackHasExplicitIdentity(token); } void IPCThreadState::clearCaller() @@ -889,6 +968,7 @@ IPCThreadState::IPCThreadState() mCallRestriction(mProcess->mCallRestriction) { pthread_setspecific(gTLS, this); clearCaller(); + mHasExplicitIdentity = false; mIn.setDataCapacity(256); mOut.setDataCapacity(256); } @@ -1279,6 +1359,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) const pid_t origPid = mCallingPid; const char* origSid = mCallingSid; const uid_t origUid = mCallingUid; + const bool origHasExplicitIdentity = mHasExplicitIdentity; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; const int32_t origWorkSource = mWorkSource; @@ -1292,6 +1373,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = tr.sender_pid; mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx); mCallingUid = tr.sender_euid; + mHasExplicitIdentity = false; mLastTransactionBinderFlags = tr.flags; // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid, @@ -1367,6 +1449,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = origPid; mCallingSid = origSid; mCallingUid = origUid; + mHasExplicitIdentity = origHasExplicitIdentity; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; mWorkSource = origWorkSource; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 07d0a65ae0..ee081c485c 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -614,11 +614,14 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { if (status_t status = readInt32(&fdIndex); status != OK) { return status; } - const auto& oldFd = otherRpcFields->mFds->at(fdIndex); + int oldFd = toRawFd(otherRpcFields->mFds->at(fdIndex)); // To match kernel binder behavior, we always dup, even if the // FD was unowned in the source parcel. - rpcFields->mFds->emplace_back( - base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0))); + int newFd = -1; + if (status_t status = dupFileDescriptor(oldFd, &newFd); status != OK) { + ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status)); + } + rpcFields->mFds->emplace_back(base::unique_fd(newFd)); // Fixup the index in the data. mDataPos = newDataPos + 4; if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) { @@ -966,7 +969,15 @@ bool Parcel::enforceInterface(const char16_t* interface, } } +void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) { + mEnforceNoDataAvail = enforceNoDataAvail; +} + binder::Status Parcel::enforceNoDataAvail() const { + if (!mEnforceNoDataAvail) { + return binder::Status::ok(); + } + const auto n = dataAvail(); if (n == 0) { return binder::Status::ok(); @@ -3077,6 +3088,7 @@ void Parcel::initState() mAllowFds = true; mDeallocZero = false; mOwner = nullptr; + mEnforceNoDataAvail = true; } void Parcel::scanForFds() const { diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 399667d02b..0820cd1d5c 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -531,56 +531,35 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str()); LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server."); - RpcTransportFd transportFd(unique_fd(TEMP_FAILURE_RETRY( - socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)))); - if (!transportFd.fd.ok()) { + unique_fd socket_fd(TEMP_FAILURE_RETRY( + socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + if (!socket_fd.ok()) { int savedErrno = errno; ALOGE("Could not create socket: %s", strerror(savedErrno)); return -savedErrno; } - - if (0 != TEMP_FAILURE_RETRY(bind(transportFd.fd.get(), addr.addr(), addr.addrSize()))) { + if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) { int savedErrno = errno; ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); return -savedErrno; } - // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client, - // the backlog is increased to a large number. - // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced - // to 1. - if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) { - int savedErrno = errno; - ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); - return -savedErrno; - } - - LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str()); - - if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) { - ALOGE("Another thread has set up server while calling setupSocketServer. Race?"); - return status; - } - return OK; + return setupRawSocketServer(std::move(socket_fd)); } -status_t RpcServer::setupRawSocketServer(base::unique_fd socket_fd) { - RpcTransportFd transportFd(std::move(socket_fd)); - if (!transportFd.fd.ok()) { - int savedErrno = errno; - ALOGE("Could not get initialized Unix socket: %s", strerror(savedErrno)); - return -savedErrno; - } +status_t RpcServer::setupRawSocketServer(unique_fd socket_fd) { + LOG_ALWAYS_FATAL_IF(!socket_fd.ok(), "Socket must be setup to listen."); + // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client, // the backlog is increased to a large number. // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced // to 1. - if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) { + if (0 != TEMP_FAILURE_RETRY(listen(socket_fd.get(), 50 /*backlog*/))) { int savedErrno = errno; ALOGE("Could not listen initialized Unix socket: %s", strerror(savedErrno)); return -savedErrno; } - if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) { + if (status_t status = setupExternalServer(std::move(socket_fd)); status != OK) { ALOGE("Another thread has set up server while calling setupSocketServer. Race?"); return status; } diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index c01e92f043..65b77c6736 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -139,6 +139,7 @@ public: int64_t clearCallingIdentity(); // Restores PID/UID (not SID) void restoreCallingIdentity(int64_t token); + bool hasExplicitIdentity(); status_t setupPolling(int* fd); status_t handlePolledCommands(); @@ -241,6 +242,7 @@ private: bool mPropagateWorkSource; bool mIsLooper; bool mIsFlushing; + bool mHasExplicitIdentity; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; CallRestriction mCallRestriction; diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 6de6ce8025..f730acb9f8 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -150,6 +150,9 @@ public: // Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed. binder::Status enforceNoDataAvail() const; + // This Api is used by fuzzers to skip dataAvail checks. + void setEnforceNoDataAvail(bool enforceNoDataAvail); + void freeData(); size_t objectsCount() const; @@ -1329,6 +1332,9 @@ private: // data to be overridden with zero when deallocated mutable bool mDeallocZero; + // Set this to false to skip dataAvail checks. + bool mEnforceNoDataAvail; + release_func mOwner; size_t mReserved; diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index dd177aff7f..f08bde8b80 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -17,20 +17,41 @@ #pragma once #include <sys/socket.h> +#include <stdint.h> extern "C" { struct AIBinder; +struct ARpcServer; // Starts an RPC server on a given port and a given root IBinder object. -// This function sets up the server and joins before returning. -bool RunVsockRpcServer(AIBinder* service, unsigned int port); +// Returns an opaque handle to the running server instance, or null if the server +// could not be started. +[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port); -// Starts an RPC server on a given port and a given root IBinder object. -// This function sets up the server, calls readyCallback with a given param, and -// then joins before returning. -bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port, - void (*readyCallback)(void* param), void* param); +// Starts a Unix domain RPC server with a given init-managed Unix domain `name` +// and a given root IBinder object. +// The socket should be created in init.rc with the same `name`. +// Returns an opaque handle to the running server instance, or null if the server +// could not be started. +[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name); + +// Runs ARpcServer_join() in a background thread. Immediately returns. +void ARpcServer_start(ARpcServer* server); + +// Joins the thread of a running RpcServer instance. At any given point, there +// can only be one thread calling ARpcServer_join(). +// If a client needs to actively terminate join, call ARpcServer_shutdown() in +// a separate thread. +void ARpcServer_join(ARpcServer* server); + +// Shuts down any running ARpcServer_join(). +void ARpcServer_shutdown(ARpcServer* server); + +// Frees the ARpcServer handle and drops the reference count on the underlying +// RpcServer instance. The handle must not be reused afterwards. +// This automatically calls ARpcServer_shutdown(). +void ARpcServer_free(ARpcServer* server); // Starts an RPC server on a given port and a given root IBinder factory. // RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of @@ -42,15 +63,6 @@ bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* c AIBinder* VsockRpcClient(unsigned int cid, unsigned int port); -// Starts a Unix domain RPC server with a given init-managed Unix domain `name` and -// a given root IBinder object. -// The socket should be created in init.rc with the same `name`. -// -// This function sets up the server, calls readyCallback with a given param, and -// then joins before returning. -bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name, - void (*readyCallback)(void* param), void* param); - // Gets the service via the RPC binder with Unix domain socket with the given // Unix socket `name`. // The final Unix domain socket path name is /dev/socket/`name`. diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index ae07aeecd1..f55c7796a0 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <binder_rpc_unstable.hpp> + #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <android/binder_libbinder.h> @@ -25,23 +27,32 @@ using android::OK; using android::RpcServer; using android::RpcSession; +using android::sp; using android::status_t; using android::statusToString; using android::base::unique_fd; -extern "C" { +// Opaque handle for RpcServer. +struct ARpcServer {}; -void RunRpcServer(android::sp<RpcServer>& server, AIBinder* service, - void (*readyCallback)(void* param), void* param) { - server->setRootObject(AIBinder_toPlatformBinder(service)); +static sp<RpcServer> toRpcServer(ARpcServer* handle) { + auto ref = reinterpret_cast<RpcServer*>(handle); + return sp<RpcServer>::fromExisting(ref); +} - if (readyCallback) readyCallback(param); - server->join(); +static ARpcServer* createRpcServerHandle(sp<RpcServer>& server) { + auto ref = server.get(); + ref->incStrong(ref); + return reinterpret_cast<ARpcServer*>(ref); +} - // Shutdown any open sessions since server failed. - (void)server->shutdown(); +static void freeRpcServerHandle(ARpcServer* handle) { + auto ref = reinterpret_cast<RpcServer*>(handle); + ref->decStrong(ref); } +extern "C" { + bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), void* factoryContext, unsigned int port) { auto server = RpcServer::make(); @@ -64,20 +75,47 @@ bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* c return true; } -bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port, - void (*readyCallback)(void* param), void* param) { +ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) { auto server = RpcServer::make(); if (status_t status = server->setupVsockServer(port); status != OK) { LOG(ERROR) << "Failed to set up vsock server with port " << port << " error: " << statusToString(status).c_str(); - return false; + return nullptr; } - RunRpcServer(server, service, readyCallback, param); - return true; + server->setRootObject(AIBinder_toPlatformBinder(service)); + return createRpcServerHandle(server); +} + +ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) { + auto server = RpcServer::make(); + auto fd = unique_fd(android_get_control_socket(name)); + if (!fd.ok()) { + LOG(ERROR) << "Failed to get fd for the socket:" << name; + return nullptr; + } + if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { + LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name + << " error: " << statusToString(status).c_str(); + return nullptr; + } + server->setRootObject(AIBinder_toPlatformBinder(service)); + return createRpcServerHandle(server); +} + +void ARpcServer_start(ARpcServer* handle) { + toRpcServer(handle)->start(); +} + +void ARpcServer_join(ARpcServer* handle) { + toRpcServer(handle)->join(); +} + +void ARpcServer_shutdown(ARpcServer* handle) { + toRpcServer(handle)->shutdown(); } -bool RunVsockRpcServer(AIBinder* service, unsigned int port) { - return RunVsockRpcServerCallback(service, port, nullptr, nullptr); +void ARpcServer_free(ARpcServer* handle) { + freeRpcServerHandle(handle); } AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) { @@ -90,19 +128,6 @@ AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) { return AIBinder_fromPlatformBinder(session->getRootObject()); } -bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name, - void (*readyCallback)(void* param), void* param) { - auto server = RpcServer::make(); - auto fd = unique_fd(android_get_control_socket(name)); - if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name - << " error: " << statusToString(status).c_str(); - return false; - } - RunRpcServer(server, service, readyCallback, param); - return true; -} - AIBinder* UnixDomainRpcClient(const char* name) { std::string pathname(name); pathname = ANDROID_SOCKET_DIR "/" + pathname; diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt index f9c7bcf117..1bc2416533 100644 --- a/libs/binder/libbinder_rpc_unstable.map.txt +++ b/libs/binder/libbinder_rpc_unstable.map.txt @@ -1,9 +1,12 @@ LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only global: - RunVsockRpcServer; - RunVsockRpcServerCallback; + ARpcServer_free; + ARpcServer_join; + ARpcServer_newInitUnixDomain; + ARpcServer_newVsock; + ARpcServer_shutdown; + ARpcServer_start; VsockRpcClient; - RunInitUnixDomainRpcServer; UnixDomainRpcClient; RpcPreconnectedClient; local: diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h index 6a25db220f..2a00736bc3 100644 --- a/libs/binder/ndk/include_cpp/android/binder_to_string.h +++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h @@ -160,7 +160,7 @@ class ToEmptyString { template <typename _T> std::string ToString(const _T& t) { if constexpr (details::ToEmptyString<_T>::value) { - return ""; + return "<unimplemented>"; } else if constexpr (std::is_same_v<bool, _T>) { return t ? "true" : "false"; } else if constexpr (std::is_same_v<char16_t, _T>) { @@ -176,9 +176,11 @@ std::string ToString(const _T& t) { return t; #ifdef HAS_NDK_INTERFACE } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) { - return (t.get() == nullptr) ? "(null)" : ""; + std::stringstream ss; + ss << "binder:" << std::hex << t.get(); + return ss.str(); } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) { - return (t.get() == -1) ? "(null)" : ""; + return "fd:" + std::to_string(t.get()); #endif #ifdef HAS_STRING16 } else if constexpr (std::is_same_v<String16, _T>) { diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index c23427012a..ad4188f499 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -68,6 +68,7 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const * * \param instance identifier of the service used to lookup the service. */ +[[deprecated("this polls 5s, use AServiceManager_waitForService or AServiceManager_checkService")]] __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance) __INTRODUCED_IN(29); @@ -108,6 +109,67 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(con __INTRODUCED_IN(31); /** + * Function to call when a service is registered. The instance is passed as well as + * ownership of the binder named 'registered'. + * + * WARNING: a lock is held when this method is called in order to prevent races with + * AServiceManager_NotificationRegistration_delete. Do not make synchronous binder calls when + * implementing this method to avoid deadlocks. + * + * \param instance instance name of service registered + * \param registered ownership-passed instance of service registered + * \param cookie data passed during registration for notifications + */ +typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered, + void* cookie); + +/** + * Represents a registration to servicemanager which can be cleared anytime. + */ +struct AServiceManager_NotificationRegistration; + +/** + * Get notifications when a service is registered. If the service is already registered, + * you will immediately get a notification. + * + * WARNING: it is strongly recommended to use AServiceManager_waitForService API instead. + * That API will wait synchronously, which is what you usually want in cases, including + * using some feature or during boot up. There is a history of bugs where waiting for + * notifications like this races with service startup. Also, when this API is used, a service + * bug will result in silent failure (rather than a debuggable deadlock). Furthermore, there + * is a history of this API being used to know when a service is up as a proxy for whethre + * that service should be started. This should only be used if you are intending to get + * ahold of the service as a client. For lazy services, whether a service is registered + * should not be used as a proxy for when it should be registered, which is only known + * by the real client. + * + * WARNING: if you use this API, you must also ensure that you check missing services are + * started and crash otherwise. If service failures are ignored, the system rots. + * + * \param instance name of service to wait for notifications about + * \param onRegister callback for when service is registered + * \param cookie data associated with this callback + * + * \return the token for this registration. Deleting this token will unregister. + */ +__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* +AServiceManager_registerForServiceNotifications(const char* instance, + AServiceManager_onRegister onRegister, void* cookie) + __INTRODUCED_IN(34); + +/** + * Unregister for notifications and delete the object. + * + * After this method is called, the callback is guaranteed to no longer be invoked. This will block + * until any in-progress onRegister callbacks have completed. It is therefore safe to immediately + * destroy the void* cookie that was registered when this method returns. + * + * \param notification object to dismiss + */ +void AServiceManager_NotificationRegistration_delete( + AServiceManager_NotificationRegistration* notification) __INTRODUCED_IN(34); + +/** * Check if a service is declared (e.g. VINTF manifest). * * \param instance identifier of the service. diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 32ca5649dd..5c7005ceb7 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -155,6 +155,8 @@ LIBBINDER_NDK33 { # introduced=33 LIBBINDER_NDK34 { # introduced=UpsideDownCake global: AServiceManager_getUpdatableApexName; # systemapi + AServiceManager_registerForServiceNotifications; # systemapi llndk + AServiceManager_NotificationRegistration_delete; # systemapi llndk }; LIBBINDER_NDK_PLATFORM { diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index c320e8d6df..86930229ce 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -129,7 +129,13 @@ binder_status_t ReadArray(const AParcel* parcel, void* arrayData, } T* array; - if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; + if (!allocator(arrayData, length, &array)) { + if (length < 0) { + return STATUS_UNEXPECTED_NULL; + } else { + return STATUS_NO_MEMORY; + } + } if (length <= 0) return STATUS_OK; if (array == nullptr) return STATUS_NO_MEMORY; @@ -157,7 +163,13 @@ binder_status_t ReadArray<char16_t>(const AParcel* parcel, void* arrayData, } char16_t* array; - if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; + if (!allocator(arrayData, length, &array)) { + if (length < 0) { + return STATUS_UNEXPECTED_NULL; + } else { + return STATUS_NO_MEMORY; + } + } if (length <= 0) return STATUS_OK; if (array == nullptr) return STATUS_NO_MEMORY; @@ -204,7 +216,13 @@ binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ArrayAllocator return status; } - if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; + if (!allocator(arrayData, length)) { + if (length < 0) { + return STATUS_UNEXPECTED_NULL; + } else { + return STATUS_NO_MEMORY; + } + } if (length <= 0) return STATUS_OK; diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index a12d0e9e8d..e107c83d14 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -28,6 +28,7 @@ using ::android::IBinder; using ::android::IServiceManager; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; @@ -86,6 +87,67 @@ AIBinder* AServiceManager_waitForService(const char* instance) { AIBinder_incStrong(ret.get()); return ret.get(); } +typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered, + void* cookie); + +struct AServiceManager_NotificationRegistration + : public IServiceManager::LocalRegistrationCallback { + std::mutex m; + const char* instance = nullptr; + void* cookie = nullptr; + AServiceManager_onRegister onRegister = nullptr; + + virtual void onServiceRegistration(const String16& smInstance, const sp<IBinder>& binder) { + std::lock_guard<std::mutex> l(m); + if (onRegister == nullptr) return; + + CHECK_EQ(String8(smInstance), instance); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + + onRegister(instance, ret.get(), cookie); + } + + void clear() { + std::lock_guard<std::mutex> l(m); + instance = nullptr; + cookie = nullptr; + onRegister = nullptr; + } +}; + +__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* +AServiceManager_registerForServiceNotifications(const char* instance, + AServiceManager_onRegister onRegister, + void* cookie) { + CHECK_NE(instance, nullptr); + CHECK_NE(onRegister, nullptr) << instance; + // cookie can be nullptr + + auto cb = sp<AServiceManager_NotificationRegistration>::make(); + cb->instance = instance; + cb->onRegister = onRegister; + cb->cookie = cookie; + + sp<IServiceManager> sm = defaultServiceManager(); + if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) { + LOG(ERROR) << "Failed to register for service notifications for " << instance << ": " + << statusToString(res); + return nullptr; + } + + cb->incStrong(nullptr); + return cb.get(); +} + +void AServiceManager_NotificationRegistration_delete( + AServiceManager_NotificationRegistration* notification) { + CHECK_NE(notification, nullptr); + notification->clear(); + notification->decStrong(nullptr); +} + bool AServiceManager_isDeclared(const char* instance) { if (instance == nullptr) { return false; diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index e221e4c2bb..9d5ef6805a 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -254,6 +254,47 @@ TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder_decStrong(binder); } +struct ServiceData { + std::string instance; + ndk::SpAIBinder binder; + + static void fillOnRegister(const char* instance, AIBinder* binder, void* cookie) { + ServiceData* d = reinterpret_cast<ServiceData*>(cookie); + d->instance = instance; + d->binder = ndk::SpAIBinder(binder); + } +}; + +TEST(NdkBinder, RegisterForServiceNotificationsNonExisting) { + ServiceData data; + auto* notif = AServiceManager_registerForServiceNotifications( + "DOES_NOT_EXIST", ServiceData::fillOnRegister, (void*)&data); + ASSERT_NE(notif, nullptr); + + sleep(1); // give us a chance to fail + AServiceManager_NotificationRegistration_delete(notif); + + // checking after deleting to avoid needing a mutex over the data - otherwise + // in an environment w/ multiple threads, you would need to guard access + EXPECT_EQ(data.instance, ""); + EXPECT_EQ(data.binder, nullptr); +} + +TEST(NdkBinder, RegisterForServiceNotificationsExisting) { + ServiceData data; + auto* notif = AServiceManager_registerForServiceNotifications( + kExistingNonNdkService, ServiceData::fillOnRegister, (void*)&data); + ASSERT_NE(notif, nullptr); + + sleep(1); // give us a chance to fail + AServiceManager_NotificationRegistration_delete(notif); + + // checking after deleting to avoid needing a mutex over the data - otherwise + // in an environment w/ multiple threads, you would need to guard access + EXPECT_EQ(data.instance, kExistingNonNdkService); + EXPECT_EQ(data.binder, ndk::SpAIBinder(AServiceManager_checkService(kExistingNonNdkService))); +} + TEST(NdkBinder, UnimplementedDump) { sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 738d16ac66..afd414a7cb 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -17,7 +17,6 @@ rust_library { rustlibs: [ "libbinder_ndk_sys", "libdowncast_rs", - "liblazy_static", "liblibc", ], host_supported: true, @@ -160,7 +159,6 @@ rust_test { rustlibs: [ "libbinder_ndk_sys", "libdowncast_rs", - "liblazy_static", "liblibc", ], } diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 9771cc9a89..f70ebfc364 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -19,6 +19,7 @@ rust_library { "libbinder_rpc_unstable_bindgen_sys", "libbinder_rs", "libdowncast_rs", + "libforeign_types", "liblibc", "liblog_rust", ], diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index 89a49a468c..1b719aa7d4 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -23,6 +23,4 @@ pub use client::{ get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface, get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service, }; -pub use server::{ - run_init_unix_domain_rpc_server, run_vsock_rpc_server, run_vsock_rpc_server_with_factory, -}; +pub use server::{run_vsock_rpc_server_with_factory, RpcServer, RpcServerRef}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index b350a133a8..42f5567542 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -18,114 +18,89 @@ use binder::{ unstable_api::{AIBinder, AsNative}, SpIBinder, }; +use binder_rpc_unstable_bindgen::ARpcServer; +use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; +use std::io::{Error, ErrorKind}; use std::{ffi::CString, os::raw, ptr::null_mut}; -/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock -/// port. -/// -/// If and when the server is ready for connections (it is listening on the port), `on_ready` is -/// called to allow appropriate action to be taken - e.g. to notify clients that they may now -/// attempt to connect. -/// -/// The current thread is joined to the binder thread pool to handle incoming messages. -/// -/// Returns true if the server has shutdown normally, false if it failed in some way. -pub fn run_vsock_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool -where - F: FnOnce(), -{ - let mut ready_notifier = ReadyNotifier(Some(on_ready)); - ready_notifier.run_vsock_server(service, port) -} +foreign_type! { + type CType = binder_rpc_unstable_bindgen::ARpcServer; + fn drop = binder_rpc_unstable_bindgen::ARpcServer_free; -/// Runs a binder RPC server, serving the supplied binder service implementation on the given -/// socket file name. The socket should be initialized in init.rc with the same name. -/// -/// If and when the server is ready for connections, `on_ready` is called to allow appropriate -/// action to be taken - e.g. to notify clients that they may now attempt to connect. -/// -/// The current thread is joined to the binder thread pool to handle incoming messages. -/// -/// Returns true if the server has shutdown normally, false if it failed in some way. -pub fn run_init_unix_domain_rpc_server<F>( - service: SpIBinder, - socket_name: &str, - on_ready: F, -) -> bool -where - F: FnOnce(), -{ - let mut ready_notifier = ReadyNotifier(Some(on_ready)); - ready_notifier.run_init_unix_domain_server(service, socket_name) + /// A type that represents a foreign instance of RpcServer. + #[derive(Debug)] + pub struct RpcServer; + /// A borrowed RpcServer. + pub struct RpcServerRef; } -struct ReadyNotifier<F>(Option<F>) -where - F: FnOnce(); +/// SAFETY - The opaque handle can be cloned freely. +unsafe impl Send for RpcServer {} +/// SAFETY - The underlying C++ RpcServer class is thread-safe. +unsafe impl Sync for RpcServer {} -impl<F> ReadyNotifier<F> -where - F: FnOnce(), -{ - fn run_vsock_server(&mut self, mut service: SpIBinder, port: u32) -> bool { +impl RpcServer { + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// vsock port. + pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> { let service = service.as_native_mut(); - let param = self.as_void_ptr(); // SAFETY: Service ownership is transferring to the server and won't be valid afterward. // Plus the binder objects are threadsafe. - // RunVsockRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only - // uses them before it returns, which is during the lifetime of `self`. unsafe { - binder_rpc_unstable_bindgen::RunVsockRpcServerCallback( - service, - port, - Some(Self::ready_callback), - param, - ) + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port)) } } - fn run_init_unix_domain_server(&mut self, mut service: SpIBinder, socket_name: &str) -> bool { + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// socket file name. The socket should be initialized in init.rc with the same name. + pub fn new_init_unix_domain( + mut service: SpIBinder, + socket_name: &str, + ) -> Result<RpcServer, Error> { let socket_name = match CString::new(socket_name) { Ok(s) => s, Err(e) => { log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); - return false; + return Err(Error::from(ErrorKind::InvalidInput)); } }; let service = service.as_native_mut(); - let param = self.as_void_ptr(); // SAFETY: Service ownership is transferring to the server and won't be valid afterward. // Plus the binder objects are threadsafe. - // RunInitUnixDomainRpcServer does not retain a reference to `ready_callback` or `param`; - // it only uses them before it returns, which is during the lifetime of `self`. unsafe { - binder_rpc_unstable_bindgen::RunInitUnixDomainRpcServer( + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain( service, socket_name.as_ptr(), - Some(Self::ready_callback), - param, - ) + )) + } + } + + unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { + if ptr.is_null() { + return Err(Error::new(ErrorKind::Other, "Failed to start server")); } + Ok(RpcServer::from_ptr(ptr)) } +} - fn as_void_ptr(&mut self) -> *mut raw::c_void { - self as *mut _ as *mut raw::c_void +impl RpcServerRef { + /// Starts a new background thread and calls join(). Returns immediately. + pub fn start(&self) { + unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; } - unsafe extern "C" fn ready_callback(param: *mut raw::c_void) { - // SAFETY: This is only ever called by `RunVsockRpcServerCallback`, within the lifetime of the - // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly - // aligned non-null pointer to an initialized instance). - let ready_notifier = param as *mut Self; - ready_notifier.as_mut().unwrap().notify() + /// Joins the RpcServer thread. The call blocks until the server terminates. + /// This must be called from exactly one thread. + pub fn join(&self) { + unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; } - fn notify(&mut self) { - if let Some(on_ready) = self.0.take() { - on_ready(); - } + /// Shuts down the running RpcServer. Can be called multiple times and from + /// multiple threads. Called automatically during drop(). + pub fn shutdown(&self) { + unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) }; } } diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index dee05d07cf..6f686fbd93 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -22,7 +22,6 @@ use crate::parcel::{BorrowedParcel, Serialize}; use crate::proxy::SpIBinder; use crate::sys; -use lazy_static::lazy_static; use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; use std::fs::File; @@ -508,10 +507,8 @@ pub struct LazyServiceGuard { _private: (), } -lazy_static! { - // Count of how many LazyServiceGuard objects are in existence. - static ref GUARD_COUNT: Mutex<u64> = Mutex::new(0); -} +// Count of how many LazyServiceGuard objects are in existence. +static GUARD_COUNT: Mutex<u64> = Mutex::new(0); impl LazyServiceGuard { /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 4e10fa915d..ca2cedc19d 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -502,7 +502,7 @@ mod tests { let instances = binder::get_declared_instances("android.hardware.light.ILights") .expect("Could not get declared instances"); - let expected_defaults = if has_lights { 1 } else { 0 }; + let expected_defaults = usize::from(has_lights); assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count()); } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 03e4a23bd8..a999d59d35 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -723,5 +723,7 @@ cc_defaults { "smoreland@google.com", "waghpawan@google.com", ], + // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer + hotlists: ["4637097"], }, } diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl index aac4b04dae..b2e0ef21c7 100644 --- a/libs/binder/tests/BinderRpcTestServerConfig.aidl +++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl @@ -21,7 +21,6 @@ parcelable BinderRpcTestServerConfig { int rpcSecurity; int serverVersion; int vsockPort; - int unixBootstrapFd; // Inherited from parent - int socketFd; + int socketFd; // Inherited from the parent process. @utf8InCpp String addr; } diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 79bd9d4993..02aa45f916 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -252,17 +252,19 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : ""); - base::unique_fd bootstrapClientFd, bootstrapServerFd, socketFd; - // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec. - // This is because we cannot pass ParcelFileDescriptor over a pipe. - if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) { - int savedErrno = errno; - LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); - } + base::unique_fd bootstrapClientFd, socketFd; + auto addr = allocateSocketAddress(); // Initializes the socket before the fork/exec. if (socketType == SocketType::UNIX_RAW) { socketFd = initUnixSocket(addr); + } else if (socketType == SocketType::UNIX_BOOTSTRAP) { + // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec. + // This is because we cannot pass ParcelFileDescriptor over a pipe. + if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) { + int savedErrno = errno; + LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); + } } auto ret = std::make_unique<LinuxProcessSession>( @@ -280,7 +282,6 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( serverConfig.serverVersion = serverVersion; serverConfig.vsockPort = allocateVsockPort(); serverConfig.addr = addr; - serverConfig.unixBootstrapFd = bootstrapServerFd.get(); serverConfig.socketFd = socketFd.get(); for (auto mode : options.serverSupportedFileDescriptorTransportModes) { serverConfig.serverSupportedFileDescriptorTransportModes.push_back( @@ -912,6 +913,33 @@ TEST_P(BinderRpc, SendTooManyFiles) { EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; } +TEST_P(BinderRpc, AppendInvalidFd) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + int badFd = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 0); + ASSERT_NE(badFd, -1); + + // Close the file descriptor so it becomes invalid for dup + close(badFd); + + Parcel p1; + p1.markForBinder(proc.rootBinder); + p1.writeInt32(3); + EXPECT_EQ(OK, p1.writeFileDescriptor(badFd, false)); + + Parcel pRaw; + pRaw.markForBinder(proc.rootBinder); + EXPECT_EQ(OK, pRaw.appendFrom(&p1, 0, p1.dataSize())); + + pRaw.setDataPosition(0); + EXPECT_EQ(3, pRaw.readInt32()); + ASSERT_EQ(-1, pRaw.readFileDescriptor()); +} + TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { if constexpr (!kEnableSharedLibs) { GTEST_SKIP() << "Test disabled because Binder was built as a static library"; diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index cc40995a52..995e761ec6 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -42,7 +42,7 @@ int main(int argc, const char* argv[]) { server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); unsigned int outPort = 0; - base::unique_fd unixBootstrapFd(serverConfig.unixBootstrapFd); + base::unique_fd socketFd(serverConfig.socketFd); switch (socketType) { case SocketType::PRECONNECTED: @@ -52,10 +52,10 @@ int main(int argc, const char* argv[]) { << serverConfig.addr; break; case SocketType::UNIX_BOOTSTRAP: - CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(unixBootstrapFd))); + CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd))); break; case SocketType::UNIX_RAW: - CHECK_EQ(OK, server->setupRawSocketServer(base::unique_fd(serverConfig.socketFd))); + CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd))); break; case SocketType::VSOCK: CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort)); diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 25f609674e..86461c8670 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -18,6 +18,7 @@ #include <fuzzbinder/random_parcel.h> #include <android-base/logging.h> +#include <binder/IPCThreadState.h> #include <binder/ProcessState.h> namespace android { @@ -30,10 +31,17 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { .extraFds = {}, }; + if (provider.ConsumeBool()) { + // set calling uid + IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>()); + } + while (provider.remaining_bytes() > 0) { uint32_t code = provider.ConsumeIntegral<uint32_t>(); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; + // for increased fuzz coverage + data.setEnforceNoDataAvail(provider.ConsumeBool()); sp<IBinder> target = options.extraBinders.at( provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1)); @@ -50,6 +58,8 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options); Parcel reply; + // for increased fuzz coverage + reply.setEnforceNoDataAvail(provider.ConsumeBool()); (void)target->transact(code, data, &reply, flags); // feed back in binders and fds that are returned from the service, so that diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h index bf877a3179..d88d18a7be 100644 --- a/libs/binder/trusty/include/log/log.h +++ b/libs/binder/trusty/include/log/log.h @@ -120,3 +120,7 @@ static inline void __ignore_va_args__(...) {} do { \ TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \ } while (0) + +extern "C" inline void __assert(const char* file, int line, const char* str) { + LOG_ALWAYS_FATAL("%s:%d: assertion \"%s\" failed", file, line, str); +} diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp index 480ec79725..1109ad8594 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -36,6 +36,9 @@ sp<IBinder> ServiceManager::checkService( const String16& name) const { status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service, bool /*allowIsolated*/, int /*dumpsysFlags*/) { + if (service == nullptr) { + return UNEXPECTED_NULL; + } mNameToService[name] = service; return NO_ERROR; } @@ -103,4 +106,8 @@ std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugIn std::vector<IServiceManager::ServiceDebugInfo> ret; return ret; } + +void ServiceManager::clear() { + mNameToService.clear(); +} } // namespace android diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h index ee0637eb7e..ba6bb7d95b 100644 --- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h @@ -64,6 +64,9 @@ public: std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override; + // Clear all of the registered services + void clear(); + private: std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp index 71e5abe126..8682c1c795 100644 --- a/libs/fakeservicemanager/test_sm.cpp +++ b/libs/fakeservicemanager/test_sm.cpp @@ -50,6 +50,12 @@ TEST(AddService, HappyHappy) { IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); } +TEST(AddService, SadNullBinder) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL); +} + TEST(AddService, HappyOverExistingService) { auto sm = new ServiceManager(); EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, @@ -58,6 +64,15 @@ TEST(AddService, HappyOverExistingService) { IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); } +TEST(AddService, HappyClearAddedService) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + EXPECT_NE(sm->getService(String16("foo")), nullptr); + sm->clear(); + EXPECT_EQ(sm->getService(String16("foo")), nullptr); +} + TEST(GetService, HappyHappy) { auto sm = new ServiceManager(); sp<IBinder> service = getBinder(); diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index df0b271a9b..8e57152b49 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -22,6 +22,7 @@ cc_test { "flags_test.cpp", "future_test.cpp", "match_test.cpp", + "mixins_test.cpp", "non_null_test.cpp", "optional_test.cpp", "shared_mutex_test.cpp", diff --git a/libs/ftl/mixins_test.cpp b/libs/ftl/mixins_test.cpp new file mode 100644 index 0000000000..2c9f9dfd8a --- /dev/null +++ b/libs/ftl/mixins_test.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2022 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 <ftl/mixins.h> +#include <gtest/gtest.h> + +#include <chrono> +#include <functional> +#include <type_traits> +#include <utility> + +namespace android::test { +namespace { + +// Keep in sync with example usage in header file. + +struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> { + using Constructible::Constructible; +}; + +static_assert(!std::is_default_constructible_v<Id>); + +struct Color : ftl::DefaultConstructible<Color, std::uint8_t>, + ftl::Equatable<Color>, + ftl::Orderable<Color> { + using DefaultConstructible::DefaultConstructible; +}; + +static_assert(Color() == Color(0u)); +static_assert(ftl::to_underlying(Color(-1)) == 255u); +static_assert(Color(1u) < Color(2u)); + +struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>, + ftl::Equatable<Sequence>, + ftl::Orderable<Sequence>, + ftl::Incrementable<Sequence> { + using DefaultConstructible::DefaultConstructible; +}; + +static_assert(Sequence() == Sequence(-1)); + +struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>, + ftl::Equatable<Timeout>, + ftl::Addable<Timeout> { + using DefaultConstructible::DefaultConstructible; +}; + +using namespace std::chrono_literals; +static_assert(Timeout() + Timeout(5s) == Timeout(15s)); + +// Construction. +constexpr Id kId{1234}; +constexpr Sequence kSequence; + +// Underlying value. +static_assert(ftl::to_underlying(Id(-42)) == -42); +static_assert(ftl::to_underlying(kSequence) == -1); + +// Casting. +static_assert(static_cast<std::int32_t>(Id(-1)) == -1); +static_assert(static_cast<std::int8_t>(kSequence) == -1); + +static_assert(!std::is_convertible_v<std::int32_t, Id>); +static_assert(!std::is_convertible_v<Id, std::int32_t>); + +// Equality. +static_assert(kId == Id(1234)); +static_assert(kId != Id(123)); +static_assert(kSequence == Sequence(-1)); + +// Ordering. +static_assert(Sequence(1) < Sequence(2)); +static_assert(Sequence(2) > Sequence(1)); +static_assert(Sequence(3) <= Sequence(4)); +static_assert(Sequence(4) >= Sequence(3)); +static_assert(Sequence(5) <= Sequence(5)); +static_assert(Sequence(6) >= Sequence(6)); + +// Incrementing. +template <typename Op, typename T, typename... Ts> +constexpr auto mutable_op(Op op, T lhs, Ts... rhs) { + const T result = op(lhs, rhs...); + return std::make_pair(lhs, result); +} + +static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Sequence()) == + std::make_pair(Sequence(0), Sequence(0))); + +static_assert(mutable_op([](auto& lhs) { return lhs++; }, Sequence()) == + std::make_pair(Sequence(0), Sequence(-1))); + +// Addition. + +// `Addable` implies `Incrementable`. +static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Timeout()) == + std::make_pair(Timeout(11s), Timeout(11s))); + +static_assert(mutable_op([](auto& lhs) { return lhs++; }, Timeout()) == + std::make_pair(Timeout(11s), Timeout(10s))); + +static_assert(Timeout(5s) + Timeout(6s) == Timeout(11s)); + +static_assert(mutable_op([](auto& lhs, const auto& rhs) { return lhs += rhs; }, Timeout(7s), + Timeout(8s)) == std::make_pair(Timeout(15s), Timeout(15s))); + +// Type safety. + +namespace traits { + +template <typename, typename = void> +struct is_incrementable : std::false_type {}; + +template <typename T> +struct is_incrementable<T, std::void_t<decltype(++std::declval<T&>())>> : std::true_type {}; + +template <typename T> +constexpr bool is_incrementable_v = is_incrementable<T>{}; + +template <typename, typename, typename, typename = void> +struct has_binary_op : std::false_type {}; + +template <typename Op, typename T, typename U> +struct has_binary_op<Op, T, U, std::void_t<decltype(Op{}(std::declval<T&>(), std::declval<U&>()))>> + : std::true_type {}; + +template <typename T, typename U> +constexpr bool is_equatable_v = + has_binary_op<std::equal_to<void>, T, U>{} && has_binary_op<std::not_equal_to<void>, T, U>{}; + +template <typename T, typename U> +constexpr bool is_orderable_v = + has_binary_op<std::less<void>, T, U>{} && has_binary_op<std::less_equal<void>, T, U>{} && + has_binary_op<std::greater<void>, T, U>{} && has_binary_op<std::greater_equal<void>, T, U>{}; + +template <typename T, typename U> +constexpr bool is_addable_v = has_binary_op<std::plus<void>, T, U>{}; + +} // namespace traits + +struct Real : ftl::Constructible<Real, float> { + using Constructible::Constructible; +}; + +static_assert(traits::is_equatable_v<Id, Id>); +static_assert(!traits::is_equatable_v<Real, Real>); +static_assert(!traits::is_equatable_v<Id, Color>); +static_assert(!traits::is_equatable_v<Sequence, Id>); +static_assert(!traits::is_equatable_v<Id, std::int32_t>); +static_assert(!traits::is_equatable_v<std::chrono::seconds, Timeout>); + +static_assert(traits::is_orderable_v<Color, Color>); +static_assert(!traits::is_orderable_v<Id, Id>); +static_assert(!traits::is_orderable_v<Real, Real>); +static_assert(!traits::is_orderable_v<Color, Sequence>); +static_assert(!traits::is_orderable_v<Color, std::uint8_t>); +static_assert(!traits::is_orderable_v<std::chrono::seconds, Timeout>); + +static_assert(traits::is_incrementable_v<Sequence>); +static_assert(traits::is_incrementable_v<Timeout>); +static_assert(!traits::is_incrementable_v<Id>); +static_assert(!traits::is_incrementable_v<Color>); +static_assert(!traits::is_incrementable_v<Real>); + +static_assert(traits::is_addable_v<Timeout, Timeout>); +static_assert(!traits::is_addable_v<Id, Id>); +static_assert(!traits::is_addable_v<Real, Real>); +static_assert(!traits::is_addable_v<Sequence, Sequence>); +static_assert(!traits::is_addable_v<Timeout, Sequence>); +static_assert(!traits::is_addable_v<Color, Timeout>); + +} // namespace +} // namespace android::test diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp index f7410c2aa9..6b3b6c49e5 100644 --- a/libs/ftl/optional_test.cpp +++ b/libs/ftl/optional_test.cpp @@ -164,4 +164,35 @@ TEST(Optional, AndThen) { })); } +// Comparison. +namespace { + +constexpr Optional<int> kOptional1 = 1; +constexpr Optional<int> kAnotherOptional1 = 1; +constexpr Optional<int> kOptional2 = 2; +constexpr Optional<int> kOptionalEmpty, kAnotherOptionalEmpty; + +constexpr std::optional<int> kStdOptional1 = 1; + +static_assert(kOptional1 == kAnotherOptional1); + +static_assert(kOptional1 != kOptional2); +static_assert(kOptional2 != kOptional1); + +static_assert(kOptional1 != kOptionalEmpty); +static_assert(kOptionalEmpty != kOptional1); + +static_assert(kOptionalEmpty == kAnotherOptionalEmpty); + +static_assert(kOptional1 == kStdOptional1); +static_assert(kStdOptional1 == kOptional1); + +static_assert(kOptional2 != kStdOptional1); +static_assert(kStdOptional1 != kOptional2); + +static_assert(kOptional2 != kOptionalEmpty); +static_assert(kOptionalEmpty != kOptional2); + +} // namespace + } // namespace android::test diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 4c887ec96d..a77ca04943 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -60,11 +60,11 @@ public: virtual ~BpSurfaceComposer(); status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, - const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, const InputWindowCommands& commands, - int64_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + Vector<ComposerState>& state, const Vector<DisplayState>& displays, + uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& commands, int64_t desiredPresentTime, + bool isAutoTimestamp, const client_cache_t& uncacheBuffer, + bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) override { Parcel data, reply; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 924e65ec4a..95962afda1 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -24,10 +24,31 @@ #include <binder/Parcel.h> #include <gui/IGraphicBufferProducer.h> #include <gui/LayerState.h> +#include <gui/SurfaceControl.h> #include <private/gui/ParcelUtils.h> #include <system/window.h> #include <utils/Errors.h> +#define CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD) \ + { \ + if ((OTHER.what & CHANGE_FLAG) && (FIELD != OTHER.FIELD)) { \ + DIFF_RESULT |= CHANGE_FLAG; \ + } \ + } + +#define CHECK_DIFF2(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2) \ + { \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \ + } + +#define CHECK_DIFF3(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2, FIELD3) \ + { \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \ + CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD3) \ + } + namespace android { using gui::FocusRequest; @@ -626,6 +647,74 @@ void layer_state_t::merge(const layer_state_t& other) { } } +uint64_t layer_state_t::diff(const layer_state_t& other) const { + uint64_t diff = 0; + CHECK_DIFF2(diff, ePositionChanged, other, x, y); + if (other.what & eLayerChanged) { + diff |= eLayerChanged; + diff &= ~eRelativeLayerChanged; + } + CHECK_DIFF(diff, eAlphaChanged, other, color.a); + CHECK_DIFF(diff, eMatrixChanged, other, matrix); + if (other.what & eTransparentRegionChanged && + (!transparentRegion.hasSameRects(other.transparentRegion))) { + diff |= eTransparentRegionChanged; + } + if (other.what & eFlagsChanged) { + uint64_t changedFlags = (flags & other.mask) ^ (other.flags & other.mask); + if (changedFlags) diff |= eFlagsChanged; + } + CHECK_DIFF(diff, eLayerStackChanged, other, layerStack); + CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius); + CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius); + if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged; + if (other.what & eRelativeLayerChanged) { + diff |= eRelativeLayerChanged; + diff &= ~eLayerChanged; + } + if (other.what & eReparent && + !SurfaceControl::isSameSurface(parentSurfaceControlForChild, + other.parentSurfaceControlForChild)) { + diff |= eReparent; + } + CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform); + CHECK_DIFF(diff, eTransformToDisplayInverseChanged, other, transformToDisplayInverse); + CHECK_DIFF(diff, eCropChanged, other, crop); + if (other.what & eBufferChanged) diff |= eBufferChanged; + CHECK_DIFF(diff, eDataspaceChanged, other, dataspace); + CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); + if (other.what & eSurfaceDamageRegionChanged && + (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) { + diff |= eSurfaceDamageRegionChanged; + } + CHECK_DIFF(diff, eApiChanged, other, api); + if (other.what & eSidebandStreamChanged) diff |= eSidebandStreamChanged; + CHECK_DIFF(diff, eApiChanged, other, api); + CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform); + if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged; + if (other.what & eInputInfoChanged) diff |= eInputInfoChanged; + CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace); + if (other.what & eMetadataChanged) diff |= eMetadataChanged; + CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius); + CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor); + CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility); + CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority); + CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility, + changeFrameRateStrategy); + CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint); + CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh); + CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay); + CHECK_DIFF(diff, eStretchChanged, other, stretchEffect); + CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop); + CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame); + if (other.what & eProducerDisconnect) diff |= eProducerDisconnect; + CHECK_DIFF(diff, eDropInputModeChanged, other, dropInputMode); + CHECK_DIFF(diff, eColorChanged, other, color.rgb); + CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic); + CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled); + return diff; +} + bool layer_state_t::hasBufferChanges() const { return what & layer_state_t::eBufferChanged; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index c4fb1cf408..edb18a86ee 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1108,9 +1108,12 @@ void Surface::applyGrallocMetadataLocked( ATRACE_CALL(); auto& mapper = GraphicBufferMapper::get(); mapper.setDataspace(buffer->handle, static_cast<ui::Dataspace>(queueBufferInput.dataSpace)); - mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086()); - mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613()); - mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus()); + if (mHdrMetadataIsSet & HdrMetadata::SMPTE2086) + mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086()); + if (mHdrMetadataIsSet & HdrMetadata::CTA861_3) + mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613()); + if (mHdrMetadataIsSet & HdrMetadata::HDR10PLUS) + mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus()); } void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, @@ -2252,6 +2255,7 @@ int Surface::setBuffersDataSpace(Dataspace dataSpace) int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) { ALOGV("Surface::setBuffersSmpte2086Metadata"); Mutex::Autolock lock(mMutex); + mHdrMetadataIsSet |= HdrMetadata::SMPTE2086; if (metadata) { mHdrMetadata.smpte2086 = *metadata; mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086; @@ -2264,6 +2268,7 @@ int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metad int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) { ALOGV("Surface::setBuffersCta8613Metadata"); Mutex::Autolock lock(mMutex); + mHdrMetadataIsSet |= HdrMetadata::CTA861_3; if (metadata) { mHdrMetadata.cta8613 = *metadata; mHdrMetadata.validTypes |= HdrMetadata::CTA861_3; @@ -2276,6 +2281,7 @@ int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) { ALOGV("Surface::setBuffersBlobMetadata"); Mutex::Autolock lock(mMutex); + mHdrMetadataIsSet |= HdrMetadata::HDR10PLUS; if (size > 0) { mHdrMetadata.hdr10plus.assign(metadata, metadata + size); mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 9e175ec42e..1e43700d06 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -912,11 +912,11 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { client_cache_t uncacheBuffer; uncacheBuffer.token = BufferCache::getInstance().getToken(); uncacheBuffer.id = cacheId; - + Vector<ComposerState> composerStates; status_t status = - sf->setTransactionState(FrameTimelineInfo{}, {}, {}, ISurfaceComposer::eOneWay, - Transaction::getDefaultApplyToken(), {}, systemTime(), true, - uncacheBuffer, false, {}, generateId()); + sf->setTransactionState(FrameTimelineInfo{}, composerStates, {}, + ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(), + {}, systemTime(), true, uncacheBuffer, false, {}, generateId()); if (status != NO_ERROR) { ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s", strerror(-status)); @@ -1250,6 +1250,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) || (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) || (mask & layer_state_t::eEnableBackpressure) || + (mask & layer_state_t::eIgnoreDestinationFrame) || (mask & layer_state_t::eLayerIsDisplayDecoration)) { s->what |= layer_state_t::eFlagsChanged; } diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index 1a8fc1a00a..2d863c2585 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -36,8 +36,12 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), mHasFenceSync(false), mHasWaitSync(false) { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - // This can only be called after EGL has been initialized; otherwise the - // check below will abort. + // eglQueryString can only be called after EGL has been initialized; + // otherwise the check below will abort. If RenderEngine is using SkiaVk, + // EGL will not have been initialized. There's no problem with initializing + // it again here (it is ref counted), and then terminating it later. + EGLBoolean initialized = eglInitialize(dpy, nullptr, nullptr); + LOG_ALWAYS_FATAL_IF(!initialized, "eglInitialize failed"); const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { @@ -63,6 +67,8 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), mString.append(" EGL_KHR_wait_sync"); } mString.append("]"); + // Terminate EGL to match the eglInitialize above + eglTerminate(dpy); } bool SyncFeatures::useNativeFenceSync() const { diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl index 80d5ced0a4..75cea157aa 100644 --- a/libs/gui/aidl/android/gui/OverlayProperties.aidl +++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl @@ -16,9 +16,11 @@ package android.gui; -import android.gui.SupportedBufferCombinations; - /** @hide */ parcelable OverlayProperties { + parcelable SupportedBufferCombinations { + int[] pixelFormats; + int[] dataspaces; + } SupportedBufferCombinations[] combinations; } diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp index 1c61d6bb8b..82e1b5ae4d 100644 --- a/libs/gui/fuzzer/Android.bp +++ b/libs/gui/fuzzer/Android.bp @@ -62,7 +62,6 @@ cc_defaults { "libutils", "libnativewindow", "libvndksupport", - "libbufferhubqueue", ], header_libs: [ "libdvr_headers", diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index e91d75467d..d517e99fda 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -55,7 +55,7 @@ namespace android { struct client_cache_t; -struct ComposerState; +class ComposerState; struct DisplayStatInfo; struct DisplayState; struct InputWindowCommands; @@ -110,7 +110,7 @@ public: /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ virtual status_t setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state, + const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index e16f89c6a5..9cf62bc7d6 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -66,8 +66,9 @@ enum class GameMode : int32_t { Standard = 1, Performance = 2, Battery = 3, + Custom = 4, - ftl_last = Battery + ftl_last = Custom }; } // namespace android::gui diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 45272e7431..6ec6bd70db 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -201,7 +201,32 @@ struct layer_state_t { void merge(const layer_state_t& other); status_t write(Parcel& output) const; status_t read(const Parcel& input); + // Compares two layer_state_t structs and returns a set of change flags describing all the + // states that are different. + uint64_t diff(const layer_state_t& other) const; bool hasBufferChanges() const; + + // Changes to the tree structure. + static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged | + layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent | + layer_state_t::eBackgroundColorChanged; + // Content updates. + static constexpr uint64_t CONTENT_CHANGES = layer_state_t::eAlphaChanged | + layer_state_t::eTransparentRegionChanged | layer_state_t::eShadowRadiusChanged | + layer_state_t::eRenderBorderChanged | layer_state_t::eColorChanged | + layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged | + layer_state_t::eApiChanged | layer_state_t::eSidebandStreamChanged | + layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | + layer_state_t::eBackgroundColorChanged | layer_state_t::eColorSpaceAgnosticChanged | + layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged | + layer_state_t::eAutoRefreshChanged | layer_state_t::eStretchChanged; + // Changes to content or children size. + static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::ePositionChanged | + layer_state_t::eMatrixChanged | layer_state_t::eTransparentRegionChanged | + layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged | + layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged | + layer_state_t::eDestinationFrameChanged; + bool hasValidBuffer() const; void sanitize(int32_t permissions); @@ -212,6 +237,11 @@ struct layer_state_t { float dsdy{0}; status_t write(Parcel& output) const; status_t read(const Parcel& input); + inline bool operator==(const matrix22_t& other) const { + return std::tie(dsdx, dtdx, dtdy, dsdy) == + std::tie(other.dsdx, other.dtdx, other.dtdy, other.dsdy); + } + inline bool operator!=(const matrix22_t& other) const { return !(*this == other); } }; sp<IBinder> surface; int32_t layerId; @@ -311,7 +341,8 @@ struct layer_state_t { bool dimmingEnabled; }; -struct ComposerState { +class ComposerState { +public: layer_state_t state; status_t write(Parcel& output) const; status_t read(const Parcel& input); diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 7aec0bf09d..b9ccdc9124 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -486,6 +486,11 @@ protected: // queue operation. There is no HDR metadata by default. HdrMetadata mHdrMetadata; + // mHdrMetadataIsSet is a bitfield to track which HDR metadata has been set. + // Prevent Surface from resetting HDR metadata that was set on a bufer when + // HDR metadata is not set on this Surface. + uint32_t mHdrMetadataIsSet{0}; + // mCrop is the crop rectangle that will be used for the next buffer // that gets queued. It is set by calling setCrop. Rect mCrop; diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index ac74c8a91f..b01a3db52d 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -272,6 +272,7 @@ public: WindowInfoHandle(const WindowInfo& other); inline const WindowInfo* getInfo() const { return &mInfo; } + inline WindowInfo* editInfo() { return &mInfo; } sp<IBinder> getToken() const; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 67c669ddb7..6d3b42515b 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -696,7 +696,7 @@ public: } status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/, - const Vector<ComposerState>& /*state*/, + Vector<ComposerState>& /*state*/, const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, const sp<IBinder>& /*applyToken*/, const InputWindowCommands& /*inputWindowCommands*/, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index cf5a7e7b05..3685f54a53 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -238,6 +238,10 @@ bool isFromSource(uint32_t source, uint32_t test) { return (source & test) == test; } +bool isStylusToolType(uint32_t toolType) { + return toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || toolType == AMOTION_EVENT_TOOL_TYPE_ERASER; +} + VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp index 16ffa10223..ed9ac9fc72 100644 --- a/libs/input/PropertyMap.cpp +++ b/libs/input/PropertyMap.cpp @@ -116,25 +116,24 @@ android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char Tokenizer* rawTokenizer; status_t status = Tokenizer::open(String8(filename), &rawTokenizer); - std::unique_ptr<Tokenizer> tokenizer(rawTokenizer); if (status) { - ALOGE("Error %d opening property file %s.", status, filename); - } else { + return android::base::Error(-status) << "Could not open file: " << filename; + } #if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(outMap.get(), tokenizer.get()); - status = parser.parse(); + std::unique_ptr<Tokenizer> tokenizer(rawTokenizer); + Parser parser(outMap.get(), tokenizer.get()); + status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed property file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), + tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (status) { - return android::base::Error(BAD_VALUE) << "Could not parse " << filename; - } + if (status) { + return android::base::Error(BAD_VALUE) << "Could not parse " << filename; } + return std::move(outMap); } diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index e2bfb508e1..5c008b1158 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -37,6 +37,10 @@ VelocityControl::VelocityControl() { reset(); } +VelocityControlParameters& VelocityControl::getParameters() { + return mParameters; +} + void VelocityControl::setParameters(const VelocityControlParameters& parameters) { mParameters = parameters; reset(); diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp index 0375915839..54af7c912d 100644 --- a/libs/jpegrecoverymap/Android.bp +++ b/libs/jpegrecoverymap/Android.bp @@ -31,6 +31,7 @@ cc_library_static { srcs: [ "recoverymap.cpp", "recoverymapmath.cpp", + "recoverymaputils.cpp", ], shared_libs: [ diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h index df24b10ebc..5c9c8b6ec6 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h @@ -61,6 +61,21 @@ public: * decompressImage(). */ size_t getDecompressedImageHeight(); + /* + * Returns the XMP data from the image. + */ + void* getXMPPtr(); + /* + * Returns the decompressed XMP buffer size. This method must be called only after + * calling decompressImage(). + */ + size_t getXMPSize(); + + bool getCompressedImageParameters(const void* image, int length, + size_t* pWidth, size_t* pHeight, + std::vector<uint8_t>* &iccData, + std::vector<uint8_t>* &exifData); + private: bool decode(const void* image, int length); // Returns false if errors occur. @@ -72,6 +87,9 @@ private: static const int kCompressBatchSize = 16; // The buffer that holds the decompressed result. std::vector<JOCTET> mResultBuffer; + // The buffer that holds XMP Data. + std::vector<JOCTET> mXMPBuffer; + // Resolution of the decompressed image. size_t mWidth; size_t mHeight; diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h index 194cd2f403..9f53a5791a 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h @@ -37,6 +37,7 @@ enum { ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2, ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3, ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4, + ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5, JPEGR_RUNTIME_ERROR_BASE = -20000, ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1, diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index b2ca481aa7..55973034bb 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -22,11 +22,24 @@ namespace android::recoverymap { typedef enum { - JPEGR_COLORSPACE_UNSPECIFIED, - JPEGR_COLORSPACE_BT709, - JPEGR_COLORSPACE_P3, - JPEGR_COLORSPACE_BT2100, -} jpegr_color_space; + JPEGR_COLORGAMUT_UNSPECIFIED, + JPEGR_COLORGAMUT_BT709, + JPEGR_COLORGAMUT_P3, + JPEGR_COLORGAMUT_BT2100, +} jpegr_color_gamut; + +// Transfer functions as defined for XMP metadata +typedef enum { + JPEGR_TF_HLG = 0, + JPEGR_TF_PQ = 1, +} jpegr_transfer_function; + +struct jpegr_info_struct { + size_t width; + size_t height; + std::vector<uint8_t>* iccData; + std::vector<uint8_t>* exifData; +}; /* * Holds information for uncompressed image or recovery map. @@ -38,8 +51,8 @@ struct jpegr_uncompressed_struct { int width; // Height of the recovery map or image in pixels. int height; - // Color space. - jpegr_color_space colorSpace; + // Color gamut. + jpegr_color_gamut colorGamut; }; /* @@ -48,10 +61,12 @@ struct jpegr_uncompressed_struct { struct jpegr_compressed_struct { // Pointer to the data location. void* data; - // Data length. + // Used data length in bytes. int length; - // Color space. - jpegr_color_space colorSpace; + // Maximum available data length in bytes. + int maxLength; + // Color gamut. + jpegr_color_gamut colorGamut; }; /* @@ -64,9 +79,52 @@ struct jpegr_exif_struct { int length; }; +struct chromaticity_coord { + float x; + float y; +}; + + +struct st2086_metadata { + // xy chromaticity coordinate of the red primary of the mastering display + chromaticity_coord redPrimary; + // xy chromaticity coordinate of the green primary of the mastering display + chromaticity_coord greenPrimary; + // xy chromaticity coordinate of the blue primary of the mastering display + chromaticity_coord bluePrimary; + // xy chromaticity coordinate of the white point of the mastering display + chromaticity_coord whitePoint; + // Maximum luminance in nits of the mastering display + uint32_t maxLuminance; + // Minimum luminance in nits of the mastering display + float minLuminance; +}; + +struct hdr10_metadata { + // Mastering display color volume + st2086_metadata st2086Metadata; + // Max frame average light level in nits + float maxFALL; + // Max content light level in nits + float maxCLL; +}; + +struct jpegr_metadata { + // JPEG/R version + uint32_t version; + // Range scaling factor for the map + float rangeScalingFactor; + // The transfer function for decoding the HDR representation of the image + jpegr_transfer_function transferFunction; + // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ + hdr10_metadata hdr10Metadata; +}; + typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr; typedef struct jpegr_compressed_struct* jr_compressed_ptr; typedef struct jpegr_exif_struct* jr_exif_ptr; +typedef struct jpegr_metadata* jr_metadata_ptr; +typedef struct jpegr_info_struct* jr_info_ptr; class RecoveryMap { public: @@ -75,9 +133,10 @@ public: * * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append * the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same - * resolution and color space. + * resolution. * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format + * @param hdr_tf transfer function of the HDR image * @param dest destination of the compressed JPEGR image * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is * the highest quality @@ -86,6 +145,7 @@ public: */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, + jpegr_transfer_function hdr_tf, jr_compressed_ptr dest, int quality, jr_exif_ptr exif); @@ -100,12 +160,14 @@ public: * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param compressed_jpeg_image compressed 8-bit JPEG image + * @param hdr_tf transfer function of the HDR image * @param dest destination of the compressed JPEGR image * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, jr_compressed_ptr dest); /* @@ -115,27 +177,28 @@ public: * * Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input * and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR - * and SDR inputs must be the same resolution and color space. + * and SDR inputs must be the same resolution. * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param compressed_jpeg_image compressed 8-bit JPEG image + * @param hdr_tf transfer function of the HDR image * @param dest destination of the compressed JPEGR image * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, jr_compressed_ptr dest); /* * Decompress JPEGR image. * + * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR. * @param compressed_jpegr_image compressed JPEGR image * @param dest destination of the uncompressed JPEGR image - * @param exif destination of the decoded EXIF metadata. Default value is nullptr where EXIF - * metadata will not be decoded. - * @param request_sdr flag that request SDR output, default to false (request HDR output). If - * set to true, decoder will only decode the primary image which is SDR. - * Setting of request_sdr and input source (HDR or SDR) can be found in - * the table below: + * @param exif destination of the decoded EXIF metadata. + * @param request_sdr flag that request SDR output. If set to true, decoder will only decode + * the primary image which is SDR. Setting of request_sdr and input source + * (HDR or SDR) can be found in the table below: * | input source | request_sdr | output of decoding | * | HDR | true | SDR | * | HDR | false | HDR | @@ -147,6 +210,17 @@ public: jr_uncompressed_ptr dest, jr_exif_ptr exif = nullptr, bool request_sdr = false); + + /* + * Gets Info from JPEGR file without decoding it. + * + * The output is filled jpegr_info structure + * @param compressed_jpegr_image compressed JPEGR image + * @param jpegr_info pointer to output JPEGR info + * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise + */ + status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, + jr_info_ptr jpegr_info); private: /* * This method is called in the decoding pipeline. It will decode the recovery map. @@ -176,29 +250,46 @@ private: * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param dest recovery map; caller responsible for memory of data - * @param hdr_ratio HDR ratio will be updated in this method + * @param metadata metadata provides the transfer function for the HDR + * image; range_scaling_factor and hdr10 FALL and CLL will + * be updated. * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_p010_image, - jr_uncompressed_ptr dest, - float &hdr_ratio); + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest); /* * This method is called in the decoding pipeline. It will take the uncompressed (decoded) - * 8-bit yuv image and the uncompressed (decoded) recovery map as input, and calculate the - * 10-bit recovered image (in p010 color format). + * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as + * input, and calculate the 10-bit recovered image. The recovered output image is the same + * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata, + * and is in RGBA1010102 data format. * * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param uncompressed_recovery_map uncompressed recovery map + * @param metadata JPEG/R metadata extracted from XMP. * @param dest reconstructed HDR image * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_recovery_map, + jr_metadata_ptr metadata, jr_uncompressed_ptr dest); /* + * This methoud is called to separate primary image and recovery map image from JPEGR + * + * @param compressed_jpegr_image compressed JPEGR image + * @param primary_image destination of primary image + * @param recovery_map destination of compressed recovery map + * @return NO_ERROR if calculation succeeds, error code if error occurs. + */ + status_t extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr primary_image, + jr_compressed_ptr recovery_map); + /* * This method is called in the decoding pipeline. It will read XMP metadata to find the start * position of the compressed recovery map, and will extract the compressed recovery map. * @@ -206,7 +297,8 @@ private: * @param dest destination of compressed recovery map * @return NO_ERROR if calculation succeeds, error code if error occurs. */ - status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr dest); + status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr dest); /* * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image @@ -215,13 +307,13 @@ private: * * @param compressed_jpeg_image compressed 8-bit JPEG image * @param compress_recovery_map compressed recover map - * @param hdr_ratio HDR ratio + * @param metadata JPEG/R metadata to encode in XMP of the jpeg * @param dest compressed JPEGR image * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, jr_compressed_ptr compressed_recovery_map, - float hdr_ratio, + jr_metadata_ptr metadata, jr_compressed_ptr dest); /* @@ -229,7 +321,7 @@ private: * * below is an example of the XMP metadata that this function generates where * secondary_image_length = 1000 - * hdr_ratio = 1.25 + * range_scaling_factor = 1.25 * * <x:xmpmeta * xmlns:x="adobe:ns:meta/" @@ -239,7 +331,7 @@ private: * <rdf:Description * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"> * <GContainer:Version>1</GContainer:Version> - * <GContainer:HdrRatio>1.25</GContainer:HdrRatio> + * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor> * <GContainer:Directory> * <rdf:Seq> * <rdf:li> @@ -260,10 +352,10 @@ private: * </x:xmpmeta> * * @param secondary_image_length length of secondary image - * @param hdr_ratio hdr ratio + * @param metadata JPEG/R metadata to encode as XMP * @return XMP metadata in type of string */ - std::string generateXmp(int secondary_image_length, float hdr_ratio); + std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata); }; } // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h index 8e9b07bf3d..fe7a651ffb 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -23,6 +23,9 @@ namespace android::recoverymap { +//////////////////////////////////////////////////////////////////////////////// +// Framework + const float kSdrWhiteNits = 100.0f; struct Color { @@ -40,36 +43,181 @@ struct Color { }; }; +typedef Color (*ColorTransformFn)(Color); +typedef float (*ColorCalculationFn)(Color); + +inline Color operator+=(Color& lhs, const Color& rhs) { + lhs.r += rhs.r; + lhs.g += rhs.g; + lhs.b += rhs.b; + return lhs; +} +inline Color operator-=(Color& lhs, const Color& rhs) { + lhs.r -= rhs.r; + lhs.g -= rhs.g; + lhs.b -= rhs.b; + return lhs; +} + +inline Color operator+(const Color& lhs, const Color& rhs) { + Color temp = lhs; + return temp += rhs; +} +inline Color operator-(const Color& lhs, const Color& rhs) { + Color temp = lhs; + return temp -= rhs; +} + +inline Color operator+=(Color& lhs, const float rhs) { + lhs.r += rhs; + lhs.g += rhs; + lhs.b += rhs; + return lhs; +} +inline Color operator-=(Color& lhs, const float rhs) { + lhs.r -= rhs; + lhs.g -= rhs; + lhs.b -= rhs; + return lhs; +} +inline Color operator*=(Color& lhs, const float rhs) { + lhs.r *= rhs; + lhs.g *= rhs; + lhs.b *= rhs; + return lhs; +} +inline Color operator/=(Color& lhs, const float rhs) { + lhs.r /= rhs; + lhs.g /= rhs; + lhs.b /= rhs; + return lhs; +} + +inline Color operator+(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp += rhs; +} +inline Color operator-(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp -= rhs; +} +inline Color operator*(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp *= rhs; +} +inline Color operator/(const Color& lhs, const float rhs) { + Color temp = lhs; + return temp /= rhs; +} + + +//////////////////////////////////////////////////////////////////////////////// +// sRGB transformations + /* - * Convert from OETF'd bt.2100 RGB to YUV, according to BT.2100 + * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1. */ -Color bt2100RgbToYuv(Color e); +float srgbLuminance(Color e); /* - * Convert srgb YUV to RGB, according to ECMA TR/98. + * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98. */ -Color srgbYuvToRgb(Color e); +Color srgbYuvToRgb(Color e_gamma); /* - * TODO: better source for srgb transfer function - * Convert from srgb to linear, according to https://en.wikipedia.org/wiki/SRGB. + * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98. + */ +Color srgbRgbToYuv(Color e_gamma); + +/* + * Convert from srgb to linear, according to IEC 61966-2-1. + * * [0.0, 1.0] range in and out. */ -float srgbInvOetf(float e); -Color srgbInvOetf(Color e); +float srgbInvOetf(float e_gamma); +Color srgbInvOetf(Color e_gamma); + + +//////////////////////////////////////////////////////////////////////////////// +// Display-P3 transformations + +/* + * Calculated the luminance of a linear RGB P3 pixel, according to EG 432-1. + */ +float p3Luminance(Color e); + + +//////////////////////////////////////////////////////////////////////////////// +// BT.2100 transformations - according to ITU-R BT.2100-2 + +/* + * Calculate the luminance of a linear RGB BT.2100 pixel. + */ +float bt2100Luminance(Color e); /* - * Convert from HLG to scene luminance in nits, according to BT.2100. + * Convert from OETF'd BT.2100 RGB to YUV. */ -float hlgInvOetf(float e); +Color bt2100RgbToYuv(Color e_gamma); /* - * Convert from scene luminance in nits to HLG, according to BT.2100. + * Convert from OETF'd BT.2100 YUV to RGB. + */ +Color bt2100YuvToRgb(Color e_gamma); + +/* + * Convert from scene luminance in nits to HLG. */ -float hlgOetf(float e); Color hlgOetf(Color e); /* + * Convert from HLG to scene luminance in nits. + */ +Color hlgInvOetf(Color e_gamma); + +/* + * Convert from scene luminance in nits to PQ. + */ +Color pqOetf(Color e); + +/* + * Convert from PQ to scene luminance in nits. + */ +Color pqInvOetf(Color e_gamma); + + +//////////////////////////////////////////////////////////////////////////////// +// Color space conversions + +/* + * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1. + * + * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the + * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is + * always the inverse of the RGB gamut to XYZ matrix. + */ +Color bt709ToP3(Color e); +Color bt709ToBt2100(Color e); +Color p3ToBt709(Color e); +Color p3ToBt2100(Color e); +Color bt2100ToBt709(Color e); +Color bt2100ToP3(Color e); + +/* + * Identity conversion. + */ +inline Color identityConversion(Color e) { return e; } + +/* + * Get the conversion to apply to the HDR image for recovery map generation + */ +ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut); + + +//////////////////////////////////////////////////////////////////////////////// +// Recovery map calculations + +/* * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR * luminances in linear space, and the hdr ratio to encode against. */ @@ -87,6 +235,11 @@ Color applyRecovery(Color e, float recovery, float hdr_ratio); Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y); /* + * Helper for sampling from images. + */ +Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y); + +/* * Sample the recovery value for the map from a given x,y coordinate on a scale * that is map scale factor larger than the map size. */ @@ -98,8 +251,20 @@ float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size * * Expect narrow-range image data for P010. */ -float sampleYuv420Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); -float sampleP010Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); +Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +/* + * Sample the image Y value at the provided location, with a weighting based on nearby pixels + * and the map scale factor. Assumes narrow-range image data for P010. + */ +Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +/* + * Convert from Color to RGBA1010102. + * + * Alpha always set to 1.0. + */ +uint32_t colorToRgba1010102(Color e_gamma); } // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h new file mode 100644 index 0000000000..e35f2d72cb --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 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. + */ + +#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H +#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H + +#include <stdint.h> +#include <cstdio> + + +namespace android::recoverymap { + +struct jpegr_metadata; + +/* + * Parses XMP packet and fills metadata with data from XMP + * + * @param xmp_data pointer to XMP packet + * @param xmp_size size of XMP packet + * @param metadata place to store HDR metadata values + * @return true if metadata is successfully retrieved, false otherwise +*/ +bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); + +} + +#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp index c1fb6c3f1d..0185e55e9e 100644 --- a/libs/jpegrecoverymap/jpegdecoder.cpp +++ b/libs/jpegrecoverymap/jpegdecoder.cpp @@ -20,8 +20,15 @@ #include <errno.h> #include <setjmp.h> +#include <string> + +using namespace std; namespace android::recoverymap { + +const uint32_t kExifMarker = JPEG_APP0 + 1; +const uint32_t kICCMarker = JPEG_APP0 + 2; + struct jpegr_source_mgr : jpeg_source_mgr { jpegr_source_mgr(const uint8_t* ptr, int len); ~jpegr_source_mgr(); @@ -88,6 +95,7 @@ bool JpegDecoder::decompressImage(const void* image, int length) { } mResultBuffer.clear(); + mXMPBuffer.clear(); if (!decode(image, length)) { return false; } @@ -103,6 +111,15 @@ size_t JpegDecoder::getDecompressedImageSize() { return mResultBuffer.size(); } +void* JpegDecoder::getXMPPtr() { + return mXMPBuffer.data(); +} + +size_t JpegDecoder::getXMPSize() { + return mXMPBuffer.size(); +} + + size_t JpegDecoder::getDecompressedImageWidth() { return mWidth; } @@ -115,6 +132,8 @@ bool JpegDecoder::decode(const void* image, int length) { jpeg_decompress_struct cinfo; jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); jpegrerror_mgr myerr; + string nameSpace = "http://ns.adobe.com/xap/1.0/"; + cinfo.err = jpeg_std_error(&myerr.pub); myerr.pub.error_exit = jpegrerror_exit; @@ -124,9 +143,26 @@ bool JpegDecoder::decode(const void* image, int length) { } jpeg_create_decompress(&cinfo); + jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); + cinfo.src = &mgr; jpeg_read_header(&cinfo, TRUE); + // Save XMP Data + for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) { + if (marker->marker == kExifMarker) { + const unsigned int len = marker->data_length; + if (len > nameSpace.size() && + !strncmp(reinterpret_cast<const char*>(marker->data), + nameSpace.c_str(), nameSpace.size())) { + mXMPBuffer.resize(len+1, 0); + memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len); + break; + } + } + } + + mWidth = cinfo.image_width; mHeight = cinfo.image_height; @@ -161,6 +197,43 @@ bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, return decompressYUV(cinfo, dest); } +bool JpegDecoder::getCompressedImageParameters(const void* image, int length, + size_t *pWidth, size_t *pHeight, + std::vector<uint8_t> *&iccData , std::vector<uint8_t> *&exifData) { + jpeg_decompress_struct cinfo; + jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); + jpegrerror_mgr myerr; + cinfo.err = jpeg_std_error(&myerr.pub); + myerr.pub.error_exit = jpegrerror_exit; + + if (setjmp(myerr.setjmp_buffer)) { + jpeg_destroy_decompress(&cinfo); + return false; + } + jpeg_create_decompress(&cinfo); + + jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); + jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF); + + cinfo.src = &mgr; + if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) { + jpeg_destroy_decompress(&cinfo); + return false; + } + + *pWidth = cinfo.image_width; + *pHeight = cinfo.image_height; + + //TODO: Parse iccProfile and exifData + (void)iccData; + (void)exifData; + + + jpeg_destroy_decompress(&cinfo); + return true; +} + + bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) { JSAMPROW y[kCompressBatchSize]; diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index bd16a68b0d..4a209ec381 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -14,24 +14,27 @@ * limitations under the License. */ -// TODO: need to clean up handling around hdr_ratio and passing it around -// TODO: need to handle color space information; currently we assume everything -// is srgb in. -// TODO: handle PQ encode/decode (currently only HLG) - #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/jpegencoder.h> #include <jpegrecoverymap/jpegdecoder.h> #include <jpegrecoverymap/recoverymapmath.h> +#include <jpegrecoverymap/recoverymaputils.h> #include <image_io/jpeg/jpeg_marker.h> #include <image_io/xml/xml_writer.h> +#include <image_io/jpeg/jpeg_info.h> +#include <image_io/jpeg/jpeg_scanner.h> +#include <image_io/jpeg/jpeg_info_builder.h> +#include <image_io/base/data_segment_data_source.h> +#include <utils/Log.h> #include <memory> #include <sstream> #include <string> +#include <cmath> using namespace std; +using namespace photos_editing_formats::image_io; namespace android::recoverymap { @@ -43,9 +46,23 @@ namespace android::recoverymap { } \ } +// The current JPEGR version that we encode to +static const uint32_t kJpegrVersion = 1; + // Map is quarter res / sixteenth size static const size_t kMapDimensionScaleFactor = 4; - +// JPEG compress quality (0 ~ 100) for recovery map +static const int kMapCompressQuality = 85; + +// TODO: fill in st2086 metadata +static const st2086_metadata kSt2086Metadata = { + {0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.0f}, + 0, + 1.0f, +}; /* * Helper function used for generating XMP metadata. @@ -70,7 +87,7 @@ string Name(const string &prefix, const string &suffix) { * @return status of succeed or error code. */ status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) { - if (position + length > destination->length) { + if (position + length > destination->maxLength) { return ERROR_JPEGR_BUFFER_TOO_SMALL; } @@ -81,6 +98,7 @@ status_t Write(jr_compressed_ptr destination, const void* source, size_t length, status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, + jpegr_transfer_function hdr_tf, jr_compressed_ptr dest, int quality, jr_exif_ptr /* exif */) { @@ -99,21 +117,27 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, return ERROR_JPEGR_RESOLUTION_MISMATCH; } + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + jpegr_uncompressed_struct map; - float hdr_ratio = 0.0f; JPEGR_CHECK(generateRecoveryMap( - uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); + uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(map.data)); jpegr_compressed_struct compressed_map; - std::unique_ptr<uint8_t[]> compressed_map_data = - std::make_unique<uint8_t[]>(map.width * map.height); + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); JpegEncoder jpeg_encoder; - // TODO: ICC data - need color space information + // TODO: determine ICC data based on color gamut information if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data, uncompressed_yuv_420_image->width, uncompressed_yuv_420_image->height, quality, nullptr, 0)) { @@ -123,7 +147,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jpeg.data = jpeg_encoder.getCompressedImagePtr(); jpeg.length = jpeg_encoder.getCompressedImageSize(); - JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, hdr_ratio, dest)); + JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest)); return NO_ERROR; } @@ -131,6 +155,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, jr_compressed_ptr dest) { if (uncompressed_p010_image == nullptr || uncompressed_yuv_420_image == nullptr @@ -144,26 +169,33 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, return ERROR_JPEGR_RESOLUTION_MISMATCH; } + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + jpegr_uncompressed_struct map; - float hdr_ratio = 0.0f; JPEGR_CHECK(generateRecoveryMap( - uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); + uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(map.data)); jpegr_compressed_struct compressed_map; - std::unique_ptr<uint8_t[]> compressed_map_data = - std::make_unique<uint8_t[]>(map.width * map.height); + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); - JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest)); + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest)); return NO_ERROR; } status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_compressed_ptr compressed_jpeg_image, + jpegr_transfer_function hdr_tf, jr_compressed_ptr dest) { if (uncompressed_p010_image == nullptr || compressed_jpeg_image == nullptr @@ -179,39 +211,71 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); + uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut; if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) { return ERROR_JPEGR_RESOLUTION_MISMATCH; } + jpegr_metadata metadata; + metadata.version = kJpegrVersion; + metadata.transferFunction = hdr_tf; + if (hdr_tf == JPEGR_TF_PQ) { + metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata; + } + jpegr_uncompressed_struct map; - float hdr_ratio = 0.0f; JPEGR_CHECK(generateRecoveryMap( - &uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); + &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map)); std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(map.data)); jpegr_compressed_struct compressed_map; - std::unique_ptr<uint8_t[]> compressed_map_data = - std::make_unique<uint8_t[]>(map.width * map.height); + compressed_map.maxLength = map.width * map.height; + unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength); compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); - JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest)); + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest)); + + return NO_ERROR; +} + +status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, + jr_info_ptr jpegr_info) { + if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + jpegr_compressed_struct primary_image, recovery_map; + JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, + &primary_image, &recovery_map)); + + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length, + &jpegr_info->width, &jpegr_info->height, + jpegr_info->iccData, jpegr_info->exifData)) { + return ERROR_JPEGR_DECODE_ERROR; + } return NO_ERROR; } + status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, jr_uncompressed_ptr dest, - jr_exif_ptr /* exif */, - bool /* request_sdr */) { + jr_exif_ptr exif, + bool request_sdr) { if (compressed_jpegr_image == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } + // TODO: fill EXIF data + (void) exif; + jpegr_compressed_struct compressed_map; + jpegr_metadata metadata; JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map)); jpegr_uncompressed_struct map; @@ -227,7 +291,19 @@ status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); - JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, dest)); + if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()), + jpeg_decoder.getXMPSize(), &metadata)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + if (request_sdr) { + memcpy(dest->data, uncompressed_yuv_420_image.data, + uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2); + dest->width = uncompressed_yuv_420_image.width; + dest->height = uncompressed_yuv_420_image.height; + } else { + JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest)); + } return NO_ERROR; } @@ -257,30 +333,36 @@ status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recov return ERROR_JPEGR_INVALID_NULL_PTR; } - // TODO: should we have ICC data? + // TODO: should we have ICC data for the map? JpegEncoder jpeg_encoder; - if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data, uncompressed_recovery_map->width, - uncompressed_recovery_map->height, 85, nullptr, 0, + if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data, + uncompressed_recovery_map->width, + uncompressed_recovery_map->height, + kMapCompressQuality, + nullptr, + 0, true /* isSingleChannel */)) { return ERROR_JPEGR_ENCODE_ERROR; } - if (dest->length < jpeg_encoder.getCompressedImageSize()) { + if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) { return ERROR_JPEGR_BUFFER_TOO_SMALL; } memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize()); dest->length = jpeg_encoder.getCompressedImageSize(); + dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED; return NO_ERROR; } status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_p010_image, - jr_uncompressed_ptr dest, - float &hdr_ratio) { + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest) { if (uncompressed_yuv_420_image == nullptr || uncompressed_p010_image == nullptr + || metadata == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } @@ -290,6 +372,11 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 return ERROR_JPEGR_RESOLUTION_MISMATCH; } + if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED + || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) { + return ERROR_JPEGR_INVALID_COLORGAMUT; + } + size_t image_width = uncompressed_yuv_420_image->width; size_t image_height = uncompressed_yuv_420_image->height; size_t map_width = image_width / kMapDimensionScaleFactor; @@ -297,35 +384,81 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 dest->width = map_width; dest->height = map_height; + dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED; dest->data = new uint8_t[map_width * map_height]; std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(dest->data)); - uint16_t yp_hdr_max = 0; + ColorTransformFn hdrInvOetf = nullptr; + switch (metadata->transferFunction) { + case JPEGR_TF_HLG: + hdrInvOetf = hlgInvOetf; + break; + case JPEGR_TF_PQ: + hdrInvOetf = pqInvOetf; + break; + } + + ColorTransformFn hdrGamutConversionFn = getHdrConversionFn( + uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut); + + ColorCalculationFn luminanceFn = nullptr; + switch (uncompressed_yuv_420_image->colorGamut) { + case JPEGR_COLORGAMUT_BT709: + luminanceFn = srgbLuminance; + break; + case JPEGR_COLORGAMUT_P3: + luminanceFn = p3Luminance; + break; + case JPEGR_COLORGAMUT_BT2100: + luminanceFn = bt2100Luminance; + break; + case JPEGR_COLORGAMUT_UNSPECIFIED: + // Should be impossible to hit after input validation. + return ERROR_JPEGR_INVALID_COLORGAMUT; + } + + float hdr_y_nits_max = 0.0f; + double hdr_y_nits_avg = 0.0f; for (size_t y = 0; y < image_height; ++y) { for (size_t x = 0; x < image_width; ++x) { - size_t pixel_idx = x + y * image_width; - uint16_t yp_hdr = reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->data)[pixel_idx]; - if (yp_hdr > yp_hdr_max) { - yp_hdr_max = yp_hdr; + Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y); + Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma); + Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma); + hdr_rgb = hdrGamutConversionFn(hdr_rgb); + float hdr_y_nits = luminanceFn(hdr_rgb); + + hdr_y_nits_avg += hdr_y_nits; + if (hdr_y_nits > hdr_y_nits_max) { + hdr_y_nits_max = hdr_y_nits; } } } + hdr_y_nits_avg /= image_width * image_height; - float y_hdr_max_nits = hlgInvOetf(yp_hdr_max); - hdr_ratio = y_hdr_max_nits / kSdrWhiteNits; + metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits; + if (metadata->transferFunction == JPEGR_TF_PQ) { + metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg; + metadata->hdr10Metadata.maxCLL = hdr_y_nits_max; + } for (size_t y = 0; y < map_height; ++y) { for (size_t x = 0; x < map_width; ++x) { - float yp_sdr = sampleYuv420Y(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y); - float yp_hdr = sampleP010Y(uncompressed_p010_image, kMapDimensionScaleFactor, x, y); - - float y_sdr_nits = srgbInvOetf(yp_sdr); - float y_hdr_nits = hlgInvOetf(yp_hdr); + Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image, + kMapDimensionScaleFactor, x, y); + Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma); + Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma); + float sdr_y_nits = luminanceFn(sdr_rgb); + + Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y); + Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma); + Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma); + hdr_rgb = hdrGamutConversionFn(hdr_rgb); + float hdr_y_nits = luminanceFn(hdr_rgb); size_t pixel_idx = x + y * map_width; reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] = - encodeRecovery(y_sdr_nits, y_hdr_nits, hdr_ratio); + encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor); } } @@ -335,17 +468,15 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_recovery_map, + jr_metadata_ptr metadata, jr_uncompressed_ptr dest) { if (uncompressed_yuv_420_image == nullptr || uncompressed_recovery_map == nullptr + || metadata == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } - // TODO: need to get this from the XMP; should probably be a function - // parameter - float hdr_ratio = 4.0f; - size_t width = uncompressed_yuv_420_image->width; size_t height = uncompressed_yuv_420_image->height; @@ -353,60 +484,117 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ dest->height = height; size_t pixel_count = width * height; + ColorTransformFn hdrOetf = nullptr; + switch (metadata->transferFunction) { + case JPEGR_TF_HLG: + hdrOetf = hlgOetf; + break; + case JPEGR_TF_PQ: + hdrOetf = pqOetf; + break; + } + for (size_t y = 0; y < height; ++y) { for (size_t x = 0; x < width; ++x) { - size_t pixel_y_idx = x + y * width; - - size_t pixel_uv_idx = x / 2 + (y / 2) * (width / 2); - - Color ypuv_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y); - Color rgbp_sdr = srgbYuvToRgb(ypuv_sdr); - Color rgb_sdr = srgbInvOetf(rgbp_sdr); + Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y); + Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr); + Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr); + // TODO: determine map scaling factor based on actual map dims float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y); - Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio); + Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor); - Color rgbp_hdr = hlgOetf(rgb_hdr); - Color ypuv_hdr = bt2100RgbToYuv(rgbp_hdr); + Color rgb_gamma_hdr = hdrOetf(rgb_hdr); + uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr); - reinterpret_cast<uint16_t*>(dest->data)[pixel_y_idx] = ypuv_hdr.r; - reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx] = ypuv_hdr.g; - reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx + 1] = ypuv_hdr.b; + size_t pixel_idx = x + y * width; + reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102; } } + return NO_ERROR; +} + +status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr primary_image, + jr_compressed_ptr recovery_map) { + if (compressed_jpegr_image == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + MessageHandler msg_handler; + std::shared_ptr<DataSegment> seg = + DataSegment::Create(DataRange(0, compressed_jpegr_image->length), + static_cast<const uint8_t*>(compressed_jpegr_image->data), + DataSegment::BufferDispositionPolicy::kDontDelete); + DataSegmentDataSource data_source(seg); + JpegInfoBuilder jpeg_info_builder; + jpeg_info_builder.SetImageLimit(2); + JpegScanner jpeg_scanner(&msg_handler); + jpeg_scanner.Run(&data_source, &jpeg_info_builder); + data_source.Reset(); + + if (jpeg_scanner.HasError()) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + const auto& jpeg_info = jpeg_info_builder.GetInfo(); + const auto& image_ranges = jpeg_info.GetImageRanges(); + if (image_ranges.empty()) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + if (image_ranges.size() != 2) { + // Must be 2 JPEG Images + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + if (primary_image != nullptr) { + primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) + + image_ranges[0].GetBegin(); + primary_image->length = image_ranges[0].GetLength(); + } + + if (recovery_map != nullptr) { + recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) + + image_ranges[1].GetBegin(); + recovery_map->length = image_ranges[1].GetLength(); + } return NO_ERROR; } + status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr dest) { if (compressed_jpegr_image == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD - return NO_ERROR; + return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest); } status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, jr_compressed_ptr compressed_recovery_map, - float hdr_ratio, + jr_metadata_ptr metadata, jr_compressed_ptr dest) { if (compressed_jpeg_image == nullptr || compressed_recovery_map == nullptr + || metadata == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } - string xmp = generateXmp(compressed_recovery_map->length, hdr_ratio); - string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + const string xmp = generateXmp(compressed_recovery_map->length, *metadata); + const string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator // 2 bytes: APP1 sign (ff e1) - // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0" + // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0", // x bytes: length of xmp packet - int length = 2 + nameSpace.size() + xmp.size(); - uint8_t lengthH = ((length >> 8) & 0xff); - uint8_t lengthL = (length & 0xff); + + const int length = 3 + nameSpaceLength + xmp.size(); + const uint8_t lengthH = ((length >> 8) & 0xff); + const uint8_t lengthL = (length & 0xff); int pos = 0; @@ -424,7 +612,7 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos)); JPEGR_CHECK(Write(dest, &lengthH, 1, pos)); JPEGR_CHECK(Write(dest, &lengthL, 1, pos)); - JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpace.size(), pos)); + JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos)); JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos)); JPEGR_CHECK(Write(dest, (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos)); @@ -434,7 +622,7 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return NO_ERROR; } -string RecoveryMap::generateXmp(int secondary_image_length, float hdr_ratio) { +string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) { const string kContainerPrefix = "GContainer"; const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; const string kItemPrefix = "Item"; @@ -447,7 +635,6 @@ string RecoveryMap::generateXmp(int secondary_image_length, float hdr_ratio) { const string kPrimary = "Primary"; const string kSemantic = "Semantic"; const string kVersion = "Version"; - const int kVersionValue = 1; const string kConDir = Name(kContainerPrefix, kDirectory); const string kContainerItem = Name(kContainerPrefix, kItem); @@ -467,8 +654,11 @@ string RecoveryMap::generateXmp(int secondary_image_length, float hdr_ratio) { writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); writer.StartWritingElement("rdf:Description"); writer.WriteXmlns(kContainerPrefix, kContainerUri); - writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kVersionValue); - writer.WriteElementAndContent(Name(kContainerPrefix, "HdrRatio"), hdr_ratio); + writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version); + writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"), + metadata.rangeScalingFactor); + // TODO: determine structure for hdr10 metadata + // TODO: write rest of metadata writer.StartWritingElements(kConDirSeq); size_t item_depth = writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp index 3e110bcd26..6dcbca3707 100644 --- a/libs/jpegrecoverymap/recoverymapmath.cpp +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -20,43 +20,108 @@ namespace android::recoverymap { -static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f; -static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f; +//////////////////////////////////////////////////////////////////////////////// +// sRGB transformations + +static const float kSrgbR = 0.299f, kSrgbG = 0.587f, kSrgbB = 0.114f; -Color bt2100RgbToYuv(Color e) { - float yp = kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b; - return {{{yp, (e.b - yp) / kBt2100Cb, (e.r - yp) / kBt2100Cr }}}; +float srgbLuminance(Color e) { + return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b; } static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f; -Color srgbYuvToRgb(Color e) { - return {{{ e.y + kSrgbRCr * e.v, e.y - kSrgbGCb * e.u - kSrgbGCr * e.v, e.y + kSrgbBCb * e.u }}}; +Color srgbYuvToRgb(Color e_gamma) { + return {{{ e_gamma.y + kSrgbRCr * e_gamma.v, + e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v, + e_gamma.y + kSrgbBCb * e_gamma.u }}}; +} + +static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f; +static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f; + +Color srgbRgbToYuv(Color e_gamma) { + return {{{ kSrgbR * e_gamma.r + kSrgbG * e_gamma.g + kSrgbB * e_gamma.b, + kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b, + kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}}; } -float srgbInvOetf(float e) { - if (e <= 0.04045f) { - return e / 12.92f; +float srgbInvOetf(float e_gamma) { + if (e_gamma <= 0.04045f) { + return e_gamma / 12.92f; } else { - return pow((e + 0.055f) / 1.055f, 2.4); + return pow((e_gamma + 0.055f) / 1.055f, 2.4); } } -Color srgbInvOetf(Color e) { - return {{{ srgbInvOetf(e.r), srgbInvOetf(e.g), srgbInvOetf(e.b) }}}; +Color srgbInvOetf(Color e_gamma) { + return {{{ srgbInvOetf(e_gamma.r), + srgbInvOetf(e_gamma.g), + srgbInvOetf(e_gamma.b) }}}; } -static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073; -float hlgInvOetf(float e) { - if (e <= 0.5f) { - return pow(e, 2.0f) / 3.0f; - } else { - return (exp((e - kHlgC) / kHlgA) + kHlgB) / 12.0f; - } +//////////////////////////////////////////////////////////////////////////////// +// Display-P3 transformations + +static const float kP3R = 0.22897f, kP3G = 0.69174f, kP3B = 0.07929f; + +float p3Luminance(Color e) { + return kP3R * e.r + kP3G * e.g + kP3B * e.b; } -float hlgOetf(float e) { + +//////////////////////////////////////////////////////////////////////////////// +// BT.2100 transformations - according to ITU-R BT.2100-2 + +static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f; + +float bt2100Luminance(Color e) { + return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b; +} + +static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f; + +Color bt2100RgbToYuv(Color e_gamma) { + float y_gamma = bt2100Luminance(e_gamma); + return {{{ y_gamma, + (e_gamma.b - y_gamma) / kBt2100Cb, + (e_gamma.r - y_gamma) / kBt2100Cr }}}; +} + +// Derived from the reverse of bt2100RgbToYuv. The derivation for R and B are +// pretty straight forward; we just reverse the formulas for U and V above. But +// deriving the formula for G is a bit more complicated: +// +// Start with equation for luminance: +// Y = kBt2100R * R + kBt2100G * G + kBt2100B * B +// Solve for G: +// G = (Y - kBt2100R * R - kBt2100B * B) / kBt2100B +// Substitute equations for R and B in terms YUV: +// G = (Y - kBt2100R * (Y + kBt2100Cr * V) - kBt2100B * (Y + kBt2100Cb * U)) / kBt2100B +// Simplify: +// G = Y * ((1 - kBt2100R - kBt2100B) / kBt2100G) +// + U * (kBt2100B * kBt2100Cb / kBt2100G) +// + V * (kBt2100R * kBt2100Cr / kBt2100G) +// +// We then get the following coeficients for calculating G from YUV: +// +// Coef for Y = (1 - kBt2100R - kBt2100B) / kBt2100G = 1 +// Coef for U = kBt2100B * kBt2100Cb / kBt2100G = kBt2100GCb = ~0.1645 +// Coef for V = kBt2100R * kBt2100Cr / kBt2100G = kBt2100GCr = ~0.5713 + +static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G; +static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G; + +Color bt2100YuvToRgb(Color e_gamma) { + return {{{ e_gamma.y + kBt2100Cr * e_gamma.v, + e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v, + e_gamma.y + kBt2100Cb * e_gamma.u }}}; +} + +static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073; + +static float hlgOetf(float e) { if (e <= 1.0f/12.0f) { return sqrt(3.0f * e); } else { @@ -68,7 +133,137 @@ Color hlgOetf(Color e) { return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}}; } -uint8_t EncodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { +static float hlgInvOetf(float e_gamma) { + if (e_gamma <= 0.5f) { + return pow(e_gamma, 2.0f) / 3.0f; + } else { + return (exp((e_gamma - kHlgC) / kHlgA) + kHlgB) / 12.0f; + } +} + +Color hlgInvOetf(Color e_gamma) { + return {{{ hlgInvOetf(e_gamma.r), + hlgInvOetf(e_gamma.g), + hlgInvOetf(e_gamma.b) }}}; +} + +static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f; +static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f, + kPqC3 = 2392.0f / 4096.0f * 32.0f; + +static float pqOetf(float e) { + if (e < 0.0f) e = 0.0f; + return pow((kPqC1 + kPqC2 * pow(e / 10000.0f, kPqM1)) / (1 + kPqC3 * pow(e / 10000.0f, kPqM1)), + kPqM2); +} + +Color pqOetf(Color e) { + return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}}; +} + +static float pqInvOetf(float e_gamma) { + static const float kPqInvOetfCoef = log2(-(pow(kPqM1, 1.0f / kPqM2) - kPqC1) + / (kPqC3 * pow(kPqM1, 1.0f / kPqM2) - kPqC2)); + return kPqInvOetfCoef / log2(e_gamma * 10000.0f); +} + +Color pqInvOetf(Color e_gamma) { + return {{{ pqInvOetf(e_gamma.r), + pqInvOetf(e_gamma.g), + pqInvOetf(e_gamma.b) }}}; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Color conversions + +Color bt709ToP3(Color e) { + return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b, + 0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b, + 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}}; +} + +Color bt709ToBt2100(Color e) { + return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b, + 0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b, + 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}}; +} + +Color p3ToBt709(Color e) { + return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b, + -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b, + -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}}; +} + +Color p3ToBt2100(Color e) { + return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b, + 0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b, + -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}}; +} + +Color bt2100ToBt709(Color e) { + return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b, + -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b, + -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}}; +} + +Color bt2100ToP3(Color e) { + return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b, + -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b, + 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b + }}}; +} + +// TODO: confirm we always want to convert like this before calculating +// luminance. +ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) { + switch (sdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + switch (hdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + return identityConversion; + case JPEGR_COLORGAMUT_P3: + return p3ToBt709; + case JPEGR_COLORGAMUT_BT2100: + return bt2100ToBt709; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } + break; + case JPEGR_COLORGAMUT_P3: + switch (hdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + return bt709ToP3; + case JPEGR_COLORGAMUT_P3: + return identityConversion; + case JPEGR_COLORGAMUT_BT2100: + return bt2100ToP3; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } + break; + case JPEGR_COLORGAMUT_BT2100: + switch (hdr_gamut) { + case JPEGR_COLORGAMUT_BT709: + return bt709ToBt2100; + case JPEGR_COLORGAMUT_P3: + return p3ToBt2100; + case JPEGR_COLORGAMUT_BT2100: + return identityConversion; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } + break; + case JPEGR_COLORGAMUT_UNSPECIFIED: + return nullptr; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Recovery map calculations + +uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { float gain = 1.0f; if (y_sdr > 0.0f) { gain = y_hdr / y_sdr; @@ -80,13 +275,23 @@ uint8_t EncodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f); } -float applyRecovery(float y_sdr, float recovery, float hdr_ratio) { - return exp2(log2(y_sdr) + recovery * log2(hdr_ratio)); +static float applyRecovery(float e, float recovery, float hdr_ratio) { + return exp2(log2(e) + recovery * log2(hdr_ratio)); +} + +Color applyRecovery(Color e, float recovery, float hdr_ratio) { + return {{{ applyRecovery(e.r, recovery, hdr_ratio), + applyRecovery(e.g, recovery, hdr_ratio), + applyRecovery(e.b, recovery, hdr_ratio) }}}; } // TODO: do we need something more clever for filtering either the map or images // to generate the map? +static size_t clamp(const size_t& val, const size_t& low, const size_t& high) { + return val < low ? low : (high < val ? high : val); +} + static float mapUintToFloat(uint8_t map_uint) { return (static_cast<float>(map_uint) - 127.5f) / 127.5f; } @@ -100,6 +305,11 @@ float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size size_t y_lower = static_cast<size_t>(floor(y_map)); size_t y_upper = y_lower + 1; + x_lower = clamp(x_lower, 0, map->width - 1); + x_upper = clamp(x_upper, 0, map->width - 1); + y_lower = clamp(y_lower, 0, map->height - 1); + y_upper = clamp(y_upper, 0, map->height - 1); + float x_influence = x_map - static_cast<float>(x_lower); float y_influence = y_map - static_cast<float>(y_lower); @@ -131,39 +341,52 @@ Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) { (static_cast<float>(v_uint) - 128.0f) / 255.0f }}}; } -typedef float (*sampleComponentFn)(jr_uncompressed_ptr, size_t, size_t); +Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) { + size_t pixel_count = image->width * image->height; -static float sampleComponent(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y, - sampleComponentFn sample_fn) { - float e = 0.0f; + size_t pixel_y_idx = x + y * image->width; + size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2); + + uint16_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_y_idx] + >> 6; + uint16_t u_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2] + >> 6; + uint16_t v_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2 + 1] + >> 6; + + // Conversions include taking narrow-range into account. + return {{{ static_cast<float>(y_uint) / 940.0f, + (static_cast<float>(u_uint) - 64.0f) / 940.0f - 0.5f, + (static_cast<float>(v_uint) - 64.0f) / 940.0f - 0.5f }}}; +} + +typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t); + +static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y, + getPixelFn get_pixel_fn) { + Color e = {{{ 0.0f, 0.0f, 0.0f }}}; for (size_t dy = 0; dy < map_scale_factor; ++dy) { for (size_t dx = 0; dx < map_scale_factor; ++dx) { - e += sample_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy); + e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy); } } return e / static_cast<float>(map_scale_factor * map_scale_factor); } -static float getYuv420Y(jr_uncompressed_ptr image, size_t x, size_t y) { - size_t pixel_idx = x + y * image->width; - uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_idx]; - return static_cast<float>(y_uint) / 255.0f; +Color sampleYuv420(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { + return samplePixels(image, map_scale_factor, x, y, getYuv420Pixel); } - -float sampleYuv420Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { - return sampleComponent(image, map_scale_factor, x, y, getYuv420Y); +Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { + return samplePixels(image, map_scale_factor, x, y, getP010Pixel); } -static float getP010Y(jr_uncompressed_ptr image, size_t x, size_t y) { - size_t pixel_idx = x + y * image->width; - uint8_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_idx]; - // Expecting narrow range input - return (static_cast<float>(y_uint) - 64.0f) / 960.0f; +uint32_t colorToRgba1010102(Color e_gamma) { + return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f)) + | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10) + | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20) + | (0x3 << 30); // Set alpha to 1.0 } -float sampleP010Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { - return sampleComponent(image, map_scale_factor, x, y, getP010Y); -} } // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp new file mode 100644 index 0000000000..fe46cbad91 --- /dev/null +++ b/libs/jpegrecoverymap/recoverymaputils.cpp @@ -0,0 +1,153 @@ +/* + * Copyright 2022 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 <jpegrecoverymap/recoverymaputils.h> +#include <jpegrecoverymap/recoverymap.h> +#include <image_io/xml/xml_reader.h> +#include <image_io/base/message_handler.h> +#include <image_io/xml/xml_element_rules.h> +#include <image_io/xml/xml_handler.h> +#include <image_io/xml/xml_rule.h> + +#include <string> +#include <sstream> + +using namespace photos_editing_formats::image_io; +using namespace std; + +namespace android::recoverymap { + + +// Extremely simple XML Handler - just searches for interesting elements +class XMPXmlHandler : public XmlHandler { +public: + + XMPXmlHandler() : XmlHandler() { + rangeScalingFactorState = NotStrarted; + } + + enum ParseState { + NotStrarted, + Started, + Done + }; + + virtual DataMatchResult StartElement(const XmlTokenContext& context) { + string val; + if (context.BuildTokenValue(&val)) { + if (!val.compare(rangeScalingFactorName)) { + rangeScalingFactorState = Started; + } else { + if (rangeScalingFactorState != Done) { + rangeScalingFactorState = NotStrarted; + } + } + } + return context.GetResult(); + } + + virtual DataMatchResult FinishElement(const XmlTokenContext& context) { + if (rangeScalingFactorState == Started) { + rangeScalingFactorState = Done; + } + return context.GetResult(); + } + + virtual DataMatchResult ElementContent(const XmlTokenContext& context) { + string val; + if (rangeScalingFactorState == Started) { + if (context.BuildTokenValue(&val)) { + rangeScalingFactorStr.assign(val); + } + } + return context.GetResult(); + } + + bool getRangeScalingFactor(float* scaling_factor) { + if (rangeScalingFactorState == Done) { + stringstream ss(rangeScalingFactorStr); + float val; + if (ss >> val) { + *scaling_factor = val; + return true; + } else { + return false; + } + } else { + return false; + } + } + + bool getTransferFunction(jpegr_transfer_function* transfer_function) { + *transfer_function = JPEGR_TF_HLG; + return true; + } + +private: + static const string rangeScalingFactorName; + string rangeScalingFactorStr; + ParseState rangeScalingFactorState; +}; + +const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor"; + + +bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { + string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + + if (xmp_size < nameSpace.size()+2) { + // Data too short + return false; + } + + if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) { + // Not correct namespace + return false; + } + + // Position the pointers to the start of XMP XML portion + xmp_data += nameSpace.size()+1; + xmp_size -= nameSpace.size()+1; + XMPXmlHandler handler; + + // We need to remove tail data until the closing tag. Otherwise parser will throw an error. + while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) { + xmp_size--; + } + + string str(reinterpret_cast<const char*>(xmp_data), xmp_size); + MessageHandler msg_handler; + unique_ptr<XmlRule> rule(new XmlElementRule); + XmlReader reader(&handler, &msg_handler); + reader.StartParse(std::move(rule)); + reader.Parse(str); + reader.FinishParse(); + if (reader.HasErrors()) { + // Parse error + return false; + } + + if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) { + return false; + } + + if (!handler.getTransferFunction(&metadata->transferFunction)) { + return false; + } + return true; +} + +} // namespace android::recoverymap
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp index 41af991093..8f37954841 100644 --- a/libs/jpegrecoverymap/tests/Android.bp +++ b/libs/jpegrecoverymap/tests/Android.bp @@ -27,7 +27,15 @@ cc_test { srcs: [ "recoverymap_test.cpp", ], + shared_libs: [ + "libimage_io", + "libjpeg", + "liblog", + ], static_libs: [ + "libgtest", + "libjpegdecoder", + "libjpegencoder", "libjpegrecoverymap", ], } diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp index c436138cdc..b3cd37e7e8 100644 --- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp @@ -14,9 +14,35 @@ * limitations under the License. */ +#include <gtest/gtest.h> #include <jpegrecoverymap/recoverymap.h> -namespace android { +namespace android::recoverymap { -// Add new tests here. -} // namespace android +class RecoveryMapTest : public testing::Test { +public: + RecoveryMapTest(); + ~RecoveryMapTest(); +protected: + virtual void SetUp(); + virtual void TearDown(); +}; + +RecoveryMapTest::RecoveryMapTest() {} +RecoveryMapTest::~RecoveryMapTest() {} + +void RecoveryMapTest::SetUp() {} +void RecoveryMapTest::TearDown() {} + +TEST_F(RecoveryMapTest, build) { + // Force all of the recovery map lib to be linked by calling all public functions. + RecoveryMap recovery_map; + recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), + nullptr, 0, nullptr); + recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0), + nullptr); + recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr); + recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false); +} + +} // namespace android::recoverymap diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 539cbaa341..e64165fef3 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -14,13 +14,10 @@ * limitations under the License. */ -#define LOG_TAG "Choreographer" -//#define LOG_NDEBUG 0 - #include <android-base/thread_annotations.h> #include <android/gui/ISurfaceComposer.h> -#include <gui/DisplayEventDispatcher.h> #include <jni.h> +#include <nativedisplay/Choreographer.h> #include <private/android/choreographer.h> #include <utils/Looper.h> #include <utils/Timers.h> @@ -31,444 +28,9 @@ #include <queue> #include <thread> -namespace { -struct { - // Global JVM that is provided by zygote - JavaVM* jvm = nullptr; - struct { - jclass clazz; - jmethodID getInstance; - jmethodID registerNativeChoreographerForRefreshRateCallbacks; - jmethodID unregisterNativeChoreographerForRefreshRateCallbacks; - } displayManagerGlobal; -} gJni; - -// Gets the JNIEnv* for this thread, and performs one-off initialization if we -// have never retrieved a JNIEnv* pointer before. -JNIEnv* getJniEnv() { - if (gJni.jvm == nullptr) { - ALOGW("AChoreographer: No JVM provided!"); - return nullptr; - } - - JNIEnv* env = nullptr; - if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { - ALOGD("Attaching thread to JVM for AChoreographer"); - JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL}; - jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args); - if (attachResult != JNI_OK) { - ALOGE("Unable to attach thread. Error: %d", attachResult); - return nullptr; - } - } - if (env == nullptr) { - ALOGW("AChoreographer: No JNI env available!"); - } - return env; -} - -inline const char* toString(bool value) { - return value ? "true" : "false"; -} -} // namespace - -namespace android { -using gui::VsyncEventData; - -struct FrameCallback { - AChoreographer_frameCallback callback; - AChoreographer_frameCallback64 callback64; - AChoreographer_vsyncCallback vsyncCallback; - void* data; - nsecs_t dueTime; - - inline bool operator<(const FrameCallback& rhs) const { - // Note that this is intentionally flipped because we want callbacks due sooner to be at - // the head of the queue - return dueTime > rhs.dueTime; - } -}; - -struct RefreshRateCallback { - AChoreographer_refreshRateCallback callback; - void* data; - bool firstCallbackFired = false; -}; - -class Choreographer; - -/** - * Implementation of AChoreographerFrameCallbackData. - */ -struct ChoreographerFrameCallbackDataImpl { - int64_t frameTimeNanos{0}; - - VsyncEventData vsyncEventData; - - const Choreographer* choreographer; -}; - -struct { - std::mutex lock; - std::vector<Choreographer*> ptrs GUARDED_BY(lock); - std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock); - bool registeredToDisplayManager GUARDED_BY(lock) = false; - - std::atomic<nsecs_t> mLastKnownVsync = -1; -} gChoreographers; - -class Choreographer : public DisplayEventDispatcher, public MessageHandler { -public: - explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock); - void postFrameCallbackDelayed(AChoreographer_frameCallback cb, - AChoreographer_frameCallback64 cb64, - AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay); - void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) - EXCLUDES(gChoreographers.lock); - void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); - // Drains the queue of pending vsync periods and dispatches refresh rate - // updates to callbacks. - // The assumption is that this method is only called on a single - // processing thread, either by looper or by AChoreographer_handleEvents - void handleRefreshRateUpdates(); - void scheduleLatestConfigRequest(); - - enum { - MSG_SCHEDULE_CALLBACKS = 0, - MSG_SCHEDULE_VSYNC = 1, - MSG_HANDLE_REFRESH_RATE_UPDATES = 2, - }; - virtual void handleMessage(const Message& message) override; - - static Choreographer* getForThread(); - virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); - int64_t getFrameInterval() const; - bool inCallback() const; - -private: - Choreographer(const Choreographer&) = delete; - - void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, - VsyncEventData vsyncEventData) override; - void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; - void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, - nsecs_t vsyncPeriod) override; - void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; - void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, - std::vector<FrameRateOverride> overrides) override; - - void scheduleCallbacks(); - - ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const; - void registerStartTime() const; - - std::mutex mLock; - // Protected by mLock - std::priority_queue<FrameCallback> mFrameCallbacks; - std::vector<RefreshRateCallback> mRefreshRateCallbacks; - - nsecs_t mLatestVsyncPeriod = -1; - VsyncEventData mLastVsyncEventData; - bool mInCallback = false; - - const sp<Looper> mLooper; - const std::thread::id mThreadId; - - // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway. - static constexpr size_t kMaxStartTimes = 250; -}; - -static thread_local Choreographer* gChoreographer; -Choreographer* Choreographer::getForThread() { - if (gChoreographer == nullptr) { - sp<Looper> looper = Looper::getForThread(); - if (!looper.get()) { - ALOGW("No looper prepared for thread"); - return nullptr; - } - gChoreographer = new Choreographer(looper); - status_t result = gChoreographer->initialize(); - if (result != OK) { - ALOGW("Failed to initialize"); - return nullptr; - } - } - return gChoreographer; -} - -Choreographer::Choreographer(const sp<Looper>& looper) - : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp), - mLooper(looper), - mThreadId(std::this_thread::get_id()) { - std::lock_guard<std::mutex> _l(gChoreographers.lock); - gChoreographers.ptrs.push_back(this); -} - -Choreographer::~Choreographer() { - std::lock_guard<std::mutex> _l(gChoreographers.lock); - gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), - gChoreographers.ptrs.end(), - [=](Choreographer* c) { return c == this; }), - gChoreographers.ptrs.end()); - // Only poke DisplayManagerGlobal to unregister if we previously registered - // callbacks. - if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { - gChoreographers.registeredToDisplayManager = false; - JNIEnv* env = getJniEnv(); - if (env == nullptr) { - ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); - return; - } - jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, - gJni.displayManagerGlobal.getInstance); - if (dmg == nullptr) { - ALOGW("DMS is not initialized yet, skipping choreographer cleanup"); - } else { - env->CallVoidMethod(dmg, - gJni.displayManagerGlobal - .unregisterNativeChoreographerForRefreshRateCallbacks); - env->DeleteLocalRef(dmg); - } - } -} - -void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, - AChoreographer_frameCallback64 cb64, - AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; - { - std::lock_guard<std::mutex> _l{mLock}; - mFrameCallbacks.push(callback); - } - if (callback.dueTime <= now) { - if (std::this_thread::get_id() != mThreadId) { - if (mLooper != nullptr) { - Message m{MSG_SCHEDULE_VSYNC}; - mLooper->sendMessage(this, m); - } else { - scheduleVsync(); - } - } else { - scheduleVsync(); - } - } else { - if (mLooper != nullptr) { - Message m{MSG_SCHEDULE_CALLBACKS}; - mLooper->sendMessageDelayed(delay, this, m); - } else { - scheduleCallbacks(); - } - } -} - -void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { - std::lock_guard<std::mutex> _l{mLock}; - for (const auto& callback : mRefreshRateCallbacks) { - // Don't re-add callbacks. - if (cb == callback.callback && data == callback.data) { - return; - } - } - mRefreshRateCallbacks.emplace_back( - RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false}); - bool needsRegistration = false; - { - std::lock_guard<std::mutex> _l2(gChoreographers.lock); - needsRegistration = !gChoreographers.registeredToDisplayManager; - } - if (needsRegistration) { - JNIEnv* env = getJniEnv(); - if (env == nullptr) { - ALOGW("JNI environment is unavailable, skipping registration"); - return; - } - jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, - gJni.displayManagerGlobal.getInstance); - if (dmg == nullptr) { - ALOGW("DMS is not initialized yet: skipping registration"); - return; - } else { - env->CallVoidMethod(dmg, - gJni.displayManagerGlobal - .registerNativeChoreographerForRefreshRateCallbacks, - reinterpret_cast<int64_t>(this)); - env->DeleteLocalRef(dmg); - { - std::lock_guard<std::mutex> _l2(gChoreographers.lock); - gChoreographers.registeredToDisplayManager = true; - } - } - } else { - scheduleLatestConfigRequest(); - } -} - -void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, - void* data) { - std::lock_guard<std::mutex> _l{mLock}; - mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), - mRefreshRateCallbacks.end(), - [&](const RefreshRateCallback& callback) { - return cb == callback.callback && - data == callback.data; - }), - mRefreshRateCallbacks.end()); -} - -void Choreographer::scheduleLatestConfigRequest() { - if (mLooper != nullptr) { - Message m{MSG_HANDLE_REFRESH_RATE_UPDATES}; - mLooper->sendMessage(this, m); - } else { - // If the looper thread is detached from Choreographer, then refresh rate - // changes will be handled in AChoreographer_handlePendingEvents, so we - // need to wake up the looper thread by writing to the write-end of the - // socket the looper is listening on. - // Fortunately, these events are small so sending packets across the - // socket should be atomic across processes. - DisplayEventReceiver::Event event; - event.header = - DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, - PhysicalDisplayId::fromPort(0), systemTime()}; - injectEvent(event); - } -} - -void Choreographer::scheduleCallbacks() { - const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - nsecs_t dueTime; - { - std::lock_guard<std::mutex> _l{mLock}; - // If there are no pending callbacks then don't schedule a vsync - if (mFrameCallbacks.empty()) { - return; - } - dueTime = mFrameCallbacks.top().dueTime; - } - - if (dueTime <= now) { - ALOGV("choreographer %p ~ scheduling vsync", this); - scheduleVsync(); - return; - } -} - -void Choreographer::handleRefreshRateUpdates() { - std::vector<RefreshRateCallback> callbacks{}; - const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); - const nsecs_t lastPeriod = mLatestVsyncPeriod; - if (pendingPeriod > 0) { - mLatestVsyncPeriod = pendingPeriod; - } - { - std::lock_guard<std::mutex> _l{mLock}; - for (auto& cb : mRefreshRateCallbacks) { - callbacks.push_back(cb); - cb.firstCallbackFired = true; - } - } - - for (auto& cb : callbacks) { - if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) { - cb.callback(pendingPeriod, cb.data); - } - } -} - -// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the -// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for -// the internal display implicitly. -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, - VsyncEventData vsyncEventData) { - std::vector<FrameCallback> callbacks{}; - { - std::lock_guard<std::mutex> _l{mLock}; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { - callbacks.push_back(mFrameCallbacks.top()); - mFrameCallbacks.pop(); - } - } - mLastVsyncEventData = vsyncEventData; - for (const auto& cb : callbacks) { - if (cb.vsyncCallback != nullptr) { - const ChoreographerFrameCallbackDataImpl frameCallbackData = - createFrameCallbackData(timestamp); - registerStartTime(); - mInCallback = true; - cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>( - &frameCallbackData), - cb.data); - mInCallback = false; - } else if (cb.callback64 != nullptr) { - cb.callback64(timestamp, cb.data); - } else if (cb.callback != nullptr) { - cb.callback(timestamp, cb.data); - } - } -} - -void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, - to_string(displayId).c_str(), toString(connected)); -} - -void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { - LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered"); -} - -void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId, - std::vector<FrameRateOverride>) { - LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered"); -} - -void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { - ALOGV("choreographer %p ~ received null event.", this); - handleRefreshRateUpdates(); -} - -void Choreographer::handleMessage(const Message& message) { - switch (message.what) { - case MSG_SCHEDULE_CALLBACKS: - scheduleCallbacks(); - break; - case MSG_SCHEDULE_VSYNC: - scheduleVsync(); - break; - case MSG_HANDLE_REFRESH_RATE_UPDATES: - handleRefreshRateUpdates(); - break; - } -} - -int64_t Choreographer::getFrameInterval() const { - return mLastVsyncEventData.frameInterval; -} - -bool Choreographer::inCallback() const { - return mInCallback; -} - -ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { - return {.frameTimeNanos = timestamp, - .vsyncEventData = mLastVsyncEventData, - .choreographer = this}; -} - -void Choreographer::registerStartTime() const { - std::scoped_lock _l(gChoreographers.lock); - for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) { - while (gChoreographers.startTimes.size() >= kMaxStartTimes) { - gChoreographers.startTimes.erase(gChoreographers.startTimes.begin()); - } - gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC); - } -} +#undef LOG_TAG +#define LOG_TAG "AChoreographer" -} // namespace android using namespace android; static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { @@ -488,27 +50,12 @@ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl( // Glue for private C api namespace android { -void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { - std::lock_guard<std::mutex> _l(gChoreographers.lock); - gChoreographers.mLastKnownVsync.store(vsyncPeriod); - for (auto c : gChoreographers.ptrs) { - c->scheduleLatestConfigRequest(); - } +void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) { + Choreographer::signalRefreshRateCallbacks(vsyncPeriod); } void AChoreographer_initJVM(JNIEnv* env) { - env->GetJavaVM(&gJni.jvm); - // Now we need to find the java classes. - jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal"); - gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass)); - gJni.displayManagerGlobal.getInstance = - env->GetStaticMethodID(dmgClass, "getInstance", - "()Landroid/hardware/display/DisplayManagerGlobal;"); - gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks = - env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V"); - gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks = - env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks", - "()V"); + Choreographer::initJVM(env); } AChoreographer* AChoreographer_routeGetInstance() { @@ -583,13 +130,7 @@ int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) { } int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) { - std::scoped_lock _l(gChoreographers.lock); - const auto iter = gChoreographers.startTimes.find(vsyncId); - if (iter == gChoreographers.startTimes.end()) { - ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId); - return 0; - } - return iter->second; + return Choreographer::getStartTimeNanosForVsyncId(vsyncId); } } // namespace android diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 8d8a2bc244..70de33da12 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -56,6 +56,7 @@ cc_library_shared { ":libgui_frame_event_aidl", "AChoreographer.cpp", "ADisplay.cpp", + "Choreographer.cpp", "surfacetexture/surface_texture.cpp", "surfacetexture/SurfaceTexture.cpp", "surfacetexture/ImageConsumer.cpp", diff --git a/libs/nativedisplay/Choreographer.cpp b/libs/nativedisplay/Choreographer.cpp new file mode 100644 index 0000000000..01e9f04d15 --- /dev/null +++ b/libs/nativedisplay/Choreographer.cpp @@ -0,0 +1,390 @@ +/* + * Copyright 2022 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. + */ + +// #define LOG_NDEBUG 0 + +#include <jni.h> +#include <nativedisplay/Choreographer.h> + +#undef LOG_TAG +#define LOG_TAG "AChoreographer" + +namespace { +struct { + // Global JVM that is provided by zygote + JavaVM* jvm = nullptr; + struct { + jclass clazz; + jmethodID getInstance; + jmethodID registerNativeChoreographerForRefreshRateCallbacks; + jmethodID unregisterNativeChoreographerForRefreshRateCallbacks; + } displayManagerGlobal; +} gJni; + +// Gets the JNIEnv* for this thread, and performs one-off initialization if we +// have never retrieved a JNIEnv* pointer before. +JNIEnv* getJniEnv() { + if (gJni.jvm == nullptr) { + ALOGW("AChoreographer: No JVM provided!"); + return nullptr; + } + + JNIEnv* env = nullptr; + if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + ALOGD("Attaching thread to JVM for AChoreographer"); + JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL}; + jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args); + if (attachResult != JNI_OK) { + ALOGE("Unable to attach thread. Error: %d", attachResult); + return nullptr; + } + } + if (env == nullptr) { + ALOGW("AChoreographer: No JNI env available!"); + } + return env; +} + +inline const char* toString(bool value) { + return value ? "true" : "false"; +} +} // namespace + +namespace android { + +Choreographer::Context Choreographer::gChoreographers; + +static thread_local Choreographer* gChoreographer; + +void Choreographer::initJVM(JNIEnv* env) { + env->GetJavaVM(&gJni.jvm); + // Now we need to find the java classes. + jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal"); + gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass)); + gJni.displayManagerGlobal.getInstance = + env->GetStaticMethodID(dmgClass, "getInstance", + "()Landroid/hardware/display/DisplayManagerGlobal;"); + gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V"); + gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks", + "()V"); +} + +Choreographer* Choreographer::getForThread() { + if (gChoreographer == nullptr) { + sp<Looper> looper = Looper::getForThread(); + if (!looper.get()) { + ALOGW("No looper prepared for thread"); + return nullptr; + } + gChoreographer = new Choreographer(looper); + status_t result = gChoreographer->initialize(); + if (result != OK) { + ALOGW("Failed to initialize"); + return nullptr; + } + } + return gChoreographer; +} + +Choreographer::Choreographer(const sp<Looper>& looper) + : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp), + mLooper(looper), + mThreadId(std::this_thread::get_id()) { + std::lock_guard<std::mutex> _l(gChoreographers.lock); + gChoreographers.ptrs.push_back(this); +} + +Choreographer::~Choreographer() { + std::lock_guard<std::mutex> _l(gChoreographers.lock); + gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), + gChoreographers.ptrs.end(), + [=](Choreographer* c) { return c == this; }), + gChoreographers.ptrs.end()); + // Only poke DisplayManagerGlobal to unregister if we previously registered + // callbacks. + if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { + gChoreographers.registeredToDisplayManager = false; + JNIEnv* env = getJniEnv(); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); + return; + } + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet, skipping choreographer cleanup"); + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .unregisterNativeChoreographerForRefreshRateCallbacks); + env->DeleteLocalRef(dmg); + } + } +} + +void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, + AChoreographer_frameCallback64 cb64, + AChoreographer_vsyncCallback vsyncCallback, void* data, + nsecs_t delay) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; + { + std::lock_guard<std::mutex> _l{mLock}; + mFrameCallbacks.push(callback); + } + if (callback.dueTime <= now) { + if (std::this_thread::get_id() != mThreadId) { + if (mLooper != nullptr) { + Message m{MSG_SCHEDULE_VSYNC}; + mLooper->sendMessage(this, m); + } else { + scheduleVsync(); + } + } else { + scheduleVsync(); + } + } else { + if (mLooper != nullptr) { + Message m{MSG_SCHEDULE_CALLBACKS}; + mLooper->sendMessageDelayed(delay, this, m); + } else { + scheduleCallbacks(); + } + } +} + +void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { + std::lock_guard<std::mutex> _l{mLock}; + for (const auto& callback : mRefreshRateCallbacks) { + // Don't re-add callbacks. + if (cb == callback.callback && data == callback.data) { + return; + } + } + mRefreshRateCallbacks.emplace_back( + RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false}); + bool needsRegistration = false; + { + std::lock_guard<std::mutex> _l2(gChoreographers.lock); + needsRegistration = !gChoreographers.registeredToDisplayManager; + } + if (needsRegistration) { + JNIEnv* env = getJniEnv(); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping registration"); + return; + } + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet: skipping registration"); + return; + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .registerNativeChoreographerForRefreshRateCallbacks, + reinterpret_cast<int64_t>(this)); + env->DeleteLocalRef(dmg); + { + std::lock_guard<std::mutex> _l2(gChoreographers.lock); + gChoreographers.registeredToDisplayManager = true; + } + } + } else { + scheduleLatestConfigRequest(); + } +} + +void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, + void* data) { + std::lock_guard<std::mutex> _l{mLock}; + mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), + mRefreshRateCallbacks.end(), + [&](const RefreshRateCallback& callback) { + return cb == callback.callback && + data == callback.data; + }), + mRefreshRateCallbacks.end()); +} + +void Choreographer::scheduleLatestConfigRequest() { + if (mLooper != nullptr) { + Message m{MSG_HANDLE_REFRESH_RATE_UPDATES}; + mLooper->sendMessage(this, m); + } else { + // If the looper thread is detached from Choreographer, then refresh rate + // changes will be handled in AChoreographer_handlePendingEvents, so we + // need to wake up the looper thread by writing to the write-end of the + // socket the looper is listening on. + // Fortunately, these events are small so sending packets across the + // socket should be atomic across processes. + DisplayEventReceiver::Event event; + event.header = + DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, + PhysicalDisplayId::fromPort(0), systemTime()}; + injectEvent(event); + } +} + +void Choreographer::scheduleCallbacks() { + const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t dueTime; + { + std::lock_guard<std::mutex> _l{mLock}; + // If there are no pending callbacks then don't schedule a vsync + if (mFrameCallbacks.empty()) { + return; + } + dueTime = mFrameCallbacks.top().dueTime; + } + + if (dueTime <= now) { + ALOGV("choreographer %p ~ scheduling vsync", this); + scheduleVsync(); + return; + } +} + +void Choreographer::handleRefreshRateUpdates() { + std::vector<RefreshRateCallback> callbacks{}; + const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); + const nsecs_t lastPeriod = mLatestVsyncPeriod; + if (pendingPeriod > 0) { + mLatestVsyncPeriod = pendingPeriod; + } + { + std::lock_guard<std::mutex> _l{mLock}; + for (auto& cb : mRefreshRateCallbacks) { + callbacks.push_back(cb); + cb.firstCallbackFired = true; + } + } + + for (auto& cb : callbacks) { + if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) { + cb.callback(pendingPeriod, cb.data); + } + } +} + +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, + VsyncEventData vsyncEventData) { + std::vector<FrameCallback> callbacks{}; + { + std::lock_guard<std::mutex> _l{mLock}; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { + callbacks.push_back(mFrameCallbacks.top()); + mFrameCallbacks.pop(); + } + } + mLastVsyncEventData = vsyncEventData; + for (const auto& cb : callbacks) { + if (cb.vsyncCallback != nullptr) { + const ChoreographerFrameCallbackDataImpl frameCallbackData = + createFrameCallbackData(timestamp); + registerStartTime(); + mInCallback = true; + cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>( + &frameCallbackData), + cb.data); + mInCallback = false; + } else if (cb.callback64 != nullptr) { + cb.callback64(timestamp, cb.data); + } else if (cb.callback != nullptr) { + cb.callback(timestamp, cb.data); + } + } +} + +void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { + ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, + to_string(displayId).c_str(), toString(connected)); +} + +void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { + LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered"); +} + +void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId, + std::vector<FrameRateOverride>) { + LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered"); +} + +void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { + ALOGV("choreographer %p ~ received null event.", this); + handleRefreshRateUpdates(); +} + +void Choreographer::handleMessage(const Message& message) { + switch (message.what) { + case MSG_SCHEDULE_CALLBACKS: + scheduleCallbacks(); + break; + case MSG_SCHEDULE_VSYNC: + scheduleVsync(); + break; + case MSG_HANDLE_REFRESH_RATE_UPDATES: + handleRefreshRateUpdates(); + break; + } +} + +int64_t Choreographer::getFrameInterval() const { + return mLastVsyncEventData.frameInterval; +} + +bool Choreographer::inCallback() const { + return mInCallback; +} + +ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { + return {.frameTimeNanos = timestamp, + .vsyncEventData = mLastVsyncEventData, + .choreographer = this}; +} + +void Choreographer::registerStartTime() const { + std::scoped_lock _l(gChoreographers.lock); + for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) { + while (gChoreographers.startTimes.size() >= kMaxStartTimes) { + gChoreographers.startTimes.erase(gChoreographers.startTimes.begin()); + } + gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC); + } +} + +void Choreographer::signalRefreshRateCallbacks(nsecs_t vsyncPeriod) { + std::lock_guard<std::mutex> _l(gChoreographers.lock); + gChoreographers.mLastKnownVsync.store(vsyncPeriod); + for (auto c : gChoreographers.ptrs) { + c->scheduleLatestConfigRequest(); + } +} + +int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) { + std::scoped_lock _l(gChoreographers.lock); + const auto iter = gChoreographers.startTimes.find(vsyncId); + if (iter == gChoreographers.startTimes.end()) { + ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId); + return 0; + } + return iter->second; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/nativedisplay/include/nativedisplay/Choreographer.h b/libs/nativedisplay/include/nativedisplay/Choreographer.h new file mode 100644 index 0000000000..bb63f291f9 --- /dev/null +++ b/libs/nativedisplay/include/nativedisplay/Choreographer.h @@ -0,0 +1,138 @@ +/* + * Copyright 2022 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 <gui/DisplayEventDispatcher.h> +#include <private/android/choreographer.h> +#include <utils/Looper.h> + +#include <mutex> +#include <queue> +#include <thread> + +namespace android { +using gui::VsyncEventData; + +struct FrameCallback { + AChoreographer_frameCallback callback; + AChoreographer_frameCallback64 callback64; + AChoreographer_vsyncCallback vsyncCallback; + void* data; + nsecs_t dueTime; + + inline bool operator<(const FrameCallback& rhs) const { + // Note that this is intentionally flipped because we want callbacks due sooner to be at + // the head of the queue + return dueTime > rhs.dueTime; + } +}; + +struct RefreshRateCallback { + AChoreographer_refreshRateCallback callback; + void* data; + bool firstCallbackFired = false; +}; + +class Choreographer; + +/** + * Implementation of AChoreographerFrameCallbackData. + */ +struct ChoreographerFrameCallbackDataImpl { + int64_t frameTimeNanos{0}; + + VsyncEventData vsyncEventData; + + const Choreographer* choreographer; +}; + +class Choreographer : public DisplayEventDispatcher, public MessageHandler { +public: + struct Context { + std::mutex lock; + std::vector<Choreographer*> ptrs GUARDED_BY(lock); + std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock); + bool registeredToDisplayManager GUARDED_BY(lock) = false; + + std::atomic<nsecs_t> mLastKnownVsync = -1; + }; + static Context gChoreographers; + + explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock); + void postFrameCallbackDelayed(AChoreographer_frameCallback cb, + AChoreographer_frameCallback64 cb64, + AChoreographer_vsyncCallback vsyncCallback, void* data, + nsecs_t delay); + void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) + EXCLUDES(gChoreographers.lock); + void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + // Drains the queue of pending vsync periods and dispatches refresh rate + // updates to callbacks. + // The assumption is that this method is only called on a single + // processing thread, either by looper or by AChoreographer_handleEvents + void handleRefreshRateUpdates(); + void scheduleLatestConfigRequest(); + + enum { + MSG_SCHEDULE_CALLBACKS = 0, + MSG_SCHEDULE_VSYNC = 1, + MSG_HANDLE_REFRESH_RATE_UPDATES = 2, + }; + virtual void handleMessage(const Message& message) override; + + static void initJVM(JNIEnv* env); + static Choreographer* getForThread(); + static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock); + static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock); + virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); + int64_t getFrameInterval() const; + bool inCallback() const; + +private: + Choreographer(const Choreographer&) = delete; + + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, + VsyncEventData vsyncEventData) override; + void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; + void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, + nsecs_t vsyncPeriod) override; + void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; + void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, + std::vector<FrameRateOverride> overrides) override; + + void scheduleCallbacks(); + + ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const; + void registerStartTime() const; + + std::mutex mLock; + // Protected by mLock + std::priority_queue<FrameCallback> mFrameCallbacks; + std::vector<RefreshRateCallback> mRefreshRateCallbacks; + + nsecs_t mLatestVsyncPeriod = -1; + VsyncEventData mLastVsyncEventData; + bool mInCallback = false; + + const sp<Looper> mLooper; + const std::thread::id mThreadId; + + // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway. + static constexpr size_t kMaxStartTimes = 250; +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 054053898c..04e24ed9ed 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -42,6 +42,7 @@ cc_defaults { "libsync", "libui", "libutils", + "libvulkan", ], static_libs: [ @@ -97,6 +98,7 @@ filegroup { "skia/ColorSpaces.cpp", "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", + "skia/SkiaVkRenderEngine.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index f1fc0a45ad..341c011dc9 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -23,6 +23,7 @@ #include "threaded/RenderEngineThreaded.h" #include "skia/SkiaGLRenderEngine.h" +#include "skia/SkiaVkRenderEngine.h" namespace android { namespace renderengine { @@ -37,6 +38,13 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg case RenderEngineType::SKIA_GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); + case RenderEngineType::SKIA_VK: +#ifdef RE_SKIAVK + ALOGD("RenderEngine with SkiaVK Backend"); + return renderengine::skia::SkiaVkRenderEngine::create(args); +#else + LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!"); +#endif case RenderEngineType::SKIA_GL_THREADED: { ALOGD("Threaded RenderEngine with SkiaGL Backend"); return renderengine::threaded::RenderEngineThreaded::create( @@ -45,6 +53,17 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg }, args.renderEngineType); } + case RenderEngineType::SKIA_VK_THREADED: +#ifdef RE_SKIAVK + ALOGD("Threaded RenderEngine with SkiaVK Backend"); + return renderengine::threaded::RenderEngineThreaded::create( + [args]() { + return android::renderengine::skia::SkiaVkRenderEngine::create(args); + }, + args.renderEngineType); +#else + LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!"); +#endif case RenderEngineType::GLES: default: ALOGD("RenderEngine with GLES Backend"); diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index d44eb463f7..bd7b617ae7 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -39,6 +39,10 @@ std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { return "skiaglthreaded"; case RenderEngine::RenderEngineType::SKIA_GL: return "skiagl"; + case RenderEngine::RenderEngineType::SKIA_VK: + return "skiavk"; + case RenderEngine::RenderEngineType::SKIA_VK_THREADED: + return "skiavkthreaded"; case RenderEngine::RenderEngineType::GLES: case RenderEngine::RenderEngineType::THREADED: LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?"); diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 9182febbe0..39621cd080 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -99,6 +99,8 @@ public: THREADED = 2, SKIA_GL = 3, SKIA_GL_THREADED = 4, + SKIA_VK = 5, + SKIA_VK_THREADED = 6, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); @@ -170,9 +172,16 @@ public: virtual void cleanupPostRender() = 0; virtual void cleanFramebufferCache() = 0; - // Returns the priority this context was actually created with. Note: this may not be - // the same as specified at context creation time, due to implementation limits on the - // number of contexts that can be created at a specific priority level in the system. + + // Returns the priority this context was actually created with. Note: this + // may not be the same as specified at context creation time, due to + // implementation limits on the number of contexts that can be created at a + // specific priority level in the system. + // + // This should return a valid EGL context priority enum as described by + // https://registry.khronos.org/EGL/extensions/IMG/EGL_IMG_context_priority.txt + // or + // https://registry.khronos.org/EGL/extensions/NV/EGL_NV_context_priority_realtime.txt virtual int getContextPriority() = 0; // Returns true if blur was requested in the RenderEngineCreationArgs and the implementation diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 347b8b7856..ff598e7ab5 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -193,9 +193,9 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( } // initialize the renderer while GL is current - std::unique_ptr<SkiaGLRenderEngine> engine = - std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext, - protectedPlaceholder); + std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt, + placeholder, protectedContext, + protectedPlaceholder)); engine->ensureGrContextsCreated(); ALOGI("OpenGL ES informations:"); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 4a37ffee83..af3311041d 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -52,9 +52,6 @@ namespace skia { class SkiaGLRenderEngine : public skia::SkiaRenderEngine { public: static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, - EGLSurface placeholder, EGLContext protectedContext, - EGLSurface protectedPlaceholder); ~SkiaGLRenderEngine() override; int getContextPriority() override; @@ -70,6 +67,9 @@ protected: void appendBackendSpecificInfoToDump(std::string& result) override; private: + SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, + EGLSurface placeholder, EGLContext protectedContext, + EGLSurface protectedPlaceholder); bool waitGpuFence(base::borrowed_fd fenceFd); base::unique_fd flush(); static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index fca6c0e486..413811ef99 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -388,9 +388,11 @@ void SkiaRenderEngine::ensureGrContextsCreated() { void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { - // Only run this if RE is running on its own thread. This way the access to GL - // operations is guaranteed to be happening on the same thread. - if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) { + // Only run this if RE is running on its own thread. This + // way the access to GL operations is guaranteed to be happening on the + // same thread. + if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED && + mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) { return; } // We currently don't attempt to map a buffer if the buffer contains protected content diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp new file mode 100644 index 0000000000..2b8495c3f7 --- /dev/null +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -0,0 +1,680 @@ +/* + * Copyright 2022 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. + */ + +// Allow the SkiaVkRenderEngine class to not be compiled, to save space +// NOTE: In order to build this class, define `RE_SKIAVK` in a build file. +#ifdef RE_SKIAVK + +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "SkiaVkRenderEngine.h" + +#include <GrBackendSemaphore.h> +#include <GrContextOptions.h> +#include <vk/GrVkExtensions.h> +#include <vk/GrVkTypes.h> + +#include <android-base/stringprintf.h> +#include <gui/TraceUtils.h> +#include <sync/sync.h> +#include <utils/Trace.h> + +#include <cstdint> +#include <memory> +#include <vector> + +#include <vulkan/vulkan.h> +#include "log/log_main.h" + +namespace android { +namespace renderengine { + +struct VulkanFuncs { + PFN_vkCreateSemaphore vkCreateSemaphore = nullptr; + PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; + PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr; + PFN_vkDestroySemaphore vkDestroySemaphore = nullptr; + + PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr; + PFN_vkDestroyDevice vkDestroyDevice = nullptr; + PFN_vkDestroyInstance vkDestroyInstance = nullptr; +}; + +struct VulkanInterface { + bool initialized = false; + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkDevice device; + VkQueue queue; + int queueIndex; + uint32_t apiVersion; + GrVkExtensions grExtensions; + VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; + VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; + VkPhysicalDeviceProtectedMemoryProperties* protectedMemoryFeatures = nullptr; + GrVkGetProc grGetProc; + bool isProtected; + bool isRealtimePriority; + + VulkanFuncs funcs; + + std::vector<std::string> instanceExtensionNames; + std::vector<std::string> deviceExtensionNames; + + GrVkBackendContext getBackendContext() { + GrVkBackendContext backendContext; + backendContext.fInstance = instance; + backendContext.fPhysicalDevice = physicalDevice; + backendContext.fDevice = device; + backendContext.fQueue = queue; + backendContext.fGraphicsQueueIndex = queueIndex; + backendContext.fMaxAPIVersion = apiVersion; + backendContext.fVkExtensions = &grExtensions; + backendContext.fDeviceFeatures2 = physicalDeviceFeatures2; + backendContext.fGetProc = grGetProc; + backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo; + return backendContext; + }; + + VkSemaphore createExportableSemaphore() { + VkExportSemaphoreCreateInfo exportInfo; + exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + exportInfo.pNext = nullptr; + exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = &exportInfo; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create semaphore. err %d\n", __func__, err); + return VK_NULL_HANDLE; + } + + return semaphore; + } + + // syncFd cannot be <= 0 + VkSemaphore importSemaphoreFromSyncFd(int syncFd) { + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create import semaphore", __func__); + return VK_NULL_HANDLE; + } + + VkImportSemaphoreFdInfoKHR importInfo; + importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + importInfo.pNext = nullptr; + importInfo.semaphore = semaphore; + importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; + importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + importInfo.fd = syncFd; + + err = funcs.vkImportSemaphoreFdKHR(device, &importInfo); + if (VK_SUCCESS != err) { + funcs.vkDestroySemaphore(device, semaphore, nullptr); + ALOGE("%s: failed to import semaphore", __func__); + return VK_NULL_HANDLE; + } + + return semaphore; + } + + int exportSemaphoreSyncFd(VkSemaphore semaphore) { + int res; + + VkSemaphoreGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.semaphore = semaphore; + getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to export semaphore, err: %d", __func__, err); + return -1; + } + return res; + } + + void destroySemaphore(VkSemaphore semaphore) { + funcs.vkDestroySemaphore(device, semaphore, nullptr); + } +}; + +static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); +}; + +#define BAIL(fmt, ...) \ + { \ + ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \ + return interface; \ + } + +#define CHECK_NONNULL(expr) \ + if ((expr) == nullptr) { \ + BAIL("[%s] null", #expr); \ + } + +#define VK_CHECK(expr) \ + if ((expr) != VK_SUCCESS) { \ + BAIL("[%s] failed. err = %d", #expr, expr); \ + return interface; \ + } + +#define VK_GET_PROC(F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_INST_PROC(instance, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_DEV_PROC(device, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \ + CHECK_NONNULL(vk##F) + +VulkanInterface initVulkanInterface(bool protectedContent = false) { + VulkanInterface interface; + + VK_GET_PROC(EnumerateInstanceVersion); + uint32_t instanceVersion; + VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion)); + + if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { + return interface; + } + + const VkApplicationInfo appInfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0, + VK_MAKE_VERSION(1, 1, 0), + }; + + VK_GET_PROC(EnumerateInstanceExtensionProperties); + + uint32_t extensionCount = 0; + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr)); + std::vector<VkExtensionProperties> instanceExtensions(extensionCount); + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, + instanceExtensions.data())); + std::vector<const char*> enabledInstanceExtensionNames; + enabledInstanceExtensionNames.reserve(instanceExtensions.size()); + interface.instanceExtensionNames.reserve(instanceExtensions.size()); + for (const auto& instExt : instanceExtensions) { + enabledInstanceExtensionNames.push_back(instExt.extensionName); + interface.instanceExtensionNames.push_back(instExt.extensionName); + } + + const VkInstanceCreateInfo instanceCreateInfo = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &appInfo, + 0, + nullptr, + (uint32_t)enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), + }; + + VK_GET_PROC(CreateInstance); + VkInstance instance; + VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + + VK_GET_INST_PROC(instance, DestroyInstance); + interface.funcs.vkDestroyInstance = vkDestroyInstance; + VK_GET_INST_PROC(instance, EnumeratePhysicalDevices); + VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2); + VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2); + VK_GET_INST_PROC(instance, CreateDevice); + + uint32_t physdevCount; + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr)); + if (physdevCount == 0) { + BAIL("Could not find any physical devices"); + } + + physdevCount = 1; + VkPhysicalDevice physicalDevice; + VkResult enumeratePhysDevsErr = + vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice); + if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) { + BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d", + enumeratePhysDevsErr); + } + + VkPhysicalDeviceProperties2 physDevProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + 0, + {}, + }; + VkPhysicalDeviceProtectedMemoryProperties protMemProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, + 0, + {}, + }; + + if (protectedContent) { + physDevProps.pNext = &protMemProps; + } + + vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps); + if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { + BAIL("Could not find a Vulkan 1.1+ physical device"); + } + + // Check for syncfd support. Bail if we cannot both import and export them. + VkPhysicalDeviceExternalSemaphoreInfo semInfo = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + nullptr, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkExternalSemaphoreProperties semProps = { + VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0, + }; + vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps); + + bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes & + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + + if (!sufficientSemaphoreSyncFdSupport) { + BAIL("Vulkan device does not support sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } else { + ALOGD("Vulkan device supports sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } + + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, nullptr); + if (queueCount == 0) { + BAIL("Could not find queues for physical device"); + } + + std::vector<VkQueueFamilyProperties> queueProps(queueCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); + + int graphicsQueueIndex = -1; + for (uint32_t i = 0; i < queueCount; ++i) { + if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + graphicsQueueIndex = i; + break; + } + } + + if (graphicsQueueIndex == -1) { + BAIL("Could not find a graphics queue family"); + } + + uint32_t deviceExtensionCount; + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + nullptr)); + std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount); + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + deviceExtensions.data())); + + std::vector<const char*> enabledDeviceExtensionNames; + enabledDeviceExtensionNames.reserve(deviceExtensions.size()); + interface.deviceExtensionNames.reserve(deviceExtensions.size()); + for (const auto& devExt : deviceExtensions) { + enabledDeviceExtensionNames.push_back(devExt.extensionName); + interface.deviceExtensionNames.push_back(devExt.extensionName); + } + + interface.grExtensions.init(sGetProc, instance, physicalDevice, + enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), + enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data()); + + if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + BAIL("Vulkan driver doesn't support external semaphore fd"); + } + + interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2; + interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + interface.physicalDeviceFeatures2->pNext = nullptr; + + interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures; + interface.samplerYcbcrConversionFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + interface.samplerYcbcrConversionFeatures->pNext = nullptr; + + interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures; + void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext; + + if (protectedContent) { + interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryProperties; + interface.protectedMemoryFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; + interface.protectedMemoryFeatures->pNext = nullptr; + *tailPnext = interface.protectedMemoryFeatures; + tailPnext = &interface.protectedMemoryFeatures->pNext; + } + + vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2); + // Looks like this would slow things down and we can't depend on it on all platforms + interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; + + float queuePriorities[1] = {0.0f}; + void* queueNextPtr = nullptr; + + VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, + nullptr, + // If queue priority is supported, RE should always have realtime priority. + VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT, + }; + + if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + queueNextPtr = &queuePriorityCreateInfo; + interface.isRealtimePriority = true; + } + + VkDeviceQueueCreateFlags deviceQueueCreateFlags = + (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0); + + const VkDeviceQueueCreateInfo queueInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueNextPtr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, + 1, + queuePriorities, + }; + + const VkDeviceCreateInfo deviceInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + interface.physicalDeviceFeatures2, + 0, + 1, + &queueInfo, + 0, + nullptr, + (uint32_t)enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data(), + nullptr, + }; + + ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent); + VkDevice device; + VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); + ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent); + + VkQueue graphicsQueue; + VK_GET_DEV_PROC(device, GetDeviceQueue); + vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue); + + VK_GET_DEV_PROC(device, DeviceWaitIdle); + VK_GET_DEV_PROC(device, DestroyDevice); + interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle; + interface.funcs.vkDestroyDevice = vkDestroyDevice; + + VK_GET_DEV_PROC(device, CreateSemaphore); + VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR); + VK_GET_DEV_PROC(device, GetSemaphoreFdKHR); + VK_GET_DEV_PROC(device, DestroySemaphore); + interface.funcs.vkCreateSemaphore = vkCreateSemaphore; + interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR; + interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR; + interface.funcs.vkDestroySemaphore = vkDestroySemaphore; + + // At this point, everything's succeeded and we can continue + interface.initialized = true; + interface.instance = instance; + interface.physicalDevice = physicalDevice; + interface.device = device; + interface.queue = graphicsQueue; + interface.queueIndex = graphicsQueueIndex; + interface.apiVersion = physDevProps.properties.apiVersion; + // grExtensions already constructed + // feature pointers already constructed + interface.grGetProc = sGetProc; + interface.isProtected = protectedContent; + // funcs already initialized + + ALOGD("%s: Success init Vulkan interface", __func__); + return interface; +} + +void teardownVulkanInterface(VulkanInterface* interface) { + interface->initialized = false; + + if (interface->device != VK_NULL_HANDLE) { + interface->funcs.vkDeviceWaitIdle(interface->device); + interface->funcs.vkDestroyDevice(interface->device, nullptr); + interface->device = VK_NULL_HANDLE; + } + if (interface->instance != VK_NULL_HANDLE) { + interface->funcs.vkDestroyInstance(interface->instance, nullptr); + interface->instance = VK_NULL_HANDLE; + } + + if (interface->protectedMemoryFeatures) { + delete interface->protectedMemoryFeatures; + } + + if (interface->samplerYcbcrConversionFeatures) { + delete interface->samplerYcbcrConversionFeatures; + } + + if (interface->physicalDeviceFeatures2) { + delete interface->physicalDeviceFeatures2; + } + + interface->samplerYcbcrConversionFeatures = nullptr; + interface->physicalDeviceFeatures2 = nullptr; + interface->protectedMemoryFeatures = nullptr; +} + +static VulkanInterface sVulkanInterface; +static VulkanInterface sProtectedContentVulkanInterface; + +static void sSetupVulkanInterface() { + if (!sVulkanInterface.initialized) { + sVulkanInterface = initVulkanInterface(false /* no protected content */); + // We will have to abort if non-protected VkDevice creation fails (then nothing works). + LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized, + "Could not initialize Vulkan RenderEngine!"); + } + if (!sProtectedContentVulkanInterface.initialized) { + sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */); + if (!sProtectedContentVulkanInterface.initialized) { + ALOGE("Could not initialize protected content Vulkan RenderEngine."); + } + } +} + +namespace skia { + +using base::StringAppendF; + +bool SkiaVkRenderEngine::canSupportSkiaVkRenderEngine() { + VulkanInterface temp = initVulkanInterface(false /* no protected content */); + ALOGD("SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(): initialized == %s.", + temp.initialized ? "true" : "false"); + return temp.initialized; +} + +std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( + const RenderEngineCreationArgs& args) { + std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); + engine->ensureGrContextsCreated(); + + if (sVulkanInterface.initialized) { + ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__); + return engine; + } else { + ALOGD("SkiaVkRenderEngine::%s: could not create SkiaVkRenderEngine. " + "Likely insufficient Vulkan support", + __func__); + return {}; + } +} + +SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args) + : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + args.useColorManagement, args.supportsBackgroundBlur) {} + +SkiaVkRenderEngine::~SkiaVkRenderEngine() { + finishRenderingAndAbandonContext(); +} + +SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts( + const GrContextOptions& options) { + sSetupVulkanInterface(); + + SkiaRenderEngine::Contexts contexts; + contexts.first = GrDirectContext::MakeVulkan(sVulkanInterface.getBackendContext(), options); + if (supportsProtectedContentImpl()) { + contexts.second = + GrDirectContext::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(), + options); + } + + return contexts; +} + +bool SkiaVkRenderEngine::supportsProtectedContentImpl() const { + return sProtectedContentVulkanInterface.initialized; +} + +bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) { + return true; +} + +static void delete_semaphore(void* _semaphore) { + VkSemaphore semaphore = (VkSemaphore)_semaphore; + sVulkanInterface.destroySemaphore(semaphore); +} + +static void delete_semaphore_protected(void* _semaphore) { + VkSemaphore semaphore = (VkSemaphore)_semaphore; + sProtectedContentVulkanInterface.destroySemaphore(semaphore); +} + +static VulkanInterface& getVulkanInterface(bool protectedContext) { + if (protectedContext) { + return sProtectedContentVulkanInterface; + } + return sVulkanInterface; +} + +void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) { + if (fenceFd.get() < 0) return; + + int dupedFd = dup(fenceFd.get()); + if (dupedFd < 0) { + ALOGE("failed to create duplicate fence fd: %d", dupedFd); + sync_wait(fenceFd.get(), -1); + return; + } + + base::unique_fd fenceDup(dupedFd); + VkSemaphore waitSemaphore = + getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); + GrBackendSemaphore beSemaphore; + beSemaphore.initVulkan(waitSemaphore); + grContext->wait(1, &beSemaphore, true /* delete after wait */); +} + +base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { + VkSemaphore signalSemaphore = getVulkanInterface(isProtected()).createExportableSemaphore(); + GrBackendSemaphore beSignalSemaphore; + beSignalSemaphore.initVulkan(signalSemaphore); + GrFlushInfo flushInfo; + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &beSignalSemaphore; + flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore; + flushInfo.fFinishedContext = (void*)signalSemaphore; + GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); + grContext->submit(false /* no cpu sync */); + int drawFenceFd = -1; + if (GrSemaphoresSubmitted::kYes == submitted) { + drawFenceFd = getVulkanInterface(isProtected()).exportSemaphoreSyncFd(signalSemaphore); + } + base::unique_fd res(drawFenceFd); + return res; +} + +int SkiaVkRenderEngine::getContextPriority() { + // EGL_CONTEXT_PRIORITY_REALTIME_NV + constexpr int kRealtimePriority = 0x3357; + if (getVulkanInterface(isProtected()).isRealtimePriority) { + return kRealtimePriority; + } else { + return 0; + } +} + +void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan----------\n"); + StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized); + StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", + sProtectedContentVulkanInterface.initialized); + + if (!sVulkanInterface.initialized) { + return; + } + + StringAppendF(&result, "\n Instance extensions:\n"); + for (const auto& name : sVulkanInterface.instanceExtensionNames) { + StringAppendF(&result, "\n %s\n", name.c_str()); + } + + StringAppendF(&result, "\n Device extensions:\n"); + for (const auto& name : sVulkanInterface.deviceExtensionNames) { + StringAppendF(&result, "\n %s\n", name.c_str()); + } +} + +} // namespace skia +} // namespace renderengine +} // namespace android +#endif // RE_SKIAVK diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h new file mode 100644 index 0000000000..1e42b80c10 --- /dev/null +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -0,0 +1,63 @@ +/* + * Copyright 2022 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. + */ + +#ifndef SF_SKIAVKRENDERENGINE_H_ +#define SF_SKIAVKRENDERENGINE_H_ + +// Allow the SkiaVkRenderEngine class to not be compiled, to save space +// NOTE: In order to build this class, define `RE_SKIAVK` in a build file. +#ifdef RE_SKIAVK + +#include <vk/GrVkBackendContext.h> + +#include "SkiaRenderEngine.h" + +namespace android { +namespace renderengine { +namespace skia { + +class SkiaVkRenderEngine : public SkiaRenderEngine { +public: + // Returns false if Vulkan implementation can't support SkiaVkRenderEngine. + static bool canSupportSkiaVkRenderEngine(); + static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args); + ~SkiaVkRenderEngine() override; + + int getContextPriority() override; + +protected: + // Implementations of abstract SkiaRenderEngine functions specific to + // rendering backend + virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options); + bool supportsProtectedContentImpl() const override; + bool useProtectedContextImpl(GrProtected isProtected) override; + void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override; + base::unique_fd flushAndSubmit(GrDirectContext* context) override; + void appendBackendSpecificInfoToDump(std::string& result) override; + +private: + SkiaVkRenderEngine(const RenderEngineCreationArgs& args); + base::unique_fd flush(); + + GrVkBackendContext mBackendContext; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android + +#endif // RE_SKIAVK +#endif // SF_SKIAVKRENDERENGINE_H_ diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 777d02f415..7db95a7ea0 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -38,6 +38,7 @@ #include <fstream> #include "../skia/SkiaGLRenderEngine.h" +#include "../skia/SkiaVkRenderEngine.h" #include "../threaded/RenderEngineThreaded.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; @@ -107,9 +108,53 @@ public: virtual std::string name() = 0; virtual renderengine::RenderEngine::RenderEngineType type() = 0; virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; + virtual bool typeSupported() = 0; virtual bool useColorManagement() const = 0; }; +#ifdef RE_SKIAVK +class SkiaVkRenderEngineFactory : public RenderEngineFactory { +public: + std::string name() override { return "SkiaVkRenderEngineFactory"; } + + renderengine::RenderEngine::RenderEngineType type() { + return renderengine::RenderEngine::RenderEngineType::SKIA_VK; + } + + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { + std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine(); + return re; + } + + std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() { + renderengine::RenderEngineCreationArgs reCreationArgs = + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setUseColorManagerment(false) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(type()) + .setUseColorManagerment(useColorManagement()) + .build(); + return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs); + } + + bool typeSupported() override { + return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); + } + bool useColorManagement() const override { return false; } + void skip() { GTEST_SKIP(); } +}; + +class SkiaVkCMRenderEngineFactory : public SkiaVkRenderEngineFactory { +public: + bool useColorManagement() const override { return true; } +}; +#endif // RE_SKIAVK + class SkiaGLESRenderEngineFactory : public RenderEngineFactory { public: std::string name() override { return "SkiaGLRenderEngineFactory"; } @@ -133,6 +178,7 @@ public: return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); } + bool typeSupported() override { return true; } bool useColorManagement() const override { return false; } }; @@ -159,6 +205,7 @@ public: return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); } + bool typeSupported() override { return true; } bool useColorManagement() const override { return true; } }; @@ -1513,16 +1560,30 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 expectBufferColor(Rect(kGreyLevels, 1), generator, 2); } +#ifdef RE_SKIAVK +INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, + testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(), + std::make_shared<SkiaGLESCMRenderEngineFactory>(), + std::make_shared<SkiaVkRenderEngineFactory>(), + std::make_shared<SkiaVkCMRenderEngineFactory>())); +#else // RE_SKIAVK INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(), std::make_shared<SkiaGLESCMRenderEngineFactory>())); +#endif // RE_SKIAVK TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); drawEmptyLayers(); } TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); @@ -1547,6 +1608,9 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { } TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -1578,6 +1642,9 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { } TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -1597,56 +1664,89 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillGreenBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBlueBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedTransparentBuffer<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferPhysicalOffset<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate0<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate90<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate180<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate270<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferLayerTransform<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransform<ColorSourceVariant>(); } @@ -1654,7 +1754,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { GTEST_SKIP(); } @@ -1665,7 +1765,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { GTEST_SKIP(); } @@ -1674,81 +1774,129 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithRoundedCorners<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferAndBlurBackground<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillSmallLayerAndBlurBackground<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); overlayCorners<ColorSourceVariant>(); } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } @@ -1756,7 +1904,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { GTEST_SKIP(); } @@ -1767,7 +1915,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { GTEST_SKIP(); } @@ -1776,81 +1924,129 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_o } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } @@ -1858,7 +2054,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { GTEST_SKIP(); } @@ -1869,7 +2065,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { GTEST_SKIP(); } @@ -1878,46 +2074,73 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_b } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferTextureTransform(); } TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithPremultiplyAlpha(); } TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); fillBufferWithoutPremultiplyAlpha(); } TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), @@ -1934,6 +2157,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -1955,6 +2181,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -1977,6 +2206,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -2000,6 +2232,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), @@ -2024,6 +2259,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); @@ -2051,6 +2289,9 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { } TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2081,12 +2322,20 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { fenceTwo->waitForever(LOG_TAG); // Only cleanup the first time. - EXPECT_FALSE(mRE->canSkipPostRenderCleanup()); - mRE->cleanupPostRender(); - EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); + if (mRE->canSkipPostRenderCleanup()) { + // Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so + // it never gets added to the cleanup list. In those cases, we can skip. + EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK); + } else { + mRE->cleanupPostRender(); + EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); + } } TEST_P(RenderEngineTest, testRoundedCornersCrop) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2137,6 +2386,9 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2182,6 +2434,9 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); renderengine::DisplaySettings settings; @@ -2259,6 +2514,9 @@ TEST_P(RenderEngineTest, testRoundedCornersXY) { } TEST_P(RenderEngineTest, testClear) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto rect = fullscreenRect(); @@ -2288,6 +2546,9 @@ TEST_P(RenderEngineTest, testClear) { } TEST_P(RenderEngineTest, testDisableBlendingBuffer) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto rect = Rect(0, 0, 1, 1); @@ -2385,6 +2646,9 @@ TEST_P(RenderEngineTest, testBorder) { } TEST_P(RenderEngineTest, testDimming) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB_LINEAR; @@ -2457,6 +2721,9 @@ TEST_P(RenderEngineTest, testDimming) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 | @@ -2532,6 +2799,9 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 | @@ -2592,6 +2862,9 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 | @@ -2653,6 +2926,9 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_devi } TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto displayRect = Rect(2, 1); @@ -2704,6 +2980,9 @@ TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { } TEST_P(RenderEngineTest, test_isOpaque) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto rect = Rect(0, 0, 1, 1); @@ -2755,7 +3034,7 @@ TEST_P(RenderEngineTest, test_isOpaque) { } TEST_P(RenderEngineTest, test_tonemapPQMatches) { - if (!GetParam()->useColorManagement()) { + if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) { GTEST_SKIP(); } @@ -2772,7 +3051,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) { } TEST_P(RenderEngineTest, test_tonemapHLGMatches) { - if (!GetParam()->useColorManagement()) { + if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) { GTEST_SKIP(); } @@ -2789,6 +3068,9 @@ TEST_P(RenderEngineTest, test_tonemapHLGMatches) { } TEST_P(RenderEngineTest, r8_behaves_as_mask) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); @@ -2846,6 +3128,9 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { } TEST_P(RenderEngineTest, r8_respects_color_transform) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); @@ -2908,6 +3193,9 @@ TEST_P(RenderEngineTest, r8_respects_color_transform) { } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); @@ -2973,6 +3261,9 @@ TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { } TEST_P(RenderEngineTest, primeShaderCache) { + if (!GetParam()->typeSupported()) { + GTEST_SKIP(); + } initializeRenderEngine(); auto fut = mRE->primeCache(); diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index 2b93c6e85e..b6b9cc4099 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,9 +21,10 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_shared { +cc_library { name: "libsensor", + host_supported: true, cflags: [ "-Wall", "-Werror", diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 78f692bb0c..2278d391b5 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -42,6 +42,7 @@ enum { GET_DYNAMIC_SENSOR_LIST, CREATE_SENSOR_DIRECT_CONNECTION, SET_OPERATION_PARAMETER, + GET_RUNTIME_SENSOR_LIST, }; class BpSensorServer : public BpInterface<ISensorServer> @@ -90,6 +91,25 @@ public: return v; } + virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) + { + Parcel data, reply; + data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); + data.writeString16(opPackageName); + data.writeInt32(deviceId); + remote()->transact(GET_RUNTIME_SENSOR_LIST, data, &reply); + Sensor s; + Vector<Sensor> v; + uint32_t n = reply.readUint32(); + v.setCapacity(n); + while (n) { + n--; + reply.read(s); + v.add(s); + } + return v; + } + virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, int mode, const String16& opPackageName, const String16& attributionTag) { @@ -194,6 +214,18 @@ status_t BnSensorServer::onTransact( } return NO_ERROR; } + case GET_RUNTIME_SENSOR_LIST: { + CHECK_INTERFACE(ISensorServer, data, reply); + const String16& opPackageName = data.readString16(); + const int deviceId = data.readInt32(); + Vector<Sensor> v(getRuntimeSensorList(opPackageName, deviceId)); + size_t n = v.size(); + reply->writeUint32(static_cast<uint32_t>(n)); + for (size_t i = 0; i < n; i++) { + reply->write(v[i]); + } + return NO_ERROR; + } case CREATE_SENSOR_DIRECT_CONNECTION: { CHECK_INTERFACE(ISensorServer, data, reply); const String16& opPackageName = data.readString16(); diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 0ba9704263..27482768f2 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -201,6 +201,19 @@ ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) { return static_cast<ssize_t>(count); } +ssize_t SensorManager::getRuntimeSensorList(int deviceId, Vector<Sensor>& runtimeSensors) { + Mutex::Autolock _l(mLock); + status_t err = assertStateLocked(); + if (err < 0) { + return static_cast<ssize_t>(err); + } + + runtimeSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, deviceId); + size_t count = runtimeSensors.size(); + + return static_cast<ssize_t>(count); +} + ssize_t SensorManager::getDynamicSensorList(Sensor const* const** list) { Mutex::Autolock _l(mLock); status_t err = assertStateLocked(); diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h index ce5c672da0..3295196ac4 100644 --- a/libs/sensor/include/sensor/ISensorServer.h +++ b/libs/sensor/include/sensor/ISensorServer.h @@ -43,6 +43,7 @@ public: virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0; virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0; + virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) = 0; virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, int mode, const String16& opPackageName, const String16& attributionTag) = 0; diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index 8d0a8a45d9..0798da292a 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -59,6 +59,7 @@ public: ssize_t getSensorList(Sensor const* const** list); ssize_t getDynamicSensorList(Vector<Sensor>& list); ssize_t getDynamicSensorList(Sensor const* const** list); + ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list); Sensor const* getDefaultSensor(int type); sp<SensorEventQueue> createEventQueue( String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16("")); diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp deleted file mode 100644 index 0bda7987a0..0000000000 --- a/libs/vr/libbufferhubqueue/Android.bp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2016 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. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -sourceFiles = [ - "buffer_hub_queue_client.cpp", - "buffer_hub_queue_parcelable.cpp", -] - -includeFiles = [ - "include", -] - -staticLibraries = [ - "libbufferhub", -] - -sharedLibraries = [ - "libbinder", - "libcutils", - "liblog", - "libui", - "libutils", - "libpdx_default_transport", -] - -headerLibraries = [ - "libdvr_headers", - "libnativebase_headers", -] - -cc_library_shared { - name: "libbufferhubqueue", - cflags: [ - "-DLOG_TAG=\"libbufferhubqueue\"", - "-DTRACE=0", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-Wall", - "-Werror", - "-Wno-format", - "-Wno-unused-parameter", - "-Wno-unused-variable", - ], - srcs: sourceFiles, - export_include_dirs: includeFiles, - export_static_lib_headers: staticLibraries, - static_libs: staticLibraries, - shared_libs: sharedLibraries, - header_libs: headerLibraries, -} - -subdirs = ["benchmarks", "tests"] diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp deleted file mode 100644 index 2d3fa4aec0..0000000000 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ /dev/null @@ -1,823 +0,0 @@ -#include "include/private/dvr/buffer_hub_queue_client.h" - -#include <inttypes.h> -#include <log/log.h> -#include <poll.h> -#include <sys/epoll.h> - -#include <array> - -#include <pdx/default_transport/client_channel.h> -#include <pdx/default_transport/client_channel_factory.h> -#include <pdx/file_handle.h> -#include <pdx/trace.h> - -#define RETRY_EINTR(fnc_call) \ - ([&]() -> decltype(fnc_call) { \ - decltype(fnc_call) result; \ - do { \ - result = (fnc_call); \ - } while (result == -1 && errno == EINTR); \ - return result; \ - })() - -using android::pdx::ErrorStatus; -using android::pdx::LocalChannelHandle; -using android::pdx::LocalHandle; -using android::pdx::Status; - -namespace android { -namespace dvr { - -namespace { - -std::pair<int32_t, int32_t> Unstuff(uint64_t value) { - return {static_cast<int32_t>(value >> 32), - static_cast<int32_t>(value & ((1ull << 32) - 1))}; -} - -uint64_t Stuff(int32_t a, int32_t b) { - const uint32_t ua = static_cast<uint32_t>(a); - const uint32_t ub = static_cast<uint32_t>(b); - return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub); -} - -} // anonymous namespace - -BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle) - : Client{pdx::default_transport::ClientChannel::Create( - std::move(channel_handle))} { - Initialize(); -} - -BufferHubQueue::BufferHubQueue(const std::string& endpoint_path) - : Client{ - pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} { - Initialize(); -} - -void BufferHubQueue::Initialize() { - int ret = epoll_fd_.Create(); - if (ret < 0) { - ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s", - strerror(-ret)); - return; - } - - epoll_event event = { - .events = EPOLLIN | EPOLLET, - .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}}; - ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); - if (ret < 0) { - ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__, - strerror(-ret)); - } -} - -Status<void> BufferHubQueue::ImportQueue() { - auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>(); - if (!status) { - ALOGE("%s: Failed to import queue: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return ErrorStatus(status.error()); - } else { - SetupQueue(status.get()); - return {}; - } -} - -void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) { - is_async_ = queue_info.producer_config.is_async; - default_width_ = queue_info.producer_config.default_width; - default_height_ = queue_info.producer_config.default_height; - default_format_ = queue_info.producer_config.default_format; - user_metadata_size_ = queue_info.producer_config.user_metadata_size; - id_ = queue_info.id; -} - -std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() { - if (auto status = CreateConsumerQueueHandle(/*silent*/ false)) - return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); - else - return nullptr; -} - -std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() { - if (auto status = CreateConsumerQueueHandle(/*silent*/ true)) - return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); - else - return nullptr; -} - -Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle( - bool silent) { - auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent); - if (!status) { - ALOGE( - "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " - "%s", - status.GetErrorMessage().c_str()); - return ErrorStatus(status.error()); - } - - return status; -} - -pdx::Status<ConsumerQueueParcelable> -BufferHubQueue::CreateConsumerQueueParcelable(bool silent) { - auto status = CreateConsumerQueueHandle(silent); - if (!status) - return status.error_status(); - - // A temporary consumer queue client to pull its channel parcelable. - auto consumer_queue = - std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take())); - ConsumerQueueParcelable queue_parcelable( - consumer_queue->GetChannel()->TakeChannelParcelable()); - - if (!queue_parcelable.IsValid()) { - ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__); - return ErrorStatus(EINVAL); - } - - return {std::move(queue_parcelable)}; -} - -bool BufferHubQueue::WaitForBuffers(int timeout) { - ATRACE_NAME("BufferHubQueue::WaitForBuffers"); - std::array<epoll_event, kMaxEvents> events; - - // Loop at least once to check for hangups. - do { - ALOGD_IF( - TRACE, - "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu", - id(), count(), capacity()); - - // If there is already a buffer then just check for hangup without waiting. - const int ret = epoll_fd_.Wait(events.data(), events.size(), - count() == 0 ? timeout : 0); - - if (ret == 0) { - ALOGI_IF(TRACE, - "BufferHubQueue::WaitForBuffers: No events before timeout: " - "queue_id=%d", - id()); - return count() != 0; - } - - if (ret < 0 && ret != -EINTR) { - ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret)); - return false; - } - - const int num_events = ret; - - // A BufferQueue's epoll fd tracks N+1 events, where there are N events, - // one for each buffer in the queue, and one extra event for the queue - // client itself. - for (int i = 0; i < num_events; i++) { - int32_t event_fd; - int32_t index; - std::tie(event_fd, index) = Unstuff(events[i].data.u64); - - PDX_TRACE_FORMAT( - "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;" - "slot=%d|", - id(), num_events, i, event_fd, index); - - ALOGD_IF(TRACE, - "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d", - i, event_fd, index); - - if (is_buffer_event_index(index)) { - HandleBufferEvent(static_cast<size_t>(index), event_fd, - events[i].events); - } else if (is_queue_event_index(index)) { - HandleQueueEvent(events[i].events); - } else { - ALOGW( - "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d " - "index=%d", - event_fd, index); - } - } - } while (count() == 0 && capacity() > 0 && !hung_up()); - - return count() != 0; -} - -Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, - int poll_events) { - ATRACE_NAME("BufferHubQueue::HandleBufferEvent"); - if (!buffers_[slot]) { - ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); - return ErrorStatus(ENOENT); - } - - auto status = buffers_[slot]->GetEventMask(poll_events); - if (!status) { - ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - const int events = status.get(); - PDX_TRACE_FORMAT( - "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;" - "events=%d|", - id(), buffers_[slot]->id(), slot, event_fd, poll_events, events); - - if (events & EPOLLIN) { - return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()}); - } else if (events & EPOLLHUP) { - ALOGW( - "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu " - "event_fd=%d buffer_id=%d", - slot, buffers_[slot]->event_fd(), buffers_[slot]->id()); - return RemoveBuffer(slot); - } else { - ALOGW( - "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " - "events=%d", - slot, events); - } - - return {}; -} - -Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { - ATRACE_NAME("BufferHubQueue::HandleQueueEvent"); - auto status = GetEventMask(poll_event); - if (!status) { - ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - const int events = status.get(); - if (events & EPOLLIN) { - // Note that after buffer imports, if |count()| still returns 0, epoll - // wait will be tried again to acquire the newly imported buffer. - auto buffer_status = OnBufferAllocated(); - if (!buffer_status) { - ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, - buffer_status.GetErrorMessage().c_str()); - } - } else if (events & EPOLLHUP) { - ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__); - hung_up_ = true; - } else { - ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events); - } - - return {}; -} - -Status<void> BufferHubQueue::AddBuffer( - const std::shared_ptr<BufferHubBase>& buffer, size_t slot) { - ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(), - slot); - - if (is_full()) { - ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_); - return ErrorStatus(E2BIG); - } - - if (buffers_[slot]) { - // Replace the buffer if the slot is occupied. This could happen when the - // producer side replaced the slot with a newly allocated buffer. Remove the - // buffer before setting up with the new one. - auto remove_status = RemoveBuffer(slot); - if (!remove_status) - return remove_status.error_status(); - } - - for (const auto& event_source : buffer->GetEventSources()) { - epoll_event event = {.events = event_source.event_mask | EPOLLET, - .data = {.u64 = Stuff(buffer->event_fd(), slot)}}; - const int ret = - epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event); - if (ret < 0) { - ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__, - strerror(-ret)); - return ErrorStatus(-ret); - } - } - - buffers_[slot] = buffer; - capacity_++; - return {}; -} - -Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { - ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot); - - if (buffers_[slot]) { - for (const auto& event_source : buffers_[slot]->GetEventSources()) { - const int ret = - epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr); - if (ret < 0) { - ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__, - strerror(-ret)); - return ErrorStatus(-ret); - } - } - - // Trigger OnBufferRemoved callback if registered. - if (on_buffer_removed_) - on_buffer_removed_(buffers_[slot]); - - buffers_[slot] = nullptr; - capacity_--; - } - - return {}; -} - -Status<void> BufferHubQueue::Enqueue(Entry entry) { - if (!is_full()) { - // Find and remove the enqueued buffer from unavailable_buffers_slot if - // exist. - auto enqueued_buffer_iter = std::find_if( - unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(), - [&entry](size_t slot) -> bool { return slot == entry.slot; }); - if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) { - unavailable_buffers_slot_.erase(enqueued_buffer_iter); - } - - available_buffers_.push(std::move(entry)); - - // Trigger OnBufferAvailable callback if registered. - if (on_buffer_available_) - on_buffer_available_(); - - return {}; - } else { - ALOGE("%s: Buffer queue is full!", __FUNCTION__); - return ErrorStatus(E2BIG); - } -} - -Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout, - size_t* slot) { - ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout); - - PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count()); - - if (count() == 0) { - if (!WaitForBuffers(timeout)) - return ErrorStatus(ETIMEDOUT); - } - - auto& entry = available_buffers_.top(); - PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), - entry.slot); - - std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer); - *slot = entry.slot; - - available_buffers_.pop(); - unavailable_buffers_slot_.push_back(*slot); - - return {std::move(buffer)}; -} - -void BufferHubQueue::SetBufferAvailableCallback( - BufferAvailableCallback callback) { - on_buffer_available_ = callback; -} - -void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) { - on_buffer_removed_ = callback; -} - -pdx::Status<void> BufferHubQueue::FreeAllBuffers() { - // Clear all available buffers. - while (!available_buffers_.empty()) - available_buffers_.pop(); - - pdx::Status<void> last_error; // No error. - // Clear all buffers this producer queue is tracking. - for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { - if (buffers_[slot] != nullptr) { - auto status = RemoveBuffer(slot); - if (!status) { - ALOGE( - "ProducerQueue::FreeAllBuffers: Failed to remove buffer at " - "slot=%zu.", - slot); - last_error = status.error_status(); - } - } - } - - return last_error; -} - -ProducerQueue::ProducerQueue(LocalChannelHandle handle) - : BASE(std::move(handle)) { - auto status = ImportQueue(); - if (!status) { - ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - } -} - -ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, - const UsagePolicy& usage) - : BASE(BufferHubRPC::kClientPath) { - auto status = - InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage); - if (!status) { - ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - return; - } - - SetupQueue(status.get()); -} - -Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( - uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t buffer_count) { - if (buffer_count == 0) { - return {std::vector<size_t>()}; - } - - if (capacity() + buffer_count > kMaxQueueCapacity) { - ALOGE( - "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot " - "allocate %zu more buffer(s).", - capacity(), buffer_count); - return ErrorStatus(E2BIG); - } - - Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status = - InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>( - width, height, layer_count, format, usage, buffer_count); - if (!status) { - ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto buffer_handle_slots = status.take(); - LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count, - "BufferHubRPC::ProducerQueueAllocateBuffers should " - "return %zu buffer handle(s), but returned %zu instead.", - buffer_count, buffer_handle_slots.size()); - - std::vector<size_t> buffer_slots; - buffer_slots.reserve(buffer_count); - - // Bookkeeping for each buffer. - for (auto& hs : buffer_handle_slots) { - auto& buffer_handle = hs.first; - size_t buffer_slot = hs.second; - - // Note that import might (though very unlikely) fail. If so, buffer_handle - // will be closed and included in returned buffer_slots. - if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)), - buffer_slot)) { - ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu", - buffer_slot); - buffer_slots.push_back(buffer_slot); - } - } - - if (buffer_slots.size() != buffer_count) { - // Error out if the count of imported buffer(s) is not correct. - ALOGE( - "ProducerQueue::AllocateBuffers: requested to import %zu " - "buffers, but actually imported %zu buffers.", - buffer_count, buffer_slots.size()); - return ErrorStatus(ENOMEM); - } - - return {std::move(buffer_slots)}; -} - -Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, - uint32_t layer_count, - uint32_t format, uint64_t usage) { - // We only allocate one buffer at a time. - constexpr size_t buffer_count = 1; - auto status = - AllocateBuffers(width, height, layer_count, format, usage, buffer_count); - if (!status) { - ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - return {status.get()[0]}; -} - -Status<void> ProducerQueue::AddBuffer( - const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", - id(), buffer->id(), slot); - // For producer buffer, we need to enqueue the newly added buffer - // immediately. Producer queue starts with all buffers in available state. - auto status = BufferHubQueue::AddBuffer(buffer, slot); - if (!status) - return status; - - return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); -} - -Status<size_t> ProducerQueue::InsertBuffer( - const std::shared_ptr<ProducerBuffer>& buffer) { - if (buffer == nullptr || - !BufferHubDefs::isClientGained(buffer->buffer_state(), - buffer->client_state_mask())) { - ALOGE( - "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in " - "gained state."); - return ErrorStatus(EINVAL); - } - - auto status_or_slot = - InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>( - buffer->cid()); - if (!status_or_slot) { - ALOGE( - "ProducerQueue::InsertBuffer: Failed to insert producer buffer: " - "buffer_cid=%d, error: %s.", - buffer->cid(), status_or_slot.GetErrorMessage().c_str()); - return status_or_slot.error_status(); - } - - size_t slot = status_or_slot.get(); - - // Note that we are calling AddBuffer() from the base class to explicitly - // avoid Enqueue() the ProducerBuffer. - auto status = BufferHubQueue::AddBuffer(buffer, slot); - if (!status) { - ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - return {slot}; -} - -Status<void> ProducerQueue::RemoveBuffer(size_t slot) { - auto status = - InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot); - if (!status) { - ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - return BufferHubQueue::RemoveBuffer(slot); -} - -Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue( - int timeout, size_t* slot, LocalHandle* release_fence) { - DvrNativeBufferMetadata canonical_meta; - return Dequeue(timeout, slot, &canonical_meta, release_fence); -} - -pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* release_fence, bool gain_posted_buffer) { - ATRACE_NAME("ProducerQueue::Dequeue"); - if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { - ALOGE("%s: Invalid parameter.", __FUNCTION__); - return ErrorStatus(EINVAL); - } - - std::shared_ptr<ProducerBuffer> buffer; - Status<std::shared_ptr<BufferHubBase>> dequeue_status = - BufferHubQueue::Dequeue(timeout, slot); - if (dequeue_status.ok()) { - buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take()); - } else { - if (gain_posted_buffer) { - Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status = - ProducerQueue::DequeueUnacquiredBuffer(slot); - if (!dequeue_unacquired_status.ok()) { - ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__, - dequeue_unacquired_status.error()); - return dequeue_unacquired_status.error_status(); - } - buffer = dequeue_unacquired_status.take(); - } else { - return dequeue_status.error_status(); - } - } - const int ret = - buffer->GainAsync(out_meta, release_fence, gain_posted_buffer); - if (ret < 0 && ret != -EALREADY) - return ErrorStatus(-ret); - - return {std::move(buffer)}; -} - -Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer( - size_t* slot) { - if (unavailable_buffers_slot_.size() < 1) { - ALOGE( - "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in " - "acquired state if exist.", - __FUNCTION__); - return ErrorStatus(ENOMEM); - } - - // Find the first buffer that is not in acquired state from - // unavailable_buffers_slot_. - for (auto iter = unavailable_buffers_slot_.begin(); - iter != unavailable_buffers_slot_.end(); iter++) { - std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter); - if (buffer == nullptr) { - ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__, - static_cast<int>(*slot)); - return ErrorStatus(EIO); - } - if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) { - *slot = *iter; - unavailable_buffers_slot_.erase(iter); - unavailable_buffers_slot_.push_back(*slot); - ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d", - __FUNCTION__, static_cast<int>(*slot)); - return {std::move(buffer)}; - } - } - ALOGE( - "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.", - __FUNCTION__); - return ErrorStatus(EBUSY); -} - -pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() { - if (capacity() != 0) { - ALOGE( - "%s: producer queue can only be taken out as a parcelable when empty. " - "Current queue capacity: %zu", - __FUNCTION__, capacity()); - return ErrorStatus(EINVAL); - } - - std::unique_ptr<pdx::ClientChannel> channel = TakeChannel(); - ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable()); - - // Here the queue parcelable is returned and holds the underlying system - // resources backing the queue; while the original client channel of this - // producer queue is destroyed in place so that this client can no longer - // provide producer operations. - return {std::move(queue_parcelable)}; -} - -/*static */ -std::unique_ptr<ConsumerQueue> ConsumerQueue::Import( - LocalChannelHandle handle) { - return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle))); -} - -ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) - : BufferHubQueue(std::move(handle)) { - auto status = ImportQueue(); - if (!status) { - ALOGE("%s: Failed to import queue: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - Close(-status.error()); - } - - auto import_status = ImportBuffers(); - if (import_status) { - ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get()); - } else { - ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, - import_status.GetErrorMessage().c_str()); - } -} - -Status<size_t> ConsumerQueue::ImportBuffers() { - auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); - if (!status) { - if (status.error() == EBADR) { - ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__); - return {0}; - } else { - ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return status.error_status(); - } - } - - int ret; - Status<void> last_error; - size_t imported_buffers_count = 0; - - auto buffer_handle_slots = status.take(); - for (auto& buffer_handle_slot : buffer_handle_slots) { - ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__, - buffer_handle_slot.first.value()); - - std::unique_ptr<ConsumerBuffer> consumer_buffer = - ConsumerBuffer::Import(std::move(buffer_handle_slot.first)); - if (!consumer_buffer) { - ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__, - buffer_handle_slot.second); - last_error = ErrorStatus(EPIPE); - continue; - } - - auto add_status = - AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second); - if (!add_status) { - ALOGE("%s: Failed to add buffer: %s", __FUNCTION__, - add_status.GetErrorMessage().c_str()); - last_error = add_status; - } else { - imported_buffers_count++; - } - } - - if (imported_buffers_count > 0) - return {imported_buffers_count}; - else - return last_error.error_status(); -} - -Status<void> ConsumerQueue::AddBuffer( - const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(), - buffer->id(), slot); - return BufferHubQueue::AddBuffer(buffer, slot); -} - -Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue( - int timeout, size_t* slot, void* meta, size_t user_metadata_size, - LocalHandle* acquire_fence) { - if (user_metadata_size != user_metadata_size_) { - ALOGE( - "%s: Metadata size (%zu) for the dequeuing buffer does not match " - "metadata size (%zu) for the queue.", - __FUNCTION__, user_metadata_size, user_metadata_size_); - return ErrorStatus(EINVAL); - } - - DvrNativeBufferMetadata canonical_meta; - auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence); - if (!status) - return status.error_status(); - - if (meta && user_metadata_size) { - void* metadata_src = - reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); - if (metadata_src) { - memcpy(meta, metadata_src, user_metadata_size); - } else { - ALOGW("%s: no user-defined metadata.", __FUNCTION__); - } - } - - return status; -} - -Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* acquire_fence) { - ATRACE_NAME("ConsumerQueue::Dequeue"); - if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) { - ALOGE("%s: Invalid parameter.", __FUNCTION__); - return ErrorStatus(EINVAL); - } - - auto status = BufferHubQueue::Dequeue(timeout, slot); - if (!status) - return status.error_status(); - - auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take()); - const int ret = buffer->AcquireAsync(out_meta, acquire_fence); - if (ret < 0) - return ErrorStatus(-ret); - - return {std::move(buffer)}; -} - -Status<void> ConsumerQueue::OnBufferAllocated() { - ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id()); - - auto status = ImportBuffers(); - if (!status) { - ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, - status.GetErrorMessage().c_str()); - return ErrorStatus(status.error()); - } else if (status.get() == 0) { - ALOGW("%s: No new buffers allocated!", __FUNCTION__); - return ErrorStatus(ENOBUFS); - } else { - ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__, - status.get()); - return {}; - } -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp deleted file mode 100644 index f705749243..0000000000 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "include/private/dvr/buffer_hub_queue_parcelable.h" - -#include <binder/Parcel.h> -#include <pdx/default_transport/channel_parcelable.h> - -namespace android { -namespace dvr { - -template <BufferHubQueueParcelableMagic Magic> -bool BufferHubQueueParcelable<Magic>::IsValid() const { - return !!channel_parcelable_ && channel_parcelable_->IsValid(); -} - -template <BufferHubQueueParcelableMagic Magic> -pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() { - if (!IsValid()) { - ALOGE( - "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel."); - return {}; // Returns an empty channel handle. - } - - // Take channel handle out of the parcelable and reset the parcelable. - pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle(); - // Now channel_parcelable_ should already be invalid, but reset it to release - // the invalid parcelable object from unique_ptr. - channel_parcelable_ = nullptr; - return handle; -} - -template <BufferHubQueueParcelableMagic Magic> -status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const { - if (!IsValid()) { - ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel."); - return -EINVAL; - } - - status_t res = parcel->writeUint32(Magic); - if (res != OK) { - ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic."); - return res; - } - - return channel_parcelable_->writeToParcel(parcel); -} - -template <BufferHubQueueParcelableMagic Magic> -status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) { - if (IsValid()) { - ALOGE( - "BufferHubQueueParcelable::readFromParcel: This parcelable object has " - "been initialized already."); - return -EINVAL; - } - - uint32_t out_magic = 0; - status_t res = OK; - - res = parcel->readUint32(&out_magic); - if (res != OK) - return res; - - if (out_magic != Magic) { - ALOGE( - "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, " - "epxected: 0x%x", - out_magic, Magic); - return -EINVAL; - } - - // (Re)Alocate channel parcelable object. - channel_parcelable_ = - std::make_unique<pdx::default_transport::ChannelParcelable>(); - return channel_parcelable_->readFromParcel(parcel); -} - -template class BufferHubQueueParcelable< - BufferHubQueueParcelableMagic::Producer>; -template class BufferHubQueueParcelable< - BufferHubQueueParcelableMagic::Consumer>; - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h deleted file mode 100644 index 74b4b3d67f..0000000000 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h +++ /dev/null @@ -1,476 +0,0 @@ -#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ -#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ - -#include <ui/BufferQueueDefs.h> - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -// The following headers are included without checking every warning. -// TODO(b/72172820): Remove the workaround once we have enforced -Weverything -// in these headers and their dependencies. -#include <pdx/client.h> -#include <pdx/status.h> -#include <private/dvr/buffer_hub_queue_parcelable.h> -#include <private/dvr/bufferhub_rpc.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/epoll_file_descriptor.h> -#include <private/dvr/producer_buffer.h> - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#include <memory> -#include <queue> -#include <vector> - -namespace android { -namespace dvr { - -class ConsumerQueue; - -// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are -// automatically re-requeued when released by the remote side. -class BufferHubQueue : public pdx::Client { - public: - using BufferAvailableCallback = std::function<void()>; - using BufferRemovedCallback = - std::function<void(const std::shared_ptr<BufferHubBase>&)>; - - virtual ~BufferHubQueue() {} - - // Creates a new consumer queue that is attached to the producer. Returns - // a new consumer queue client or nullptr on failure. - std::unique_ptr<ConsumerQueue> CreateConsumerQueue(); - - // Creates a new consumer queue that is attached to the producer. This queue - // sets each of its imported consumer buffers to the ignored state to avoid - // participation in lifecycle events. - std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue(); - - // Returns whether the buffer queue is in async mode. - bool is_async() const { return is_async_; } - - // Returns the default buffer width of this buffer queue. - uint32_t default_width() const { return default_width_; } - - // Returns the default buffer height of this buffer queue. - uint32_t default_height() const { return default_height_; } - - // Returns the default buffer format of this buffer queue. - uint32_t default_format() const { return default_format_; } - - // Creates a new consumer in handle form for immediate transport over RPC. - pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle( - bool silent = false); - - // Creates a new consumer in parcelable form for immediate transport over - // Binder. - pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable( - bool silent = false); - - // Returns the number of buffers avaiable for dequeue. - size_t count() const { return available_buffers_.size(); } - - // Returns the total number of buffers that the queue is tracking. - size_t capacity() const { return capacity_; } - - // Returns the size of metadata structure associated with this queue. - size_t metadata_size() const { return user_metadata_size_; } - - // Returns whether the buffer queue is full. - bool is_full() const { - return available_buffers_.size() >= kMaxQueueCapacity; - } - - // Returns whether the buffer queue is connected to bufferhubd. - bool is_connected() const { return !!GetChannel(); } - - int GetBufferId(size_t slot) const { - return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id() - : -1; - } - - std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const { - return buffers_[slot]; - } - - pdx::Status<int> GetEventMask(int events) { - if (auto* client_channel = GetChannel()) { - return client_channel->GetEventMask(events); - } else { - return pdx::ErrorStatus(EINVAL); - } - } - - // Returns an fd that signals pending queue events using - // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be - // called to handle pending queue events. - int queue_fd() const { return epoll_fd_.Get(); } - - // Handles any pending events, returning available buffers to the queue and - // reaping disconnected buffers. Returns true if successful, false if an error - // occurred. - bool HandleQueueEvents() { return WaitForBuffers(0); } - - // Set buffer event callbacks, which are std::function wrappers. The caller is - // responsible for ensuring the validity of these callbacks' callable targets. - void SetBufferAvailableCallback(BufferAvailableCallback callback); - void SetBufferRemovedCallback(BufferRemovedCallback callback); - - // The queue tracks at most this many buffers. - static constexpr size_t kMaxQueueCapacity = - android::BufferQueueDefs::NUM_BUFFER_SLOTS; - - static constexpr int kNoTimeOut = -1; - - int id() const { return id_; } - bool hung_up() const { return hung_up_; } - - protected: - explicit BufferHubQueue(pdx::LocalChannelHandle channel); - explicit BufferHubQueue(const std::string& endpoint_path); - - // Imports the queue parameters by querying BufferHub for the parameters for - // this channel. - pdx::Status<void> ImportQueue(); - - // Sets up the queue with the given parameters. - void SetupQueue(const QueueInfo& queue_info); - - // Register a buffer for management by the queue. Used by subclasses to add a - // buffer to internal bookkeeping. - pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer, - size_t slot); - - // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only - // to deregister a buffer for epoll and internal bookkeeping. - virtual pdx::Status<void> RemoveBuffer(size_t slot); - - // Free all buffers that belongs to this queue. Can only be called from - // producer side. - virtual pdx::Status<void> FreeAllBuffers(); - - // Dequeue a buffer from the free queue, blocking until one is available. The - // timeout argument specifies the number of milliseconds that |Dequeue()| will - // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely, - // while specifying a timeout equal to zero cause Dequeue() to return - // immediately, even if no buffers are available. - pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout, - size_t* slot); - - // Waits for buffers to become available and adds them to the available queue. - bool WaitForBuffers(int timeout); - - pdx::Status<void> HandleBufferEvent(size_t slot, int event_fd, - int poll_events); - pdx::Status<void> HandleQueueEvent(int poll_events); - - // Entry in the priority queue of available buffers that stores related - // per-buffer data. - struct Entry { - Entry() : slot(0) {} - Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot, - uint64_t in_index) - : buffer(in_buffer), slot(in_slot), index(in_index) {} - Entry(const std::shared_ptr<BufferHubBase>& in_buffer, - std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence, - size_t in_slot) - : buffer(in_buffer), - metadata(std::move(in_metadata)), - fence(std::move(in_fence)), - slot(in_slot) {} - Entry(Entry&&) = default; - Entry& operator=(Entry&&) = default; - - std::shared_ptr<BufferHubBase> buffer; - std::unique_ptr<uint8_t[]> metadata; - pdx::LocalHandle fence; - size_t slot; - uint64_t index; - }; - - struct EntryComparator { - bool operator()(const Entry& lhs, const Entry& rhs) { - return lhs.index > rhs.index; - } - }; - - // Enqueues a buffer to the available list (Gained for producer or Acquireed - // for consumer). - pdx::Status<void> Enqueue(Entry entry); - - // Called when a buffer is allocated remotely. - virtual pdx::Status<void> OnBufferAllocated() { return {}; } - - // Size of the metadata that buffers in this queue cary. - size_t user_metadata_size_{0}; - - // Buffers and related data that are available for dequeue. - std::priority_queue<Entry, std::vector<Entry>, EntryComparator> - available_buffers_; - - // Slot of the buffers that are not available for normal dequeue. For example, - // the slot of posted or acquired buffers in the perspective of a producer. - std::vector<size_t> unavailable_buffers_slot_; - - private: - void Initialize(); - - // Special epoll data field indicating that the epoll event refers to the - // queue. - static constexpr int64_t kEpollQueueEventIndex = -1; - - static constexpr size_t kMaxEvents = 128; - - // The u64 data field of an epoll event is interpreted as int64_t: - // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific - // element of |buffers_| as a direct index; - static bool is_buffer_event_index(int64_t index) { - return index >= 0 && - index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity); - } - - // When |index| == kEpollQueueEventIndex it refers to the queue itself. - static bool is_queue_event_index(int64_t index) { - return index == BufferHubQueue::kEpollQueueEventIndex; - } - - // Whether the buffer queue is operating in Async mode. - // From GVR's perspective of view, this means a buffer can be acquired - // asynchronously by the compositor. - // From Android Surface's perspective of view, this is equivalent to - // IGraphicBufferProducer's async mode. When in async mode, a producer - // will never block even if consumer is running slow. - bool is_async_{false}; - - // Default buffer width that is set during ProducerQueue's creation. - uint32_t default_width_{1}; - - // Default buffer height that is set during ProducerQueue's creation. - uint32_t default_height_{1}; - - // Default buffer format that is set during ProducerQueue's creation. - uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888 - - // Tracks the buffers belonging to this queue. Buffers are stored according to - // "slot" in this vector. Each slot is a logical id of the buffer within this - // queue regardless of its queue position or presence in the ring buffer. - std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_; - - // Keeps track with how many buffers have been added into the queue. - size_t capacity_{0}; - - // Epoll fd used to manage buffer events. - EpollFileDescriptor epoll_fd_; - - // Flag indicating that the other side hung up. For ProducerQueues this - // triggers when BufferHub dies or explicitly closes the queue channel. For - // ConsumerQueues this can either mean the same or that the ProducerQueue on - // the other end hung up. - bool hung_up_{false}; - - // Global id for the queue that is consistent across processes. - int id_{-1}; - - // Buffer event callbacks - BufferAvailableCallback on_buffer_available_; - BufferRemovedCallback on_buffer_removed_; - - BufferHubQueue(const BufferHubQueue&) = delete; - void operator=(BufferHubQueue&) = delete; -}; - -class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { - public: - // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits - // in |usage_clear_mask| will be automatically masked off. Note that - // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but - // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer - // allocation through this producer queue shall not have any of the usage bits - // in |usage_deny_set_mask| set. Allocation calls violating this will be - // rejected. All buffer allocation through this producer queue must have all - // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating - // this will be rejected. Note that |usage_deny_set_mask| and - // |usage_deny_clear_mask| shall not conflict with each other. Such - // configuration will be treated as invalid input on creation. - static std::unique_ptr<ProducerQueue> Create( - const ProducerQueueConfig& config, const UsagePolicy& usage) { - return BASE::Create(config, usage); - } - - // Import a ProducerQueue from a channel handle. - static std::unique_ptr<ProducerQueue> Import(pdx::LocalChannelHandle handle) { - return BASE::Create(std::move(handle)); - } - - // Get a producer buffer. Note that the method doesn't check whether the - // buffer slot has a valid buffer that has been allocated already. When no - // buffer has been imported before it returns nullptr; otherwise it returns - // a shared pointer to a ProducerBuffer. - std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const { - return std::static_pointer_cast<ProducerBuffer>( - BufferHubQueue::GetBuffer(slot)); - } - - // Batch allocate buffers. Once allocated, producer buffers are automatically - // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED - // state). Upon success, returns a list of slots for each buffer allocated. - pdx::Status<std::vector<size_t>> AllocateBuffers( - uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t buffer_count); - - // Allocate producer buffer to populate the queue. Once allocated, a producer - // buffer is automatically enqueue'd into the ProducerQueue and available to - // use (i.e. in GAINED state). Upon success, returns the slot number for the - // buffer allocated. - pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height, - uint32_t layer_count, uint32_t format, - uint64_t usage); - - // Add a producer buffer to populate the queue. Once added, a producer buffer - // is available to use (i.e. in GAINED state). - pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer, - size_t slot); - - // Inserts a ProducerBuffer into the queue. On success, the method returns the - // |slot| number where the new buffer gets inserted. Note that the buffer - // being inserted should be in Gain'ed state prior to the call and it's - // considered as already Dequeued when the function returns. - pdx::Status<size_t> InsertBuffer( - const std::shared_ptr<ProducerBuffer>& buffer); - - // Remove producer buffer from the queue. - pdx::Status<void> RemoveBuffer(size_t slot) override; - - // Free all buffers on this producer queue. - pdx::Status<void> FreeAllBuffers() override { - return BufferHubQueue::FreeAllBuffers(); - } - - // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, - // and caller should call Post() once it's done writing to release the buffer - // to the consumer side. - // @return a buffer in gained state, which was originally in released state. - pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue( - int timeout, size_t* slot, pdx::LocalHandle* release_fence); - - // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, - // and caller should call Post() once it's done writing to release the buffer - // to the consumer side. - // - // @param timeout to dequeue a buffer. - // @param slot is the slot of the output ProducerBuffer. - // @param release_fence for gaining a buffer. - // @param out_meta metadata of the output buffer. - // @param gain_posted_buffer whether to gain posted buffer if no released - // buffer is available to gain. - // @return a buffer in gained state, which was originally in released state if - // gain_posted_buffer is false, or in posted/released state if - // gain_posted_buffer is true. - // TODO(b/112007999): gain_posted_buffer true is only used to prevent - // libdvrtracking from starving when there are non-responding clients. This - // gain_posted_buffer param can be removed once libdvrtracking start to use - // the new AHardwareBuffer API. - pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* release_fence, bool gain_posted_buffer = false); - - // Enqueues a producer buffer in the queue. - pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer, - size_t slot, uint64_t index) { - return BufferHubQueue::Enqueue({buffer, slot, index}); - } - - // Takes out the current producer queue as a binder parcelable object. Note - // that the queue must be empty to be exportable. After successful export, the - // producer queue client should no longer be used. - pdx::Status<ProducerQueueParcelable> TakeAsParcelable(); - - private: - friend BASE; - - // Constructors are automatically exposed through ProducerQueue::Create(...) - // static template methods inherited from ClientBase, which take the same - // arguments as the constructors. - explicit ProducerQueue(pdx::LocalChannelHandle handle); - ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage); - - // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, - // and caller should call Post() once it's done writing to release the buffer - // to the consumer side. - // - // @param slot the slot of the returned buffer. - // @return a buffer in gained state, which was originally in posted state or - // released state. - pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer( - size_t* slot); -}; - -class ConsumerQueue : public BufferHubQueue { - public: - // Get a consumer buffer. Note that the method doesn't check whether the - // buffer slot has a valid buffer that has been imported already. When no - // buffer has been imported before it returns nullptr; otherwise returns a - // shared pointer to a ConsumerBuffer. - std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const { - return std::static_pointer_cast<ConsumerBuffer>( - BufferHubQueue::GetBuffer(slot)); - } - - // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls - // whether or not buffers are set to be ignored when imported. This may be - // used to avoid participation in the buffer lifecycle by a consumer queue - // that is only used to spawn other consumer queues, such as in an - // intermediate service. - static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle); - - // Import newly created buffers from the service side. - // Returns number of buffers successfully imported or an error. - pdx::Status<size_t> ImportBuffers(); - - // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed - // mode, and caller should call Releasse() once it's done writing to release - // the buffer to the producer side. |meta| is passed along from BufferHub, - // The user of ProducerBuffer is responsible with making sure that the - // Dequeue() is done with the corect metadata type and size with those used - // when the buffer is orignally created. - template <typename Meta> - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) { - return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence); - } - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) { - return Dequeue(timeout, slot, nullptr, 0, acquire_fence); - } - - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, void* meta, size_t user_metadata_size, - pdx::LocalHandle* acquire_fence); - pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue( - int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* acquire_fence); - - private: - friend BufferHubQueue; - - explicit ConsumerQueue(pdx::LocalChannelHandle handle); - - // Add a consumer buffer to populate the queue. Once added, a consumer buffer - // is NOT available to use until the producer side |Post| it. |WaitForBuffers| - // will catch the |Post| and |Acquire| the buffer to make it available for - // consumer. - pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, - size_t slot); - - pdx::Status<void> OnBufferAllocated() override; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h deleted file mode 100644 index 36ab5f6ac7..0000000000 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ -#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -// The following headers are included without checking every warning. -// TODO(b/72172820): Remove the workaround once we have enforced -Weverything -// in these headers and their dependencies. -#include <pdx/channel_parcelable.h> - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -namespace android { -namespace dvr { - -enum BufferHubQueueParcelableMagic : uint32_t { - Producer = 0x62687170, // 'bhqp' - Consumer = 0x62687163, // 'bhqc' -}; - -template <BufferHubQueueParcelableMagic Magic> -class BufferHubQueueParcelable : public Parcelable { - public: - BufferHubQueueParcelable() = default; - - BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default; - BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept { - channel_parcelable_ = std::move(other.channel_parcelable_); - return *this; - } - - // Constructs an parcelable contains the channel parcelable. - explicit BufferHubQueueParcelable( - std::unique_ptr<pdx::ChannelParcelable> channel_parcelable) - : channel_parcelable_(std::move(channel_parcelable)) {} - - BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete; - void operator=(const BufferHubQueueParcelable&) = delete; - - bool IsValid() const; - - // Returns a channel handle constructed from this parcelable object and takes - // the ownership of all resources from the parcelable object. - pdx::LocalChannelHandle TakeChannelHandle(); - - // Serializes the queue parcelable into the given parcel. Note that no system - // resources are getting duplicated, nor did the parcel takes ownership of the - // queue parcelable. Thus, the parcelable object must remain valid for the - // lifetime of the parcel. - status_t writeToParcel(Parcel* parcel) const override; - - // Deserialize the queue parcelable from the given parcel. Note that system - // resources are duplicated from the parcel into the queue parcelable. Returns - // error if the targeting parcelable object is already valid. - status_t readFromParcel(const Parcel* parcel) override; - - private: - std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_; -}; - -using ProducerQueueParcelable = - BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>; -using ConsumerQueueParcelable = - BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h deleted file mode 100644 index 2f14f7cd91..0000000000 --- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ -#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ - -#include <android-base/unique_fd.h> -#include <log/log.h> -#include <sys/epoll.h> - -namespace android { -namespace dvr { - -class EpollFileDescriptor { - public: - static const int CTL_ADD = EPOLL_CTL_ADD; - static const int CTL_MOD = EPOLL_CTL_MOD; - static const int CTL_DEL = EPOLL_CTL_DEL; - - EpollFileDescriptor() : fd_(-1) {} - - // Constructs an EpollFileDescriptor from an integer file descriptor and - // takes ownership. - explicit EpollFileDescriptor(int fd) : fd_(fd) {} - - bool IsValid() const { return fd_.get() >= 0; } - - int Create() { - if (IsValid()) { - ALOGW("epoll fd has already been created."); - return -EALREADY; - } - - fd_.reset(epoll_create1(EPOLL_CLOEXEC)); - - if (fd_.get() < 0) - return -errno; - else - return 0; - } - - int Control(int op, int target_fd, epoll_event* ev) { - if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0) - return -errno; - else - return 0; - } - - int Wait(epoll_event* events, int maxevents, int timeout) { - int ret = epoll_wait(fd_.get(), events, maxevents, timeout); - - if (ret < 0) - return -errno; - else - return ret; - } - - int Get() const { return fd_.get(); } - - private: - base::unique_fd fd_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp deleted file mode 100644 index e373376ab9..0000000000 --- a/libs/vr/libbufferhubqueue/tests/Android.bp +++ /dev/null @@ -1,50 +0,0 @@ - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -header_libraries = [ - "libdvr_headers", -] - -shared_libraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libpdx_default_transport", -] - -static_libraries = [ - "libchrome", - "libdvrcommon", - "libperformance", -] - -cc_test { - srcs: ["buffer_hub_queue-test.cpp"], - header_libs: header_libraries, - static_libs: static_libraries, - shared_libs: shared_libraries, - cflags: [ - "-DLOG_TAG=\"buffer_hub_queue-test\"", - "-DTRACE=0", - "-O0", - "-g", - "-Wall", - "-Werror", - "-Wno-error=sign-compare", // to fix later - ], - name: "buffer_hub_queue-test", -} diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp deleted file mode 100644 index 6ae603b892..0000000000 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp +++ /dev/null @@ -1,1083 +0,0 @@ -#include <base/logging.h> -#include <binder/Parcel.h> -#include <dvr/dvr_api.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/producer_buffer.h> - -#include <gtest/gtest.h> -#include <poll.h> -#include <sys/eventfd.h> - -#include <vector> - -// Enable/disable debug logging. -#define TRACE 0 - -namespace android { -namespace dvr { - -using pdx::LocalChannelHandle; -using pdx::LocalHandle; - -namespace { - -constexpr uint32_t kBufferWidth = 100; -constexpr uint32_t kBufferHeight = 1; -constexpr uint32_t kBufferLayerCount = 1; -constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; -constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; -constexpr int kTimeoutMs = 100; -constexpr int kNoTimeout = 0; - -class BufferHubQueueTest : public ::testing::Test { - public: - bool CreateProducerQueue(const ProducerQueueConfig& config, - const UsagePolicy& usage) { - producer_queue_ = ProducerQueue::Create(config, usage); - return producer_queue_ != nullptr; - } - - bool CreateConsumerQueue() { - if (producer_queue_) { - consumer_queue_ = producer_queue_->CreateConsumerQueue(); - return consumer_queue_ != nullptr; - } else { - return false; - } - } - - bool CreateQueues(const ProducerQueueConfig& config, - const UsagePolicy& usage) { - return CreateProducerQueue(config, usage) && CreateConsumerQueue(); - } - - void AllocateBuffer(size_t* slot_out = nullptr) { - // Create producer buffer. - auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, - kBufferFormat, kBufferUsage); - - ASSERT_TRUE(status.ok()); - size_t slot = status.take(); - if (slot_out) - *slot_out = slot; - } - - bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) { - pollfd pfd{queue->queue_fd(), POLLIN, 0}; - int ret; - do { - ret = poll(&pfd, 1, timeout_ms); - } while (ret == -1 && errno == EINTR); - - if (ret < 0) { - ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(), - strerror(errno)); - return false; - } else if (ret == 0) { - return false; - } - return queue->HandleQueueEvents(); - } - - protected: - ProducerQueueConfigBuilder config_builder_; - std::unique_ptr<ProducerQueue> producer_queue_; - std::unique_ptr<ConsumerQueue> consumer_queue_; -}; - -TEST_F(BufferHubQueueTest, TestDequeue) { - const int64_t nb_dequeue_times = 16; - - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Allocate only one buffer. - AllocateBuffer(); - - // But dequeue multiple times. - for (int64_t i = 0; i < nb_dequeue_times; i++) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer gains a buffer. - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Producer posts the buffer. - mi.index = i; - EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); - - // Consumer acquires a buffer. - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - EXPECT_EQ(mi.index, i); - EXPECT_EQ(mo.index, i); - - // Consumer releases the buffer. - EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); - } -} - -TEST_F(BufferHubQueueTest, - TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Allocate 3 buffers to use. - const size_t test_queue_capacity = 3; - for (int64_t i = 0; i < test_queue_capacity; i++) { - AllocateBuffer(); - } - EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); - - size_t producer_slot, consumer_slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer posts 2 buffers and remember their posted sequence. - std::deque<size_t> posted_slots; - for (int64_t i = 0; i < 2; i++) { - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Producer should not be gaining posted buffer when there are still - // available buffers to gain. - auto found_iter = - std::find(posted_slots.begin(), posted_slots.end(), producer_slot); - EXPECT_EQ(found_iter, posted_slots.end()); - posted_slots.push_back(producer_slot); - - // Producer posts the buffer. - mi.index = i; - EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); - } - - // Consumer acquires one buffer. - auto c1_status = - consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - // Consumer should get the oldest posted buffer. No checks here. - // posted_slots[0] should be in acquired state now. - EXPECT_EQ(mo.index, 0); - // Consumer releases the buffer. - EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); - // posted_slots[0] should be in released state now. - - // Producer gain and post 2 buffers. - for (int64_t i = 0; i < 2; i++) { - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // The gained buffer should be the one in released state or the one haven't - // been use. - EXPECT_NE(posted_slots[1], producer_slot); - - mi.index = i + 2; - EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); - } - - // Producer gains a buffer. - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // The gained buffer should be the oldest posted buffer. - EXPECT_EQ(posted_slots[1], producer_slot); - - // Producer posts the buffer. - mi.index = 4; - EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); -} - -TEST_F(BufferHubQueueTest, - TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Allocate 4 buffers to use. - const size_t test_queue_capacity = 4; - for (int64_t i = 0; i < test_queue_capacity; i++) { - AllocateBuffer(); - } - EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); - - // Post all allowed buffers and remember their posted sequence. - std::deque<size_t> posted_slots; - for (int64_t i = 0; i < test_queue_capacity; i++) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer gains a buffer. - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Producer should not be gaining posted buffer when there are still - // available buffers to gain. - auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot); - EXPECT_EQ(found_iter, posted_slots.end()); - posted_slots.push_back(slot); - - // Producer posts the buffer. - mi.index = i; - EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); - } - - // Gain posted buffers in sequence. - const int64_t nb_dequeue_all_times = 2; - for (int j = 0; j < nb_dequeue_all_times; ++j) { - for (int i = 0; i < test_queue_capacity; ++i) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - // Producer gains a buffer. - auto p1_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // The gained buffer should be the oldest posted buffer. - EXPECT_EQ(posted_slots[i], slot); - - // Producer posts the buffer. - mi.index = i + test_queue_capacity * (j + 1); - EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); - } - } -} - -TEST_F(BufferHubQueueTest, TestProducerConsumer) { - const size_t kBufferCount = 16; - size_t slot; - DvrNativeBufferMetadata mi, mo; - LocalHandle fence; - - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - for (size_t i = 0; i < kBufferCount; i++) { - AllocateBuffer(); - - // Producer queue has all the available buffers on initialize. - ASSERT_EQ(producer_queue_->count(), i + 1); - ASSERT_EQ(producer_queue_->capacity(), i + 1); - - // Consumer queue has no avaiable buffer on initialize. - ASSERT_EQ(consumer_queue_->count(), 0U); - // Consumer queue does not import buffers until a dequeue is issued. - ASSERT_EQ(consumer_queue_->capacity(), i); - // Dequeue returns timeout since no buffer is ready to consumer, but - // this implicitly triggers buffer import and bump up |capacity|. - auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence); - ASSERT_FALSE(status.ok()); - ASSERT_EQ(ETIMEDOUT, status.error()); - ASSERT_EQ(consumer_queue_->capacity(), i + 1); - } - - // Use eventfd as a stand-in for a fence. - LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); - - for (size_t i = 0; i < kBufferCount; i++) { - // First time there is no buffer available to dequeue. - auto consumer_status = - consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence); - ASSERT_FALSE(consumer_status.ok()); - ASSERT_EQ(consumer_status.error(), ETIMEDOUT); - - // Make sure Producer buffer is POSTED so that it's ready to Accquire - // in the consumer's Dequeue() function. - auto producer_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - auto producer = producer_status.take(); - ASSERT_NE(nullptr, producer); - - mi.index = static_cast<int64_t>(i); - ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0); - - // Second time the just the POSTED buffer should be dequeued. - consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(consumer_status.ok()); - EXPECT_TRUE(fence.IsValid()); - - auto consumer = consumer_status.take(); - ASSERT_NE(nullptr, consumer); - ASSERT_EQ(mi.index, mo.index); - } -} - -TEST_F(BufferHubQueueTest, TestInsertBuffer) { - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - - consumer_queue_ = producer_queue_->CreateConsumerQueue(); - ASSERT_TRUE(consumer_queue_ != nullptr); - EXPECT_EQ(producer_queue_->capacity(), 0); - EXPECT_EQ(consumer_queue_->capacity(), 0); - - std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create( - kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0); - ASSERT_TRUE(p1 != nullptr); - ASSERT_EQ(p1->GainAsync(), 0); - - // Inserting a posted buffer will fail. - DvrNativeBufferMetadata meta; - EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0); - auto status_or_slot = producer_queue_->InsertBuffer(p1); - EXPECT_FALSE(status_or_slot.ok()); - EXPECT_EQ(status_or_slot.error(), EINVAL); - - // Inserting a gained buffer will succeed. - std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create( - kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage); - ASSERT_EQ(p2->GainAsync(), 0); - ASSERT_TRUE(p2 != nullptr); - status_or_slot = producer_queue_->InsertBuffer(p2); - EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage(); - // This is the first buffer inserted, should take slot 0. - size_t slot = status_or_slot.get(); - EXPECT_EQ(slot, 0); - - // Wait and expect the consumer to kick up the newly inserted buffer. - WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); - EXPECT_EQ(consumer_queue_->capacity(), 1ULL); -} - -TEST_F(BufferHubQueueTest, TestRemoveBuffer) { - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - DvrNativeBufferMetadata mo; - - // Allocate buffers. - const size_t kBufferCount = 4u; - for (size_t i = 0; i < kBufferCount; i++) { - AllocateBuffer(); - } - ASSERT_EQ(kBufferCount, producer_queue_->count()); - ASSERT_EQ(kBufferCount, producer_queue_->capacity()); - - consumer_queue_ = producer_queue_->CreateConsumerQueue(); - ASSERT_NE(nullptr, consumer_queue_); - - // Check that buffers are correctly imported on construction. - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_EQ(0u, consumer_queue_->count()); - - // Dequeue all the buffers and keep track of them in an array. This prevents - // the producer queue ring buffer ref counts from interfering with the tests. - struct Entry { - std::shared_ptr<ProducerBuffer> buffer; - LocalHandle fence; - size_t slot; - }; - std::array<Entry, kBufferCount> buffers; - - for (size_t i = 0; i < kBufferCount; i++) { - Entry* entry = &buffers[i]; - auto producer_status = - producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence); - ASSERT_TRUE(producer_status.ok()); - entry->buffer = producer_status.take(); - ASSERT_NE(nullptr, entry->buffer); - } - - // Remove a buffer and make sure both queues reflect the change. - ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot)); - EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); - - // As long as the removed buffer is still alive the consumer queue won't know - // its gone. - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Release the removed buffer. - buffers[0].buffer = nullptr; - - // Now the consumer queue should know it's gone. - EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs)); - ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity()); - - // Allocate a new buffer. This should take the first empty slot. - size_t slot; - AllocateBuffer(&slot); - ALOGE_IF(TRACE, "ALLOCATE %zu", slot); - EXPECT_EQ(buffers[0].slot, slot); - EXPECT_EQ(kBufferCount, producer_queue_->capacity()); - - // The consumer queue should pick up the new buffer. - EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Remove and allocate a buffer. - ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot)); - EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); - buffers[1].buffer = nullptr; - - AllocateBuffer(&slot); - ALOGE_IF(TRACE, "ALLOCATE %zu", slot); - EXPECT_EQ(buffers[1].slot, slot); - EXPECT_EQ(kBufferCount, producer_queue_->capacity()); - - // The consumer queue should pick up the new buffer but the count shouldn't - // change. - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Remove and allocate a buffer, but don't free the buffer right away. - ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot)); - EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); - - AllocateBuffer(&slot); - ALOGE_IF(TRACE, "ALLOCATE %zu", slot); - EXPECT_EQ(buffers[2].slot, slot); - EXPECT_EQ(kBufferCount, producer_queue_->capacity()); - - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - - // Release the producer buffer to trigger a POLLHUP event for an already - // removed buffer. - buffers[2].buffer = nullptr; - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); - EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); - EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); -} - -TEST_F(BufferHubQueueTest, TestMultipleConsumers) { - // ProducerConfigureBuilder doesn't set Metadata{size}, which means there - // is no metadata associated with this BufferQueue's buffer. - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - - // Allocate buffers. - const size_t kBufferCount = 4u; - for (size_t i = 0; i < kBufferCount; i++) { - AllocateBuffer(); - } - ASSERT_EQ(kBufferCount, producer_queue_->count()); - - // Build a silent consumer queue to test multi-consumer queue features. - auto silent_queue = producer_queue_->CreateSilentConsumerQueue(); - ASSERT_NE(nullptr, silent_queue); - - // Check that silent queue doesn't import buffers on creation. - EXPECT_EQ(silent_queue->capacity(), 0U); - - // Dequeue and post a buffer. - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - auto producer_status = - producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(producer_status.ok()); - auto producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0); - // After post, check the number of remaining available buffers. - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); - - // Currently we expect no buffer to be available prior to calling - // WaitForBuffers/HandleQueueEvents. - // TODO(eieio): Note this behavior may change in the future. - EXPECT_EQ(silent_queue->count(), 0U); - EXPECT_FALSE(silent_queue->HandleQueueEvents()); - EXPECT_EQ(silent_queue->count(), 0U); - - // Build a new consumer queue to test multi-consumer queue features. - consumer_queue_ = silent_queue->CreateConsumerQueue(); - ASSERT_NE(consumer_queue_, nullptr); - - // Check that buffers are correctly imported on construction. - EXPECT_EQ(consumer_queue_->capacity(), kBufferCount); - // Buffers are only imported, but their availability is not checked until - // first call to Dequeue(). - EXPECT_EQ(consumer_queue_->count(), 0U); - - // Reclaim released/ignored buffers. - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); - - usleep(10000); - WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs); - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); - - // Post another buffer. - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0); - - // Verify that the consumer queue receives it. - size_t consumer_queue_count = consumer_queue_->count(); - WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); - EXPECT_GT(consumer_queue_->count(), consumer_queue_count); - - // Save the current consumer queue buffer count to compare after the dequeue. - consumer_queue_count = consumer_queue_->count(); - - // Dequeue and acquire/release (discard) buffers on the consumer end. - auto consumer_status = - consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(consumer_status.ok()); - auto consumer_buffer = consumer_status.take(); - ASSERT_NE(consumer_buffer, nullptr); - consumer_buffer->Discard(); - - // Buffer should be returned to the producer queue without being handled by - // the silent consumer queue. - EXPECT_LT(consumer_queue_->count(), consumer_queue_count); - EXPECT_EQ(producer_queue_->count(), kBufferCount - 2); - - WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs); - EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); -} - -struct TestUserMetadata { - char a; - int32_t b; - int64_t c; -}; - -constexpr uint64_t kUserMetadataSize = - static_cast<uint64_t>(sizeof(TestUserMetadata)); - -TEST_F(BufferHubQueueTest, TestUserMetadata) { - ASSERT_TRUE(CreateQueues( - config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{})); - - AllocateBuffer(); - - std::vector<TestUserMetadata> user_metadata_list = { - {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}}; - - for (auto user_metadata : user_metadata_list) { - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // TODO(b/69469185): Test against metadata from consumer once we implement - // release metadata properly. - // EXPECT_EQ(mo.user_metadata_ptr, 0U); - // EXPECT_EQ(mo.user_metadata_size, 0U); - - mi.user_metadata_size = kUserMetadataSize; - mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); - EXPECT_EQ(p1->PostAsync(&mi, {}), 0); - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - - EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize); - auto out_user_metadata = - reinterpret_cast<TestUserMetadata*>(mo.user_metadata_ptr); - EXPECT_EQ(user_metadata.a, out_user_metadata->a); - EXPECT_EQ(user_metadata.b, out_user_metadata->b); - EXPECT_EQ(user_metadata.c, out_user_metadata->c); - - // When release, empty metadata is also legit. - mi.user_metadata_size = 0U; - mi.user_metadata_ptr = 0U; - c1->ReleaseAsync(&mi, {}); - } -} - -TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) { - ASSERT_TRUE(CreateQueues( - config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{})); - - AllocateBuffer(); - - TestUserMetadata user_metadata; - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // Post with mismatched user metadata size will fail. But the producer buffer - // itself should stay untouched. - mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); - mi.user_metadata_size = kUserMetadataSize + 1; - EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG); - // Post with the exact same user metdata size can success. - mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); - mi.user_metadata_size = kUserMetadataSize; - EXPECT_EQ(p1->PostAsync(&mi, {}), 0); -} - -TEST_F(BufferHubQueueTest, TestEnqueue) { - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(), - UsagePolicy{})); - AllocateBuffer(); - - size_t slot; - LocalHandle fence; - DvrNativeBufferMetadata mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(nullptr, p1); - - producer_queue_->Enqueue(p1, slot, 0ULL); - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_FALSE(c1_status.ok()); -} - -TEST_F(BufferHubQueueTest, TestAllocateBuffer) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - size_t ps1; - AllocateBuffer(); - LocalHandle fence; - DvrNativeBufferMetadata mi, mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_NE(p1, nullptr); - - // producer queue is exhausted - size_t ps2; - auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence); - ASSERT_FALSE(p2_status.ok()); - ASSERT_EQ(ETIMEDOUT, p2_status.error()); - - // dynamically add buffer. - AllocateBuffer(); - ASSERT_EQ(producer_queue_->count(), 1U); - ASSERT_EQ(producer_queue_->capacity(), 2U); - - // now we can dequeue again - p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence); - ASSERT_TRUE(p2_status.ok()); - auto p2 = p2_status.take(); - ASSERT_NE(p2, nullptr); - ASSERT_EQ(producer_queue_->count(), 0U); - // p1 and p2 should have different slot number - ASSERT_NE(ps1, ps2); - - // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers| - // are called. So far consumer_queue_ should be empty. - ASSERT_EQ(consumer_queue_->count(), 0U); - - int64_t seq = 1; - mi.index = seq; - ASSERT_EQ(p1->PostAsync(&mi, {}), 0); - - size_t cs1, cs2; - auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence); - ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); - auto c1 = c1_status.take(); - ASSERT_NE(c1, nullptr); - ASSERT_EQ(consumer_queue_->count(), 0U); - ASSERT_EQ(consumer_queue_->capacity(), 2U); - ASSERT_EQ(cs1, ps1); - - ASSERT_EQ(p2->PostAsync(&mi, {}), 0); - auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence); - ASSERT_TRUE(c2_status.ok()); - auto c2 = c2_status.take(); - ASSERT_NE(c2, nullptr); - ASSERT_EQ(cs2, ps2); -} - -TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - ASSERT_EQ(producer_queue_->capacity(), 0); - auto status = producer_queue_->AllocateBuffers( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage, /*buffer_count=*/2); - ASSERT_TRUE(status.ok()); - std::vector<size_t> buffer_slots = status.take(); - ASSERT_EQ(buffer_slots.size(), 2); - ASSERT_EQ(producer_queue_->capacity(), 2); -} - -TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - ASSERT_EQ(producer_queue_->capacity(), 0); - auto status = producer_queue_->AllocateBuffers( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage, /*buffer_count=*/0); - ASSERT_TRUE(status.ok()); - std::vector<size_t> buffer_slots = status.take(); - ASSERT_EQ(buffer_slots.size(), 0); - ASSERT_EQ(producer_queue_->capacity(), 0); -} - -TEST_F(BufferHubQueueTest, TestUsageSetMask) { - const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE( - CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0})); - - // When allocation, leave out |set_mask| from usage bits on purpose. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage & ~set_mask); - ASSERT_TRUE(status.ok()); - - LocalHandle fence; - size_t slot; - DvrNativeBufferMetadata mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_EQ(p1->usage() & set_mask, set_mask); -} - -TEST_F(BufferHubQueueTest, TestUsageClearMask) { - const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE( - CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0})); - - // When allocation, add |clear_mask| into usage bits on purpose. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage | clear_mask); - ASSERT_TRUE(status.ok()); - - LocalHandle fence; - size_t slot; - DvrNativeBufferMetadata mo; - auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(p1_status.ok()); - auto p1 = p1_status.take(); - ASSERT_EQ(p1->usage() & clear_mask, 0U); -} - -TEST_F(BufferHubQueueTest, TestUsageDenySetMask) { - const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(), - UsagePolicy{0, 0, deny_set_mask, 0})); - - // Now that |deny_set_mask| is illegal, allocation without those bits should - // be able to succeed. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage & ~deny_set_mask); - ASSERT_TRUE(status.ok()); - - // While allocation with those bits should fail. - status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, kBufferFormat, - kBufferUsage | deny_set_mask); - ASSERT_FALSE(status.ok()); - ASSERT_EQ(EINVAL, status.error()); -} - -TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) { - const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(), - UsagePolicy{0, 0, 0, deny_clear_mask})); - - // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are - // mandatory), allocation with those bits should be able to succeed. - auto status = producer_queue_->AllocateBuffer( - kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, - kBufferUsage | deny_clear_mask); - ASSERT_TRUE(status.ok()); - - // While allocation without those bits should fail. - status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, kBufferFormat, - kBufferUsage & ~deny_clear_mask); - ASSERT_FALSE(status.ok()); - ASSERT_EQ(EINVAL, status.error()); -} - -TEST_F(BufferHubQueueTest, TestQueueInfo) { - static const bool kIsAsync = true; - ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync) - .SetDefaultWidth(kBufferWidth) - .SetDefaultHeight(kBufferHeight) - .SetDefaultFormat(kBufferFormat) - .Build(), - UsagePolicy{})); - - EXPECT_EQ(producer_queue_->default_width(), kBufferWidth); - EXPECT_EQ(producer_queue_->default_height(), kBufferHeight); - EXPECT_EQ(producer_queue_->default_format(), kBufferFormat); - EXPECT_EQ(producer_queue_->is_async(), kIsAsync); - - EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth); - EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight); - EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat); - EXPECT_EQ(consumer_queue_->is_async(), kIsAsync); -} - -TEST_F(BufferHubQueueTest, TestFreeAllBuffers) { - constexpr size_t kBufferCount = 2; - -#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers) \ - EXPECT_EQ(consumer_queue_->count(), 0U); \ - EXPECT_EQ(consumer_queue_->capacity(), 0U); \ - EXPECT_EQ(producer_queue_->count(), 0U); \ - EXPECT_EQ(producer_queue_->capacity(), 0U); \ - for (size_t i = 0; i < num_buffers; i++) { \ - AllocateBuffer(); \ - } \ - EXPECT_EQ(producer_queue_->count(), num_buffers); \ - EXPECT_EQ(producer_queue_->capacity(), num_buffers); - - size_t slot; - LocalHandle fence; - pdx::Status<void> status; - pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status; - pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status; - std::shared_ptr<ConsumerBuffer> consumer_buffer; - std::shared_ptr<ProducerBuffer> producer_buffer; - DvrNativeBufferMetadata mi, mo; - - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - // Free all buffers when buffers are avaible for dequeue. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when one buffer is dequeued. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when all buffers are dequeued. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - for (size_t i = 0; i < kBufferCount; i++) { - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - } - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when one buffer is posted. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(nullptr, producer_buffer); - ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence)); - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when all buffers are posted. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - for (size_t i = 0; i < kBufferCount; i++) { - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); - } - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // Free all buffers when all buffers are acquired. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - for (size_t i = 0; i < kBufferCount; i++) { - producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(producer_status.ok()); - producer_buffer = producer_status.take(); - ASSERT_NE(producer_buffer, nullptr); - ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); - consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage(); - } - - status = producer_queue_->FreeAllBuffers(); - EXPECT_TRUE(status.ok()); - - // In addition to FreeAllBuffers() from the queue, it is also required to - // delete all references to the ProducerBuffer (i.e. the PDX client). - producer_buffer = nullptr; - - // Crank consumer queue events to pickup EPOLLHUP events on the queue. - consumer_queue_->HandleQueueEvents(); - - // One last check. - CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); - -#undef CHECK_NO_BUFFER_THEN_ALLOCATE -} - -TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) { - ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(), - UsagePolicy{})); - - // Allocate only one buffer. - AllocateBuffer(); - - // Export should fail as the queue is not empty. - auto status = producer_queue_->TakeAsParcelable(); - EXPECT_FALSE(status.ok()); -} - -TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { - ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); - - auto s1 = producer_queue_->TakeAsParcelable(); - EXPECT_TRUE(s1.ok()); - - ProducerQueueParcelable output_parcelable = s1.take(); - EXPECT_TRUE(output_parcelable.IsValid()); - - Parcel parcel; - status_t res; - res = output_parcelable.writeToParcel(&parcel); - EXPECT_EQ(res, OK); - - // After written into parcelable, the output_parcelable is still valid has - // keeps the producer channel alive. - EXPECT_TRUE(output_parcelable.IsValid()); - - // Creating producer buffer should fail. - auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, - kBufferLayerCount, kBufferFormat, - kBufferUsage); - ASSERT_FALSE(s2.ok()); - - // Reset the data position so that we can read back from the same parcel - // without doing actually Binder IPC. - parcel.setDataPosition(0); - producer_queue_ = nullptr; - - // Recreate the producer queue from the parcel. - ProducerQueueParcelable input_parcelable; - EXPECT_FALSE(input_parcelable.IsValid()); - - res = input_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, OK); - EXPECT_TRUE(input_parcelable.IsValid()); - - EXPECT_EQ(producer_queue_, nullptr); - producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle()); - EXPECT_FALSE(input_parcelable.IsValid()); - ASSERT_NE(producer_queue_, nullptr); - - // Newly created queue from the parcel can allocate buffer, post buffer to - // consumer. - EXPECT_NO_FATAL_FAILURE(AllocateBuffer()); - EXPECT_EQ(producer_queue_->count(), 1U); - EXPECT_EQ(producer_queue_->capacity(), 1U); - - size_t slot; - DvrNativeBufferMetadata producer_meta; - DvrNativeBufferMetadata consumer_meta; - LocalHandle fence; - auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence); - EXPECT_TRUE(s3.ok()); - - std::shared_ptr<ProducerBuffer> p1 = s3.take(); - ASSERT_NE(p1, nullptr); - - producer_meta.timestamp = 42; - EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0); - - // Make sure the buffer can be dequeued from consumer side. - auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); - EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage(); - EXPECT_EQ(consumer_queue_->capacity(), 1U); - - auto consumer = s4.take(); - ASSERT_NE(consumer, nullptr); - EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp); -} - -TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { - ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); - - auto s1 = producer_queue_->CreateConsumerQueueParcelable(); - EXPECT_TRUE(s1.ok()); - ConsumerQueueParcelable output_parcelable = s1.take(); - EXPECT_TRUE(output_parcelable.IsValid()); - - // Write to a Parcel new object. - Parcel parcel; - status_t res; - res = output_parcelable.writeToParcel(&parcel); - - // Reset the data position so that we can read back from the same parcel - // without doing actually Binder IPC. - parcel.setDataPosition(0); - - // No consumer queue created yet. - EXPECT_EQ(consumer_queue_, nullptr); - - // If the parcel contains a consumer queue, read into a - // ProducerQueueParcelable should fail. - ProducerQueueParcelable wrongly_typed_parcelable; - EXPECT_FALSE(wrongly_typed_parcelable.IsValid()); - res = wrongly_typed_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, -EINVAL); - parcel.setDataPosition(0); - - // Create the consumer queue from the parcel. - ConsumerQueueParcelable input_parcelable; - EXPECT_FALSE(input_parcelable.IsValid()); - - res = input_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, OK); - EXPECT_TRUE(input_parcelable.IsValid()); - - consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle()); - EXPECT_FALSE(input_parcelable.IsValid()); - ASSERT_NE(consumer_queue_, nullptr); - - EXPECT_NO_FATAL_FAILURE(AllocateBuffer()); - EXPECT_EQ(producer_queue_->count(), 1U); - EXPECT_EQ(producer_queue_->capacity(), 1U); - - size_t slot; - DvrNativeBufferMetadata producer_meta; - DvrNativeBufferMetadata consumer_meta; - LocalHandle fence; - auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence); - EXPECT_TRUE(s2.ok()); - - std::shared_ptr<ProducerBuffer> p1 = s2.take(); - ASSERT_NE(p1, nullptr); - - producer_meta.timestamp = 42; - EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0); - - // Make sure the buffer can be dequeued from consumer side. - auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); - EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage(); - EXPECT_EQ(consumer_queue_->capacity(), 1U); - - auto consumer = s3.take(); - ASSERT_NE(consumer, nullptr); - EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp); -} - -} // namespace - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp deleted file mode 100644 index b0ed950c51..0000000000 --- a/libs/vr/libdisplay/Android.bp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2015 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. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -sourceFiles = [ - "display_client.cpp", - "display_manager_client.cpp", - "display_protocol.cpp", - "shared_buffer_helpers.cpp", - "vsync_service.cpp", -] - -localIncludeFiles = [ - "include", -] - -sharedLibraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "liblog", - "libutils", - "libui", - "libgui", - "libhardware", - "libsync", - "libnativewindow", - "libpdx_default_transport", -] - -staticLibraries = [ - "libdvrcommon", - "libbroadcastring", -] - -headerLibraries = [ - "vulkan_headers", - "libdvr_headers", -] - -cc_library { - srcs: sourceFiles, - cflags: ["-DLOG_TAG=\"libdisplay\"", - "-DTRACE=0", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-DGL_GLEXT_PROTOTYPES", - "-DEGL_EGLEXT_PROTOTYPES", - "-Wall", - "-Werror", - ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ], - export_include_dirs: localIncludeFiles, - shared_libs: sharedLibraries, - static_libs: staticLibraries, - header_libs: headerLibraries, - export_header_lib_headers: headerLibraries, - - name: "libdisplay", -} diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp deleted file mode 100644 index 62856dfbf8..0000000000 --- a/libs/vr/libdisplay/display_client.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include "include/private/dvr/display_client.h" - -#include <cutils/native_handle.h> -#include <log/log.h> -#include <pdx/default_transport/client_channel.h> -#include <pdx/default_transport/client_channel_factory.h> -#include <pdx/status.h> - -#include <mutex> - -#include <private/dvr/display_protocol.h> - -using android::pdx::ErrorStatus; -using android::pdx::LocalHandle; -using android::pdx::LocalChannelHandle; -using android::pdx::Status; -using android::pdx::Transaction; -using android::pdx::rpc::IfAnyOf; - -namespace android { -namespace dvr { -namespace display { - -Surface::Surface(LocalChannelHandle channel_handle, int* error) - : BASE{pdx::default_transport::ClientChannel::Create( - std::move(channel_handle))} { - auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>(); - if (!status) { - ALOGE("Surface::Surface: Failed to get surface info: %s", - status.GetErrorMessage().c_str()); - Close(status.error()); - if (error) - *error = status.error(); - } - - surface_id_ = status.get().surface_id; - z_order_ = status.get().z_order; - visible_ = status.get().visible; -} - -Surface::Surface(const SurfaceAttributes& attributes, int* error) - : BASE{pdx::default_transport::ClientChannelFactory::Create( - DisplayProtocol::kClientPath), - kInfiniteTimeout} { - auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes); - if (!status) { - ALOGE("Surface::Surface: Failed to create display surface: %s", - status.GetErrorMessage().c_str()); - Close(status.error()); - if (error) - *error = status.error(); - } - - surface_id_ = status.get().surface_id; - z_order_ = status.get().z_order; - visible_ = status.get().visible; -} - -Status<void> Surface::SetVisible(bool visible) { - return SetAttributes( - {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}}); -} - -Status<void> Surface::SetZOrder(int z_order) { - return SetAttributes( - {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}}); -} - -Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) { - auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes); - if (!status) { - ALOGE( - "Surface::SetAttributes: Failed to set display surface " - "attributes: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - // Set the local cached copies of the attributes we care about from the full - // set of attributes sent to the display service. - for (const auto& attribute : attributes) { - const auto& key = attribute.first; - const auto* variant = &attribute.second; - bool invalid_value = false; - switch (key) { - case SurfaceAttribute::Visible: - invalid_value = - !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_); - break; - case SurfaceAttribute::ZOrder: - invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_); - break; - } - - if (invalid_value) { - ALOGW( - "Surface::SetAttributes: Failed to set display surface " - "attribute %d because of incompatible type: %d", - key, variant->index()); - } - } - - return {}; -} - -Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue( - uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) { - ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue."); - auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>( - ProducerQueueConfigBuilder() - .SetDefaultWidth(width) - .SetDefaultHeight(height) - .SetDefaultFormat(format) - .SetMetadataSize(metadata_size) - .Build()); - if (!status) { - ALOGE("Surface::CreateQueue: Failed to create queue: %s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto producer_queue = ProducerQueue::Import(status.take()); - if (!producer_queue) { - ALOGE("Surface::CreateQueue: Failed to import producer queue!"); - return ErrorStatus(ENOMEM); - } - - return {std::move(producer_queue)}; -} - -Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue( - uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, - uint64_t usage, size_t capacity, size_t metadata_size) { - ALOGD_IF(TRACE, - "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u " - "usage=%" PRIx64 " capacity=%zu", - width, height, layer_count, format, usage, capacity); - auto status = CreateQueue(width, height, format, metadata_size); - if (!status) - return status.error_status(); - - auto producer_queue = status.take(); - - ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity); - auto allocate_status = producer_queue->AllocateBuffers( - width, height, layer_count, format, usage, capacity); - if (!allocate_status) { - ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s", - producer_queue->id(), allocate_status.GetErrorMessage().c_str()); - return allocate_status.error_status(); - } - - return {std::move(producer_queue)}; -} - -DisplayClient::DisplayClient(int* error) - : BASE(pdx::default_transport::ClientChannelFactory::Create( - DisplayProtocol::kClientPath), - kInfiniteTimeout) { - if (error) - *error = Client::error(); -} - -Status<Metrics> DisplayClient::GetDisplayMetrics() { - return InvokeRemoteMethod<DisplayProtocol::GetMetrics>(); -} - -Status<std::string> DisplayClient::GetConfigurationData( - ConfigFileType config_type) { - auto status = - InvokeRemoteMethod<DisplayProtocol::GetConfigurationData>(config_type); - if (!status && status.error() != ENOENT) { - ALOGE( - "DisplayClient::GetConfigurationData: Unable to get" - "configuration data. Error: %s", - status.GetErrorMessage().c_str()); - } - return status; -} - -Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() { - return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(); -} - -Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface( - const SurfaceAttributes& attributes) { - int error; - if (auto client = Surface::Create(attributes, &error)) - return {std::move(client)}; - else - return ErrorStatus(error); -} - -pdx::Status<std::unique_ptr<IonBuffer>> DisplayClient::SetupGlobalBuffer( - DvrGlobalBufferKey key, size_t size, uint64_t usage) { - auto status = - InvokeRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(key, size, usage); - if (!status) { - ALOGE( - "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer " - "%s", - status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto ion_buffer = std::make_unique<IonBuffer>(); - auto native_buffer_handle = status.take(); - const int ret = native_buffer_handle.Import(ion_buffer.get()); - if (ret < 0) { - ALOGE( - "DisplayClient::GetGlobalBuffer: Failed to import global buffer: " - "key=%d; error=%s", - key, strerror(-ret)); - return ErrorStatus(-ret); - } - - return {std::move(ion_buffer)}; -} - -pdx::Status<void> DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) { - auto status = InvokeRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(key); - if (!status) { - ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s", - status.GetErrorMessage().c_str()); - } - - return status; -} - -Status<std::unique_ptr<IonBuffer>> DisplayClient::GetGlobalBuffer( - DvrGlobalBufferKey key) { - auto status = InvokeRemoteMethod<DisplayProtocol::GetGlobalBuffer>(key); - if (!status) { - ALOGE( - "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; " - "error=%s", - key, status.GetErrorMessage().c_str()); - return status.error_status(); - } - - auto ion_buffer = std::make_unique<IonBuffer>(); - auto native_buffer_handle = status.take(); - const int ret = native_buffer_handle.Import(ion_buffer.get()); - if (ret < 0) { - ALOGE( - "DisplayClient::GetGlobalBuffer: Failed to import global buffer: " - "key=%d; error=%s", - key, strerror(-ret)); - return ErrorStatus(-ret); - } - - return {std::move(ion_buffer)}; -} - -Status<bool> DisplayClient::IsVrAppRunning() { - return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>(); -} - -} // namespace display -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp deleted file mode 100644 index fdeeb70dfb..0000000000 --- a/libs/vr/libdisplay/display_manager_client.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "include/private/dvr/display_manager_client.h" - -#include <pdx/default_transport/client_channel_factory.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/display_protocol.h> -#include <utils/Log.h> - -using android::pdx::ErrorStatus; -using android::pdx::LocalChannelHandle; -using android::pdx::Transaction; - -namespace android { -namespace dvr { -namespace display { - -DisplayManagerClient::DisplayManagerClient() - : BASE(pdx::default_transport::ClientChannelFactory::Create( - DisplayManagerProtocol::kClientPath)) {} - -DisplayManagerClient::~DisplayManagerClient() {} - -pdx::Status<std::vector<display::SurfaceState>> -DisplayManagerClient::GetSurfaceState() { - auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(); - if (!status) { - ALOGE( - "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s", - status.GetErrorMessage().c_str()); - } - - return status; -} - -pdx::Status<std::unique_ptr<ConsumerQueue>> -DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) { - auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>( - surface_id, queue_id); - if (!status) { - ALOGE( - "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for " - "surface_id=%d queue_id=%d: %s", - surface_id, queue_id, status.GetErrorMessage().c_str()); - return status.error_status(); - } - - return {ConsumerQueue::Import(status.take())}; -} - -} // namespace display -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp deleted file mode 100644 index 773f9a5aa3..0000000000 --- a/libs/vr/libdisplay/display_protocol.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "include/private/dvr/display_protocol.h" - -namespace android { -namespace dvr { -namespace display { - -constexpr char DisplayProtocol::kClientPath[]; -constexpr char DisplayManagerProtocol::kClientPath[]; -constexpr char VSyncProtocol::kClientPath[]; - -} // namespace display -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg deleted file mode 100644 index 2f8a3c018c..0000000000 --- a/libs/vr/libdisplay/include/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -filter=-build/header_guard diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h deleted file mode 100644 index 81546ac5c2..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/display_client.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_ -#define ANDROID_DVR_DISPLAY_CLIENT_H_ - -#include <dvr/dvr_api.h> -#include <hardware/hwcomposer.h> -#include <pdx/client.h> -#include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/display_protocol.h> - -namespace android { -namespace dvr { -namespace display { - -class Surface : public pdx::ClientBase<Surface> { - public: - // Utility named constructor. This can be removed once ClientBase::Create is - // refactored to return Status<T> types. - static pdx::Status<std::unique_ptr<Surface>> CreateSurface( - const SurfaceAttributes& attributes) { - int error; - pdx::Status<std::unique_ptr<Surface>> status; - if (auto surface = Create(attributes, &error)) - status.SetValue(std::move(surface)); - else - status.SetError(error); - return status; - } - - int surface_id() const { return surface_id_; } - int z_order() const { return z_order_; } - bool visible() const { return visible_; } - - pdx::Status<void> SetVisible(bool visible); - pdx::Status<void> SetZOrder(int z_order); - pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes); - - // Creates an empty queue. - pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width, - uint32_t height, - uint32_t format, - size_t metadata_size); - - // Creates a queue and populates it with |capacity| buffers of the specified - // parameters. - pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width, - uint32_t height, - uint32_t layer_count, - uint32_t format, - uint64_t usage, - size_t capacity, - size_t metadata_size); - - private: - friend BASE; - - int surface_id_ = -1; - int z_order_ = 0; - bool visible_ = false; - - // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create() - // returns Status<T>. - explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr); - explicit Surface(pdx::LocalChannelHandle channel_handle, - int* error = nullptr); - - Surface(const Surface&) = delete; - void operator=(const Surface&) = delete; -}; - -class DisplayClient : public pdx::ClientBase<DisplayClient> { - public: - pdx::Status<Metrics> GetDisplayMetrics(); - pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type); - pdx::Status<uint8_t> GetDisplayIdentificationPort(); - pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer( - DvrGlobalBufferKey key, size_t size, uint64_t usage); - pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key); - pdx::Status<std::unique_ptr<IonBuffer>> GetGlobalBuffer( - DvrGlobalBufferKey key); - pdx::Status<std::unique_ptr<Surface>> CreateSurface( - const SurfaceAttributes& attributes); - - // Temporary query for current VR status. Will be removed later. - pdx::Status<bool> IsVrAppRunning(); - - private: - friend BASE; - - explicit DisplayClient(int* error = nullptr); - - DisplayClient(const DisplayClient&) = delete; - void operator=(const DisplayClient&) = delete; -}; - -} // namespace display -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DISPLAY_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h deleted file mode 100644 index 45aef51baf..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ -#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ - -#include <string> -#include <vector> - -#include <pdx/client.h> -#include <pdx/status.h> -#include <private/dvr/display_protocol.h> - -namespace android { -namespace dvr { - -class IonBuffer; -class ConsumerQueue; - -namespace display { - -class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> { - public: - ~DisplayManagerClient() override; - - pdx::Status<std::vector<SurfaceState>> GetSurfaceState(); - pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id, - int queue_id); - - using Client::event_fd; - - pdx::Status<int> GetEventMask(int events) { - if (auto* client_channel = GetChannel()) - return client_channel->GetEventMask(events); - else - return pdx::ErrorStatus(EINVAL); - } - - private: - friend BASE; - - DisplayManagerClient(); - - DisplayManagerClient(const DisplayManagerClient&) = delete; - void operator=(const DisplayManagerClient&) = delete; -}; - -} // namespace display -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h deleted file mode 100644 index 9f4cc4afcc..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h +++ /dev/null @@ -1,304 +0,0 @@ -#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_ -#define ANDROID_DVR_DISPLAY_PROTOCOL_H_ - -#include <sys/types.h> - -#include <array> -#include <map> - -#include <dvr/dvr_display_types.h> - -#include <dvr/dvr_api.h> -#include <pdx/rpc/buffer_wrapper.h> -#include <pdx/rpc/remote_method.h> -#include <pdx/rpc/serializable.h> -#include <pdx/rpc/variant.h> -#include <private/dvr/bufferhub_rpc.h> - -// RPC protocol definitions for DVR display services (VrFlinger). - -namespace android { -namespace dvr { -namespace display { - -// Native display metrics. -struct Metrics { - // Basic display properties. - uint32_t display_width; - uint32_t display_height; - uint32_t display_x_dpi; - uint32_t display_y_dpi; - uint32_t vsync_period_ns; - - // HMD metrics. - // TODO(eieio): Determine how these fields should be populated. On phones - // these values are determined at runtime by VrCore based on which headset the - // phone is in. On dedicated hardware this needs to come from somewhere else. - // Perhaps these should be moved to a separate structure that is returned by a - // separate runtime call. - uint32_t distorted_width; - uint32_t distorted_height; - uint32_t hmd_ipd_mm; - float inter_lens_distance_m; - std::array<float, 4> left_fov_lrbt; - std::array<float, 4> right_fov_lrbt; - - private: - PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height, - display_x_dpi, display_y_dpi, vsync_period_ns, - distorted_width, distorted_height, hmd_ipd_mm, - inter_lens_distance_m, left_fov_lrbt, - right_fov_lrbt); -}; - -// Serializable base type for enum structs. Enum structs are easier to use than -// enum classes, especially for bitmasks. This base type provides common -// utilities for flags types. -template <typename Integer> -class Flags { - public: - using Base = Flags<Integer>; - using Type = Integer; - - // NOLINTNEXTLINE(google-explicit-constructor) - Flags(const Integer& value) : value_{value} {} - Flags(const Flags&) = default; - Flags& operator=(const Flags&) = default; - - Integer value() const { return value_; } - // NOLINTNEXTLINE(google-explicit-constructor) - operator Integer() const { return value_; } - - bool IsSet(Integer bits) const { return (value_ & bits) == bits; } - bool IsClear(Integer bits) const { return (value_ & bits) == 0; } - - void Set(Integer bits) { value_ |= bits; } - void Clear(Integer bits) { value_ &= ~bits; } - - Integer operator|(Integer bits) const { return value_ | bits; } - Integer operator&(Integer bits) const { return value_ & bits; } - - Flags& operator|=(Integer bits) { - value_ |= bits; - return *this; - } - Flags& operator&=(Integer bits) { - value_ &= bits; - return *this; - } - - private: - Integer value_; - - PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_); -}; - -// Flags indicating what changed since last update. -struct SurfaceUpdateFlags : public Flags<uint32_t> { - enum : Type { - None = DVR_SURFACE_UPDATE_FLAGS_NONE, - NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE, - BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED, - VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED, - AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED, - }; - - SurfaceUpdateFlags() : Base{None} {} - using Base::Base; -}; - -// Surface attribute key/value types. -using SurfaceAttributeKey = int32_t; -using SurfaceAttributeValue = - pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>, - std::array<float, 3>, std::array<float, 4>, - std::array<float, 8>, std::array<float, 16>>; - -// Defined surface attribute keys. -struct SurfaceAttribute : public Flags<SurfaceAttributeKey> { - enum : Type { - // Keys in the negative integer space are interpreted by VrFlinger for - // direct surfaces. - Direct = DVR_SURFACE_ATTRIBUTE_DIRECT, - ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER, - Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE, - - // Invalid key. May be used to terminate C style lists in public API code. - Invalid = DVR_SURFACE_ATTRIBUTE_INVALID, - - // Positive keys are interpreted by the compositor only. - FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY, - }; - - SurfaceAttribute() : Base{Invalid} {} - using Base::Base; -}; - -// Collection of surface attribute key/value pairs. -using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>; - -struct SurfaceState { - int32_t surface_id; - int32_t process_id; - int32_t user_id; - - SurfaceAttributes surface_attributes; - SurfaceUpdateFlags update_flags; - std::vector<int32_t> queue_ids; - - // Convenience accessors. - bool GetVisible() const { - bool bool_value = false; - GetAttribute(SurfaceAttribute::Visible, &bool_value, - ValidTypes<int32_t, int64_t, bool, float>{}); - return bool_value; - } - - int GetZOrder() const { - int int_value = 0; - GetAttribute(SurfaceAttribute::ZOrder, &int_value, - ValidTypes<int32_t, int64_t, float>{}); - return int_value; - } - - private: - template <typename... Types> - struct ValidTypes {}; - - template <typename ReturnType, typename... Types> - bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value, - ValidTypes<Types...>) const { - auto search = surface_attributes.find(key); - if (search != surface_attributes.end()) - return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value); - else - return false; - } - - PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id, - surface_attributes, update_flags, queue_ids); -}; - -struct SurfaceInfo { - int surface_id; - bool visible; - int z_order; - - private: - PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order); -}; - -enum class ConfigFileType : uint32_t { - kLensMetrics, - kDeviceMetrics, - kDeviceConfiguration, - kDeviceEdid -}; - -struct DisplayProtocol { - // Service path. - static constexpr char kClientPath[] = "system/vr/display/client"; - - // Op codes. - enum { - kOpGetMetrics = 0, - kOpGetConfigurationData, - kOpSetupGlobalBuffer, - kOpDeleteGlobalBuffer, - kOpGetGlobalBuffer, - kOpIsVrAppRunning, - kOpCreateSurface, - kOpGetSurfaceInfo, - kOpCreateQueue, - kOpSetAttributes, - kOpGetDisplayIdentificationPort, - }; - - // Aliases. - using LocalChannelHandle = pdx::LocalChannelHandle; - using Void = pdx::rpc::Void; - - // Methods. - PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void)); - PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData, - std::string(ConfigFileType config_type)); - PDX_REMOTE_METHOD(GetDisplayIdentificationPort, - kOpGetDisplayIdentificationPort, uint8_t(Void)); - PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer, - LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size, - uint64_t usage)); - PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer, - void(DvrGlobalBufferKey key)); - PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer, - LocalNativeBufferHandle(DvrGlobalBufferKey key)); - PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void)); - PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface, - SurfaceInfo(const SurfaceAttributes& attributes)); - PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void)); - PDX_REMOTE_METHOD( - CreateQueue, kOpCreateQueue, - LocalChannelHandle(const ProducerQueueConfig& producer_config)); - PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes, - void(const SurfaceAttributes& attributes)); -}; - -struct DisplayManagerProtocol { - // Service path. - static constexpr char kClientPath[] = "system/vr/display/manager"; - - // Op codes. - enum { - kOpGetSurfaceState = 0, - kOpGetSurfaceQueue, - }; - - // Aliases. - using LocalChannelHandle = pdx::LocalChannelHandle; - using Void = pdx::rpc::Void; - - // Methods. - PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState, - std::vector<SurfaceState>(Void)); - PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue, - LocalChannelHandle(int surface_id, int queue_id)); -}; - -struct VSyncSchedInfo { - int64_t vsync_period_ns; - int64_t timestamp_ns; - uint32_t next_vsync_count; - - private: - PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns, - next_vsync_count); -}; - -struct VSyncProtocol { - // Service path. - static constexpr char kClientPath[] = "system/vr/display/vsync"; - - // Op codes. - enum { - kOpWait = 0, - kOpAck, - kOpGetLastTimestamp, - kOpGetSchedInfo, - kOpAcknowledge, - }; - - // Aliases. - using Void = pdx::rpc::Void; - using Timestamp = int64_t; - - // Methods. - PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void)); - PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void)); - PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void)); - PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void)); -}; - -} // namespace display -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h b/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h deleted file mode 100644 index 20541a69a5..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ -#define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ - -#include <assert.h> -#include <tuple> - -#include <libbroadcastring/broadcast_ring.h> -#include <private/dvr/display_client.h> - -namespace android { -namespace dvr { - -// The buffer usage type for mapped shared buffers. -enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY }; - -// Holds the memory for the mapped shared buffer. Unlocks and releases the -// underlying IonBuffer in destructor. -class CPUMappedBuffer { - public: - // This constructor will create a display client and get the buffer from it. - CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode); - - // If you already have the IonBuffer, use this. It will take ownership. - CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode); - - // Use this if you do not want to take ownership. - CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode); - - ~CPUMappedBuffer(); - - // Getters. - size_t Size() const { return size_; } - void* Address() const { return address_; } - bool IsMapped() const { return Address() != nullptr; } - - // Attempt mapping this buffer to the CPU addressable space. - // This will create a display client and see if the buffer exists. - // If the buffer has not been setup yet, you will need to try again later. - void TryMapping(); - - protected: - // The memory area if we managed to map it. - size_t size_ = 0; - void* address_ = nullptr; - - // If we are polling the display client, the buffer key here. - DvrGlobalBufferKey buffer_key_; - - // If we just own the IonBuffer outright, it's here. - std::unique_ptr<IonBuffer> owned_buffer_ = nullptr; - - // The last time we connected to the display service. - int64_t last_display_service_connection_ns_ = 0; - - // If we do not own the IonBuffer, it's here - IonBuffer* buffer_ = nullptr; - - // The usage mode. - CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN; -}; - -// Represents a broadcast ring inside a mapped shared memory buffer. -// If has the same set of constructors as CPUMappedBuffer. -// The template argument is the concrete BroadcastRing class that this buffer -// holds. -template <class RingType> -class CPUMappedBroadcastRing : public CPUMappedBuffer { - public: - CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode) - : CPUMappedBuffer(key, mode) {} - - CPUMappedBroadcastRing(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode) - : CPUMappedBuffer(std::move(buffer), mode) {} - - CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode) - : CPUMappedBuffer(buffer, mode) {} - - // Helper function for publishing records in the ring. - void Publish(const typename RingType::Record& record) { - assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) || - (usage_mode_ == CPUUsageMode::WRITE_RARELY)); - - auto ring = Ring(); - if (ring) { - ring->Put(record); - } - } - - // Helper function for getting records from the ring. - // Returns true if we were able to retrieve the latest. - bool GetNewest(typename RingType::Record* record) { - assert((usage_mode_ == CPUUsageMode::READ_OFTEN) || - (usage_mode_ == CPUUsageMode::READ_RARELY)); - - auto ring = Ring(); - if (ring) { - return ring->GetNewest(&sequence_, record); - } - - return false; - } - - // Try obtaining the ring. If the named buffer has not been created yet, it - // will return nullptr. - RingType* Ring() { - // No ring created yet? - if (ring_ == nullptr) { - // Not mapped the memory yet? - if (IsMapped() == false) { - TryMapping(); - } - - // If have the memory mapped, allocate the ring. - if (IsMapped()) { - switch (usage_mode_) { - case CPUUsageMode::READ_OFTEN: - case CPUUsageMode::READ_RARELY: { - RingType ring; - bool import_ok; - std::tie(ring, import_ok) = RingType::Import(address_, size_); - if (import_ok) { - ring_ = std::make_unique<RingType>(ring); - } - } break; - case CPUUsageMode::WRITE_OFTEN: - case CPUUsageMode::WRITE_RARELY: - ring_ = - std::make_unique<RingType>(RingType::Create(address_, size_)); - break; - } - } - } - - return ring_.get(); - } - - protected: - std::unique_ptr<RingType> ring_ = nullptr; - - uint32_t sequence_ = 0; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h deleted file mode 100644 index 152464abd1..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/vsync_service.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef ANDROID_DVR_VSYNC_SERVICE_H_ -#define ANDROID_DVR_VSYNC_SERVICE_H_ - -#include <binder/IInterface.h> - -namespace android { -namespace dvr { - -class IVsyncCallback : public IInterface { - public: - DECLARE_META_INTERFACE(VsyncCallback) - - enum { - ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION - }; - - virtual status_t onVsync(int64_t vsync_timestamp) = 0; -}; - -class BnVsyncCallback : public BnInterface<IVsyncCallback> { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -// Register a callback with IVsyncService to be notified of vsync events and -// timestamps. There's also a shared memory vsync buffer defined in -// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared -// memory buffer that make it preferable in certain situations: -// -// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService -// is always available as long as surface flinger is running. -// -// 2. IVsyncService will make a binder callback when a vsync event occurs. This -// allows the client to not write code to implement periodic "get the latest -// vsync" calls, which is necessary with the vsync shared memory buffer. -// -// 3. The IVsyncService provides the real vsync timestamp reported by hardware -// composer, whereas the vsync shared memory buffer only has predicted vsync -// times. -class IVsyncService : public IInterface { -public: - DECLARE_META_INTERFACE(VsyncService) - - static const char* GetServiceName() { return "vrflinger_vsync"; } - - enum { - REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, - UNREGISTER_CALLBACK - }; - - virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0; - virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0; -}; - -class BnVsyncService : public BnInterface<IVsyncService> { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_VSYNC_SERVICE_H_ diff --git a/libs/vr/libdisplay/shared_buffer_helpers.cpp b/libs/vr/libdisplay/shared_buffer_helpers.cpp deleted file mode 100644 index 6ebf487d16..0000000000 --- a/libs/vr/libdisplay/shared_buffer_helpers.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include <private/dvr/clock_ns.h> -#include <private/dvr/shared_buffer_helpers.h> - -namespace android { -namespace dvr { -namespace { - -// We will not poll the display service for buffers more frequently than this. -constexpr size_t kDisplayServiceTriesPerSecond = 2; -} // namespace - -CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode) - : buffer_key_(key), usage_mode_(mode) { - TryMapping(); -} - -CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, - CPUUsageMode mode) - : owned_buffer_(std::move(buffer)), - buffer_(owned_buffer_.get()), - usage_mode_(mode) { - TryMapping(); -} - -CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode) - : buffer_(buffer), usage_mode_(mode) { - TryMapping(); -} - -CPUMappedBuffer::~CPUMappedBuffer() { - if (IsMapped()) { - buffer_->Unlock(); - } -} - -void CPUMappedBuffer::TryMapping() { - // Do we have an IonBuffer for this shared memory object? - if (buffer_ == nullptr) { - // Has it been too long since we last connected to the display service? - const auto current_time_ns = GetSystemClockNs(); - if ((current_time_ns - last_display_service_connection_ns_) < - (1e9 / kDisplayServiceTriesPerSecond)) { - // Early exit. - return; - } - last_display_service_connection_ns_ = current_time_ns; - - // Create a display client and get the buffer. - auto display_client = display::DisplayClient::Create(); - if (display_client) { - auto get_result = display_client->GetGlobalBuffer(buffer_key_); - if (get_result.ok()) { - owned_buffer_ = get_result.take(); - buffer_ = owned_buffer_.get(); - } else { - // The buffer has not been created yet. This is OK, we will keep - // retrying. - } - } else { - ALOGE("Unable to create display client for shared buffer access"); - } - } - - if (buffer_) { - auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK & - ~GRALLOC_USAGE_SW_WRITE_MASK; - - // Figure out the usage bits. - switch (usage_mode_) { - case CPUUsageMode::READ_OFTEN: - usage |= GRALLOC_USAGE_SW_READ_OFTEN; - break; - case CPUUsageMode::READ_RARELY: - usage |= GRALLOC_USAGE_SW_READ_RARELY; - break; - case CPUUsageMode::WRITE_OFTEN: - usage |= GRALLOC_USAGE_SW_WRITE_OFTEN; - break; - case CPUUsageMode::WRITE_RARELY: - usage |= GRALLOC_USAGE_SW_WRITE_RARELY; - break; - } - - int width = static_cast<int>(buffer_->width()); - int height = 1; - const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_); - - if (ret < 0 || !address_) { - ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_); - buffer_->Unlock(); - } else { - size_ = width; - } - } -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg deleted file mode 100644 index 2f8a3c018c..0000000000 --- a/libs/vr/libdisplay/system/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -filter=-build/header_guard diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp deleted file mode 100644 index 04d4f30140..0000000000 --- a/libs/vr/libdisplay/vsync_service.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "include/private/dvr/vsync_service.h" - -#include <binder/Parcel.h> -#include <log/log.h> - -namespace android { -namespace dvr { - -status_t BnVsyncCallback::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch (code) { - case ON_VSYNC: { - CHECK_INTERFACE(IVsyncCallback, data, reply); - int64_t vsync_timestamp = 0; - status_t result = data.readInt64(&vsync_timestamp); - if (result != OK) { - ALOGE("onVsync failed to readInt64: %d", result); - return result; - } - onVsync(vsync_timestamp); - return OK; - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -class BpVsyncCallback : public BpInterface<IVsyncCallback> { -public: - explicit BpVsyncCallback(const sp<IBinder>& impl) - : BpInterface<IVsyncCallback>(impl) {} - virtual ~BpVsyncCallback() {} - - virtual status_t onVsync(int64_t vsync_timestamp) { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IVsyncCallback::getInterfaceDescriptor()); - if (result != OK) { - ALOGE("onVsync failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeInt64(vsync_timestamp); - if (result != OK) { - ALOGE("onVsync failed to writeInt64: %d", result); - return result; - } - result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply, - IBinder::FLAG_ONEWAY); - if (result != OK) { - ALOGE("onVsync failed to transact: %d", result); - return result; - } - return result; - } -}; - -IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback"); - - -status_t BnVsyncService::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch (code) { - case REGISTER_CALLBACK: { - CHECK_INTERFACE(IVsyncService, data, reply); - sp<IBinder> callback; - status_t result = data.readStrongBinder(&callback); - if (result != OK) { - ALOGE("registerCallback failed to readStrongBinder: %d", result); - return result; - } - registerCallback(interface_cast<IVsyncCallback>(callback)); - return OK; - } - case UNREGISTER_CALLBACK: { - CHECK_INTERFACE(IVsyncService, data, reply); - sp<IBinder> callback; - status_t result = data.readStrongBinder(&callback); - if (result != OK) { - ALOGE("unregisterCallback failed to readStrongBinder: %d", result); - return result; - } - unregisterCallback(interface_cast<IVsyncCallback>(callback)); - return OK; - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -class BpVsyncService : public BpInterface<IVsyncService> { -public: - explicit BpVsyncService(const sp<IBinder>& impl) - : BpInterface<IVsyncService>(impl) {} - virtual ~BpVsyncService() {} - - virtual status_t registerCallback(const sp<IVsyncCallback> callback) { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IVsyncService::getInterfaceDescriptor()); - if (result != OK) { - ALOGE("registerCallback failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(IInterface::asBinder(callback)); - if (result != OK) { - ALOGE("registerCallback failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact( - BnVsyncService::REGISTER_CALLBACK, data, &reply); - if (result != OK) { - ALOGE("registerCallback failed to transact: %d", result); - return result; - } - return result; - } - - virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) { - Parcel data, reply; - status_t result = data.writeInterfaceToken( - IVsyncService::getInterfaceDescriptor()); - if (result != OK) { - ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(IInterface::asBinder(callback)); - if (result != OK) { - ALOGE("unregisterCallback failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact( - BnVsyncService::UNREGISTER_CALLBACK, data, &reply); - if (result != OK) { - ALOGE("unregisterCallback failed to transact: %d", result); - return result; - } - return result; - } -}; - -IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService"); - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp deleted file mode 100644 index 40a5099177..0000000000 --- a/libs/vr/libvrsensor/Android.bp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2015 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. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -sourceFiles = [ - "pose_client.cpp", - "latency_model.cpp", -] - -includeFiles = [ - "include", -] - -staticLibraries = [ - "libdisplay", - "libdvrcommon", - "libbroadcastring", -] - -sharedLibraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libhardware", - "liblog", - "libutils", - "libui", - "libpdx_default_transport", -] - -cc_library { - srcs: sourceFiles, - cflags: [ - "-Wall", - "-Werror", - "-Wno-macro-redefined", - ], - export_include_dirs: includeFiles, - static_libs: staticLibraries, - shared_libs: sharedLibraries, - header_libs: ["libdvr_headers"], - name: "libvrsensor", -} diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg deleted file mode 100644 index 2f8a3c018c..0000000000 --- a/libs/vr/libvrsensor/include/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -filter=-build/header_guard diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h deleted file mode 100644 index b663a67cea..0000000000 --- a/libs/vr/libvrsensor/include/dvr/pose_client.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef ANDROID_DVR_POSE_CLIENT_H_ -#define ANDROID_DVR_POSE_CLIENT_H_ - -#ifdef __ARM_NEON -#include <arm_neon.h> -#else -#ifndef __FLOAT32X4T_86 -#define __FLOAT32X4T_86 -typedef float float32x4_t __attribute__ ((__vector_size__ (16))); -typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t; -#endif -#endif - -#include <stdbool.h> -#include <stdint.h> - -#include <dvr/dvr_pose.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct DvrPoseClient DvrPoseClient; - -// Returned by the async pose ring buffer access API. -typedef struct DvrPoseRingBufferInfo { - // Read-only pointer to the pose ring buffer. The current pose is in this - // buffer at element buffer[current_frame & (buffer_size - 1)]. The next - // frame's forecasted pose is at element - // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are - // predicted for when 50% of the corresponding frame's pixel data is visible - // to the user. - // The last value returned by dvrPresent is the count for the next frame, - // which is the earliest that the application could display something if they - // were to render promptly. (TODO(jbates) move this comment to dvrPresent). - volatile const DvrPoseAsync* buffer; - // Minimum number of accurate forecasted poses including the current frame's - // pose. This is the number of poses that are udpated by the pose service. - // If the application reads past this count, they will get a stale prediction - // from a previous frame. Guaranteed to be at least 2. - uint32_t min_future_count; - // Number of elements in buffer. At least 8 and greater than min_future_count. - // Guaranteed to be a power of two. The total size of the buffer in bytes is: - // total_count * sizeof(DvrPoseAsync) - uint32_t total_count; -} DvrPoseRingBufferInfo; - -typedef enum DvrPoseMode { - DVR_POSE_MODE_6DOF = 0, - DVR_POSE_MODE_3DOF, - DVR_POSE_MODE_MOCK_FROZEN, - DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW, - DVR_POSE_MODE_MOCK_HEAD_TURN_FAST, - DVR_POSE_MODE_MOCK_ROTATE_SLOW, - DVR_POSE_MODE_MOCK_ROTATE_MEDIUM, - DVR_POSE_MODE_MOCK_ROTATE_FAST, - DVR_POSE_MODE_MOCK_CIRCLE_STRAFE, - DVR_POSE_MODE_FLOAT, - DVR_POSE_MODE_MOCK_MOTION_SICKNESS, - - // Always last. - DVR_POSE_MODE_COUNT, -} DvrPoseMode; - -typedef enum DvrControllerId { - DVR_CONTROLLER_0 = 0, - DVR_CONTROLLER_1 = 1, -} DvrControllerId; - -// Creates a new pose client. -// -// @return Pointer to the created pose client, nullptr on failure. -DvrPoseClient* dvrPoseClientCreate(); - -// Destroys a pose client. -// -// @param client Pointer to the pose client to be destroyed. -void dvrPoseClientDestroy(DvrPoseClient* client); - -// Gets the pose for the given vsync count. -// -// @param client Pointer to the pose client. -// @param vsync_count Vsync that this pose should be forward-predicted to. -// Typically this is the count returned by dvrGetNextVsyncCount. -// @param out_pose Struct to store pose state. -// @return Zero on success, negative error code on failure. -int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count, - DvrPoseAsync* out_pose); - -// Gets the current vsync count. -uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client); - -// Gets the pose for the given controller at the given vsync count. -// -// @param client Pointer to the pose client. -// @param controller_id The controller id. -// @param vsync_count Vsync that this pose should be forward-predicted to. -// Typically this is the count returned by dvrGetNextVsyncCount. -// @param out_pose Struct to store pose state. -// @return Zero on success, negative error code on failure. -int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id, - uint32_t vsync_count, DvrPoseAsync* out_pose); - -// Enables/disables logging for the controller fusion. -// -// @param client Pointer to the pose client. -// @param enable True starts logging, False stops. -// @return Zero on success, negative error code on failure. -int dvrPoseClientLogController(DvrPoseClient* client, bool enable); - -// DEPRECATED -// Polls current pose state. -// -// @param client Pointer to the pose client. -// @param state Struct to store polled state. -// @return Zero on success, negative error code on failure. -int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state); - -// Freezes the pose to the provided state. -// -// Future poll operations will return this state until a different state is -// frozen or dvrPoseClientModeSet() is called with a different mode. The timestamp is -// not frozen. -// -// @param client Pointer to the pose client. -// @param frozen_state State pose to be frozen to. -// @return Zero on success, negative error code on failure. -int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state); - -// Sets the pose service mode. -// -// @param mode The requested pose mode. -// @return Zero on success, negative error code on failure. -int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode); - -// Gets the pose service mode. -// -// @param mode Return value for the current pose mode. -// @return Zero on success, negative error code on failure. -int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode); - -// Get access to the shared memory pose ring buffer. -// A future pose at vsync <current> + <offset> is accessed at index: -// index = (<current> + <offset>) % out_buffer_size -// Where <current> was the last value returned by dvrPresent and -// <offset> is less than or equal to |out_min_future_count|. -// |out_buffer| will be set to a pointer to the buffer. -// |out_fd| will be set to the gralloc buffer file descriptor, which is -// required for binding this buffer for GPU use. -// Returns 0 on success. -int dvrPoseClientGetRingBuffer(DvrPoseClient* client, - DvrPoseRingBufferInfo* out_info); - -// Sets enabled state for sensors pose processing. -// -// @param enabled Whether sensors are enabled or disabled. -// @return Zero on success -int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled); - -// Requests a burst of data samples from pose service. The data samples are -// passed through a shared memory buffer obtained by calling -// dvrPoseClientGetDataReader(). -// -// @param DvrPoseDataCaptureRequest Parameters on how to capture data. -// @return Zero on success. -int dvrPoseClientDataCapture(DvrPoseClient* client, - const DvrPoseDataCaptureRequest* request); - -// Destroys the write buffer queue for the given |data_type|. -int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // ANDROID_DVR_POSE_CLIENT_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h deleted file mode 100644 index bf0e687b7f..0000000000 --- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ANDROID_DVR_LATENCY_MODEL_H_ -#define ANDROID_DVR_LATENCY_MODEL_H_ - -#include <vector> - -namespace android { -namespace dvr { - -// This class models the latency from sensors. It will look at the first -// window_size measurements and return their average after that. -class LatencyModel { - public: - explicit LatencyModel(size_t window_size); - ~LatencyModel() = default; - - void AddLatency(int64_t latency_ns); - int64_t CurrentLatencyEstimate() const { return latency_; } - - private: - size_t window_size_; - int64_t latency_sum_ = 0; - size_t num_summed_ = 0; - int64_t latency_ = 0; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_LATENCY_MODEL_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h deleted file mode 100644 index 7bf1cd4d29..0000000000 --- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ANDROID_DVR_POSE_IPC_H_ -#define ANDROID_DVR_POSE_IPC_H_ - -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define DVR_POSE_SERVICE_BASE "system/vr/pose" -#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client") - -enum { - DVR_POSE_FREEZE = 0, - DVR_POSE_SET_MODE, - DVR_POSE_GET_MODE, - DVR_POSE_GET_CONTROLLER_RING_BUFFER, - DVR_POSE_LOG_CONTROLLER, - DVR_POSE_SENSORS_ENABLE, - DVR_POSE_GET_TANGO_READER, - DVR_POSE_DATA_CAPTURE, - DVR_POSE_TANGO_READER_DESTROY, -}; - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // ANDROID_DVR_POSE_IPC_H_ diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h deleted file mode 100644 index 39592bb15d..0000000000 --- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ -#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ - -#include <private/dvr/buffer_hub_queue_client.h> - -using android::dvr::ConsumerQueue; - -typedef struct DvrPoseClient DvrPoseClient; - -namespace android { -namespace dvr { - -int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type, - ConsumerQueue **queue_out); - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_ diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp deleted file mode 100644 index d3a45210a7..0000000000 --- a/libs/vr/libvrsensor/latency_model.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <private/dvr/latency_model.h> - -#include <cmath> - -namespace android { -namespace dvr { - -LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {} - -void LatencyModel::AddLatency(int64_t latency_ns) { - // Not enough samples yet? - if (num_summed_ < window_size_) { - // Accumulate. - latency_sum_ += latency_ns; - - // Have enough samples for latency estimate? - if (++num_summed_ == window_size_) { - latency_ = latency_sum_ / window_size_; - } - } -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp deleted file mode 100644 index 4ff6a0912c..0000000000 --- a/libs/vr/libvrsensor/pose_client.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#define LOG_TAG "PoseClient" -#include <dvr/dvr_shared_buffers.h> -#include <dvr/pose_client.h> - -#include <stdint.h> - -#include <log/log.h> -#include <pdx/client.h> -#include <pdx/default_transport/client_channel_factory.h> -#include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_queue_client.h> -#include <private/dvr/consumer_buffer.h> -#include <private/dvr/display_client.h> -#include <private/dvr/pose-ipc.h> -#include <private/dvr/shared_buffer_helpers.h> - -using android::dvr::ConsumerQueue; -using android::pdx::LocalHandle; -using android::pdx::LocalChannelHandle; -using android::pdx::Status; -using android::pdx::Transaction; - -namespace android { -namespace dvr { -namespace { - -typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing; - -constexpr static int32_t MAX_CONTROLLERS = 2; -} // namespace - -// PoseClient is a remote interface to the pose service in sensord. -class PoseClient : public pdx::ClientBase<PoseClient> { - public: - ~PoseClient() override {} - - // Casts C handle into an instance of this class. - static PoseClient* FromC(DvrPoseClient* client) { - return reinterpret_cast<PoseClient*>(client); - } - - // Polls the pose service for the current state and stores it in *state. - // Returns zero on success, a negative error code otherwise. - int Poll(DvrPose* state) { - // Allocate the helper class to access the sensor pose buffer. - if (sensor_pose_buffer_ == nullptr) { - sensor_pose_buffer_ = std::make_unique<SensorPoseRing>( - DvrGlobalBuffers::kSensorPoseBuffer, CPUUsageMode::READ_RARELY); - } - - if (state) { - if (sensor_pose_buffer_->GetNewest(state)) { - return 0; - } else { - return -EAGAIN; - } - } - - return -EINVAL; - } - - int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) { - const auto vsync_buffer = GetVsyncBuffer(); - if (vsync_buffer) { - *out_pose = - vsync_buffer - ->vsync_poses[vsync_count & DvrVsyncPoseBuffer::kIndexMask]; - return 0; - } else { - return -EAGAIN; - } - } - - uint32_t GetVsyncCount() { - const auto vsync_buffer = GetVsyncBuffer(); - if (vsync_buffer) { - return vsync_buffer->vsync_count; - } - - return 0; - } - - int GetControllerPose(int32_t controller_id, uint32_t vsync_count, - DvrPoseAsync* out_pose) { - if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) { - return -EINVAL; - } - if (!controllers_[controller_id].mapped_pose_buffer) { - int ret = GetControllerRingBuffer(controller_id); - if (ret < 0) - return ret; - } - *out_pose = - controllers_[controller_id] - .mapped_pose_buffer[vsync_count & DvrVsyncPoseBuffer::kIndexMask]; - return 0; - } - - int LogController(bool enable) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable, - sizeof(enable), nullptr, 0); - ALOGE_IF(!status, "Pose LogController() failed because: %s", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Freezes the pose to the provided state. Future poll operations will return - // this state until a different state is frozen or SetMode() is called with a - // different mode. - // Returns zero on success, a negative error code otherwise. - int Freeze(const DvrPose& frozen_state) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state, - sizeof(frozen_state), nullptr, 0); - ALOGE_IF(!status, "Pose Freeze() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Sets the data mode for the pose service. - int SetMode(DvrPoseMode mode) { - Transaction trans{*this}; - Status<int> status = - trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0); - ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Gets the data mode for the pose service. - int GetMode(DvrPoseMode* out_mode) { - int mode; - Transaction trans{*this}; - Status<int> status = - trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode)); - ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s", - status.GetErrorMessage().c_str()); - if (status) - *out_mode = DvrPoseMode(mode); - return ReturnStatusOrError(status); - } - - int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) { - // Get buffer. - Transaction trans{*this}; - Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>( - DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0); - - if (!status) { - ALOGE("PoseClient GetTangoReaderHandle() failed because: %s", - status.GetErrorMessage().c_str()); - *queue_out = nullptr; - return -status.error(); - } - - std::unique_ptr<ConsumerQueue> consumer_queue = - ConsumerQueue::Import(status.take()); - *queue_out = consumer_queue.release(); - return 0; - } - - int DataCapture(const DvrPoseDataCaptureRequest* request) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request, - sizeof(*request), nullptr, 0); - ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - int DataReaderDestroy(uint64_t data_type) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY, - &data_type, sizeof(data_type), nullptr, - 0); - ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - // Enables or disables all pose processing from sensors - int EnableSensors(bool enabled) { - Transaction trans{*this}; - Status<int> status = trans.Send<int>(DVR_POSE_SENSORS_ENABLE, &enabled, - sizeof(enabled), nullptr, 0); - ALOGE_IF(!status, "Pose EnableSensors() failed because: %s\n", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); - } - - int GetRingBuffer(DvrPoseRingBufferInfo* out_info) { - // First time mapping the buffer? - const auto vsync_buffer = GetVsyncBuffer(); - if (vsync_buffer) { - if (out_info) { - out_info->min_future_count = DvrVsyncPoseBuffer::kMinFutureCount; - out_info->total_count = DvrVsyncPoseBuffer::kSize; - out_info->buffer = vsync_buffer->vsync_poses; - } - return -EINVAL; - } - - return -EAGAIN; - } - - int GetControllerRingBuffer(int32_t controller_id) { - if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) { - return -EINVAL; - } - ControllerClientState& client_state = controllers_[controller_id]; - if (client_state.pose_buffer.get()) { - return 0; - } - - Transaction trans{*this}; - Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>( - DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id, - sizeof(controller_id), nullptr, 0); - if (!status) { - return -status.error(); - } - - auto buffer = ConsumerBuffer::Import(status.take()); - if (!buffer) { - ALOGE("Pose failed to import ring buffer"); - return -EIO; - } - constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync); - void* addr = nullptr; - int ret = buffer->GetBlobReadWritePointer(size, &addr); - if (ret < 0 || !addr) { - ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr); - return -EIO; - } - client_state.pose_buffer.swap(buffer); - client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr); - ALOGI( - "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f", - controller_id, client_state.mapped_pose_buffer[0].position[0], - client_state.mapped_pose_buffer[0].position[1], - client_state.mapped_pose_buffer[0].position[2], - client_state.mapped_pose_buffer[0].orientation[0], - client_state.mapped_pose_buffer[0].orientation[1], - client_state.mapped_pose_buffer[0].orientation[2], - client_state.mapped_pose_buffer[0].orientation[3]); - return 0; - } - - private: - friend BASE; - - // Set up a channel to the pose service. - PoseClient() - : BASE(pdx::default_transport::ClientChannelFactory::Create( - DVR_POSE_SERVICE_CLIENT)) { - // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't - // block while waiting for the pose service to come back up. - EnableAutoReconnect(kInfiniteTimeout); - } - - PoseClient(const PoseClient&) = delete; - PoseClient& operator=(const PoseClient&) = delete; - - const DvrVsyncPoseBuffer* GetVsyncBuffer() { - if (mapped_vsync_pose_buffer_ == nullptr) { - if (vsync_pose_buffer_ == nullptr) { - // The constructor tries mapping it so we do not need TryMapping after. - vsync_pose_buffer_ = std::make_unique<CPUMappedBuffer>( - DvrGlobalBuffers::kVsyncPoseBuffer, CPUUsageMode::READ_OFTEN); - } else if (vsync_pose_buffer_->IsMapped() == false) { - vsync_pose_buffer_->TryMapping(); - } - - if (vsync_pose_buffer_->IsMapped()) { - mapped_vsync_pose_buffer_ = - static_cast<DvrVsyncPoseBuffer*>(vsync_pose_buffer_->Address()); - } - } - - return mapped_vsync_pose_buffer_; - } - - // The vsync pose buffer if already mapped. - std::unique_ptr<CPUMappedBuffer> vsync_pose_buffer_; - - // The direct sensor pose buffer. - std::unique_ptr<SensorPoseRing> sensor_pose_buffer_; - - const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr; - - struct ControllerClientState { - std::unique_ptr<ConsumerBuffer> pose_buffer; - const DvrPoseAsync* mapped_pose_buffer = nullptr; - }; - ControllerClientState controllers_[MAX_CONTROLLERS]; -}; - -int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type, - ConsumerQueue** queue_out) { - return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out); -} - -} // namespace dvr -} // namespace android - -using android::dvr::PoseClient; - -extern "C" { - -DvrPoseClient* dvrPoseClientCreate() { - auto* client = PoseClient::Create().release(); - return reinterpret_cast<DvrPoseClient*>(client); -} - -void dvrPoseClientDestroy(DvrPoseClient* client) { - delete PoseClient::FromC(client); -} - -int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count, - DvrPoseAsync* out_pose) { - return PoseClient::FromC(client)->GetPose(vsync_count, out_pose); -} - -uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client) { - return PoseClient::FromC(client)->GetVsyncCount(); -} - -int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id, - uint32_t vsync_count, DvrPoseAsync* out_pose) { - return PoseClient::FromC(client)->GetControllerPose(controller_id, - vsync_count, out_pose); -} - -int dvrPoseClientLogController(DvrPoseClient* client, bool enable) { - return PoseClient::FromC(client)->LogController(enable); -} - -int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state) { - return PoseClient::FromC(client)->Poll(state); -} - -int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state) { - return PoseClient::FromC(client)->Freeze(*frozen_state); -} - -int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode) { - return PoseClient::FromC(client)->SetMode(mode); -} - -int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) { - return PoseClient::FromC(client)->GetMode(mode); -} - -int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) { - return PoseClient::FromC(client)->EnableSensors(enabled); -} - -int dvrPoseClientDataCapture(DvrPoseClient* client, - const DvrPoseDataCaptureRequest* request) { - return PoseClient::FromC(client)->DataCapture(request); -} - -int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) { - return PoseClient::FromC(client)->DataReaderDestroy(data_type); -} - -} // extern "C" diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index d1de551799..b8854352ad 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -60,7 +60,6 @@ filegroup { name: "libinputflinger_sources", srcs: [ "InputCommonConverter.cpp", - "InputManager.cpp", "InputProcessor.cpp", "PreferStylusOverTouchBlocker.cpp", "UnwantedInteractionBlocker.cpp", @@ -116,6 +115,10 @@ cc_library_shared { "inputflinger_defaults", "libinputflinger_defaults", ], + srcs: [ + "InputManager.cpp", + // other sources are added via "defaults" + ], cflags: [ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden //-fvisibility=hidden diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp index beec2e162e..ddd514676b 100644 --- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp +++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp @@ -25,8 +25,7 @@ static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) { for (size_t i = 0; i < args.pointerCount; i++) { // Make sure we are canceling stylus pointers const int32_t toolType = args.pointerProperties[i].toolType; - if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + if (isStylusToolType(toolType)) { hasStylus = true; } if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 4a0f2ec304..3d7242e9ea 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -15,6 +15,9 @@ "name": "inputflinger_tests" }, { + "name": "libchrome-gestures_test" + }, + { "name": "libpalmrejection_test" }, { @@ -41,6 +44,7 @@ "include-filter": "android.view.cts.input", "include-filter": "android.view.cts.MotionEventTest", "include-filter": "android.view.cts.PointerCaptureTest", + "include-filter": "android.view.cts.TooltipTest", "include-filter": "android.view.cts.VerifyInputEventTest" } ] @@ -128,6 +132,7 @@ { "include-filter": "android.view.cts.MotionEventTest", "include-filter": "android.view.cts.PointerCaptureTest", + "include-filter": "android.view.cts.TooltipTest", "include-filter": "android.view.cts.VerifyInputEventTest" } ] diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index ec41025e9d..c170b81475 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -99,14 +99,17 @@ static bool isPalmRejectionEnabled() { } static int getLinuxToolCode(int toolType) { - if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) { - return BTN_TOOL_PEN; + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return BTN_TOOL_PEN; + case AMOTION_EVENT_TOOL_TYPE_ERASER: + return BTN_TOOL_RUBBER; + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return BTN_TOOL_FINGER; + default: + ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType); + return BTN_TOOL_FINGER; } - if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { - return BTN_TOOL_FINGER; - } - ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType); - return BTN_TOOL_FINGER; } static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { @@ -195,7 +198,7 @@ NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) { std::set<int32_t> stylusPointerIds; for (uint32_t i = 0; i < args.pointerCount; i++) { - if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) { + if (isStylusToolType(args.pointerProperties[i].toolType)) { stylusPointerIds.insert(args.pointerProperties[i].id); } } diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h index d210e9e846..512cb6e692 100644 --- a/services/inputflinger/dispatcher/CancelationOptions.h +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -24,7 +24,7 @@ namespace inputdispatcher { /* Specifies which events are to be canceled and why. */ struct CancelationOptions { - enum Mode { + enum class Mode { CANCEL_ALL_EVENTS = 0, CANCEL_POINTER_EVENTS = 1, CANCEL_NON_POINTER_EVENTS = 2, diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index ec9701ac24..7bbfb95b88 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -166,7 +166,7 @@ KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t sou repeatCount(repeatCount), downTime(downTime), syntheticRepeat(false), - interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), + interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN), interceptKeyWakeupTime(0) {} KeyEntry::~KeyEntry() {} @@ -189,7 +189,7 @@ void KeyEntry::recycle() { dispatchInProgress = false; syntheticRepeat = false; - interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; interceptKeyWakeupTime = 0; } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index f8019126f3..3799814f67 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -140,11 +140,11 @@ struct KeyEntry : EventEntry { bool syntheticRepeat; // set to true for synthetic key repeats - enum InterceptKeyResult { - INTERCEPT_KEY_RESULT_UNKNOWN, - INTERCEPT_KEY_RESULT_SKIP, - INTERCEPT_KEY_RESULT_CONTINUE, - INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, + enum class InterceptKeyResult { + UNKNOWN, + SKIP, + CONTINUE, + TRY_AGAIN_LATER, }; InterceptKeyResult interceptKeyResult; // set based on the interception result nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 7b7c42a211..466c51eb88 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -60,7 +60,6 @@ using android::gui::FocusRequest; using android::gui::TouchOcclusionMode; using android::gui::WindowInfo; using android::gui::WindowInfoHandle; -using android::os::IInputConstants; using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; @@ -481,8 +480,7 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32 bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && - (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); + isStylusToolType(entry.pointerProperties[pointerIndex].toolType); } // Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND. @@ -1034,8 +1032,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent); if (pendingKey.keyCode == keyEntry.keyCode && pendingKey.interceptKeyResult == - KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { - pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { + pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; pendingKey.interceptKeyWakeupTime = 0; needWake = true; } @@ -1170,17 +1168,18 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason switch (entry.type) { case EventEntry::Type::KEY: { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); break; } case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry); if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); } else { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, + reason); synthesizeCancelationEventsForAllConnectionsLocked(options); } break; @@ -1335,7 +1334,7 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, resetKeyRepeatLocked(); } - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset"); + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset"); options.deviceId = entry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); return true; @@ -1540,19 +1539,19 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key } // Handle case where the policy asked us to try again later last time. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { if (entry->interceptKeyWakeupTime < *nextWakeupTime) { *nextWakeupTime = entry->interceptKeyWakeupTime; } return false; // wait until next wakeup } - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; entry->interceptKeyWakeupTime = 0; } // Give the policy a chance to intercept the key. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { sp<IBinder> focusedWindowToken = mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry)); @@ -1563,9 +1562,9 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key postCommandLocked(std::move(command)); return false; // wait for the command to run } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; } - } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { + } else if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::SKIP) { if (*dropReason == DropReason::NOT_DROPPED) { *dropReason = DropReason::POLICY; } @@ -1724,9 +1723,9 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< return true; } if (injectionResult != InputEventInjectionResult::SUCCEEDED) { - CancelationOptions::Mode mode(isPointerEvent - ? CancelationOptions::CANCEL_POINTER_EVENTS - : CancelationOptions::CANCEL_NON_POINTER_EVENTS); + CancelationOptions::Mode mode( + isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS + : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS); CancelationOptions options(mode, "input event injection failed"); synthesizeCancelationEventsForMonitorsLocked(options); return true; @@ -1737,7 +1736,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< // Dispatch the motion. if (conflictingPointerActions) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } @@ -1770,15 +1769,16 @@ void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<Dr void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32 ", policyFlags=0x%x, " "action=%s, actionButton=0x%x, flags=0x%x, " "metaState=0x%x, buttonState=0x%x," "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, - entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(), - entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags, - entry.xPrecision, entry.yPrecision, entry.downTime); + prefix, entry.eventTime, entry.deviceId, + inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags, + MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags, + entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision, + entry.yPrecision, entry.downTime); for (uint32_t i = 0; i < entry.pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " @@ -1837,7 +1837,7 @@ void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) ALOGW("Canceling events for %s because it is unresponsive", connection->inputChannel->getName().c_str()); if (connection->status == Connection::Status::NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "application not responding"); synthesizeCancelationEventsForConnectionLocked(connection, options); } @@ -2306,6 +2306,20 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( entry.eventTime); } } + + // Update the pointerIds for non-splittable when it received pointer down. + if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // If no split, we suppose all touched windows should receive pointer down. + const int32_t pointerIndex = getMotionEventActionPointerIndex(action); + for (size_t i = 0; i < tempTouchState.windows.size(); i++) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; + // Ignore drag window for it should just track one pointer. + if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { + continue; + } + touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id); + } + } } // Update dispatching for hover enter and exit. @@ -2415,12 +2429,14 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( if (info->displayId == displayId && windowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::IS_WALLPAPER)) { + BitSet32 pointerIds; + pointerIds.markBit(entry.pointerProperties[0].id); tempTouchState.addOrUpdateWindow(windowHandle, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags:: WINDOW_IS_PARTIALLY_OBSCURED | InputTarget::Flags::DISPATCH_AS_IS, - BitSet32(0), entry.eventTime); + pointerIds, entry.eventTime); } } } @@ -2480,17 +2496,6 @@ Failed: } i += 1; } - } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { - // If no split, we suppose all touched windows should receive pointer down. - const int32_t pointerIndex = getMotionEventActionPointerIndex(action); - for (size_t i = 0; i < tempTouchState.windows.size(); i++) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; - // Ignore drag window for it should just track one pointer. - if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { - continue; - } - touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id); - } } // Save changes unless the action was scroll in which case the temporary touch @@ -2503,6 +2508,10 @@ Failed: } } + if (tempTouchState.windows.empty()) { + mTouchStatesByDisplay.erase(displayId); + } + // Update hover state. mLastHoverWindowHandle = newHoverWindowHandle; @@ -4796,10 +4805,13 @@ void InputDispatcher::setInputWindowsLocked( updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); - if (mLastHoverWindowHandle && - std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == - windowHandles.end()) { - mLastHoverWindowHandle = nullptr; + if (mLastHoverWindowHandle) { + const WindowInfo* lastHoverWindowInfo = mLastHoverWindowHandle->getInfo(); + if (lastHoverWindowInfo->displayId == displayId && + std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == + windowHandles.end()) { + mLastHoverWindowHandle = nullptr; + } } std::optional<FocusResolver::FocusChanges> changes = @@ -4822,7 +4834,7 @@ void InputDispatcher::setInputWindowsLocked( std::shared_ptr<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); // Since we are about to drop the touch, cancel the events for the wallpaper as @@ -4868,7 +4880,7 @@ void InputDispatcher::setInputWindowsLocked( std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(newWindowHandle->getToken()); if (inputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window's orientation changed"); synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); } @@ -4949,7 +4961,7 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { getInputChannelLocked(oldFocusedWindowToken); if (inputChannel != nullptr) { CancelationOptions - options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, "The display which contains this window no longer has focus."); options.displayId = ADISPLAY_ID_NONE; synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); @@ -5176,7 +5188,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions - options(CancelationOptions::CANCEL_POINTER_EVENTS, + options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "transferring touch focus from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection); @@ -5250,7 +5262,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { ALOGD("Resetting and dropping all events (%s).", reason); } - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); resetKeyRepeatLocked(); @@ -5431,9 +5443,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mReplacedKeys.empty()) { dump += INDENT "ReplacedKeys:\n"; - for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) { - const KeyReplacement& replacement = pair.first; - int32_t newKeyCode = pair.second; + for (const auto& [replacement, newKeyCode] : mReplacedKeys) { dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n", replacement.keyCode, replacement.deviceId, newKeyCode); } @@ -5677,7 +5687,7 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { TouchState& state = *statePtr; TouchedWindow& window = *windowPtr; // Send cancel events to all the input channels we're stealing from. - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "input channel stole pointer stream"); options.deviceId = state.deviceId; options.displayId = displayId; @@ -5963,11 +5973,11 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& } // acquire lock if (delay < 0) { - entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; + entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP; } else if (delay == 0) { - entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; } else { - entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; + entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER; entry.interceptKeyWakeupTime = now() + delay; } } @@ -6077,7 +6087,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& con // Cancel the fallback key. if (fallbackKeyCode != AKEYCODE_UNKNOWN) { - CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "application handled the original non-fallback key " "or is no longer a foreground target, " "canceling previously dispatched fallback key"); @@ -6154,7 +6164,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& con } } - CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "canceling fallback, policy no longer desires it"); options.keyCode = fallbackKeyCode; synthesizeCancelationEventsForConnectionLocked(connection, options); @@ -6307,7 +6317,7 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch if (changes.oldFocus) { std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus); if (focusedInputChannel) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason); @@ -6447,7 +6457,7 @@ void InputDispatcher::cancelCurrentTouch() { { std::scoped_lock _l(mLock); ALOGD("Canceling all ongoing pointer gestures on all displays."); - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "cancel current touch"); synthesizeCancelationEventsForAllConnectionsLocked(options); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 047c628e67..563868d10e 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -500,10 +500,10 @@ bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOpt } switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + case CancelationOptions::Mode::CANCEL_ALL_EVENTS: + case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return true; - case CancelationOptions::CANCEL_FALLBACK_EVENTS: + case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: return memento.flags & AKEY_EVENT_FLAG_FALLBACK; default: return false; @@ -521,11 +521,11 @@ bool InputState::shouldCancelMotion(const MotionMemento& memento, } switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: + case CancelationOptions::Mode::CANCEL_ALL_EVENTS: return true; - case CancelationOptions::CANCEL_POINTER_EVENTS: + case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: return memento.source & AINPUT_SOURCE_CLASS_POINTER; - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); default: return false; diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index ee7da93975..114e0bf130 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -148,7 +148,8 @@ bool TouchState::isDown() const { std::string TouchState::dump() const { std::string out; - out += StringPrintf("deviceId=%d, source=0x%08x\n", deviceId, source); + out += StringPrintf("deviceId=%d, source=%s\n", deviceId, + inputEventSourceToString(source).c_str()); if (!windows.empty()) { out += " Windows:\n"; for (size_t i = 0; i < windows.size(); i++) { diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 484b0d3b7f..76dce63aac 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -125,9 +125,12 @@ public: /** * Set the touch mode state. - * Touch mode is a global state that apps may enter / exit based on specific - * user interactions with input devices. - * If true, the device is in touch mode. + * Touch mode is a per display state that apps may enter / exit based on specific user + * interactions with input devices. If <code>inTouchMode</code> is set to true, the display + * identified by <code>displayId</code> will be changed to touch mode. Performs a permission + * check if hasPermission is set to false. + * + * This method also enqueues a a TouchModeEntry message for dispatching. * * Returns true when changing touch mode state. */ diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h index f28dbf3039..c46f90501f 100644 --- a/services/inputflinger/include/NotifyArgs.h +++ b/services/inputflinger/include/NotifyArgs.h @@ -116,6 +116,8 @@ struct NotifyMotionArgs { NotifyMotionArgs(const NotifyMotionArgs& other); + NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default; + bool operator==(const NotifyMotionArgs& rhs) const; std::string dump() const; diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 24168a12a6..cf020161cc 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -79,6 +79,7 @@ cc_defaults { ], header_libs: [ "libbatteryservice_headers", + "libchrome-gestures_headers", "libinputreader_headers", ], target: { @@ -97,6 +98,22 @@ cc_defaults { }, } +cc_library_static { + name: "libinputreader_static", + defaults: [ + "inputflinger_defaults", + "libinputreader_defaults", + ], + shared_libs: [ + "libinputflinger_base", + ], + export_header_lib_headers: [ + "libbatteryservice_headers", + "libchrome-gestures_headers", + "libinputreader_headers", + ], +} + cc_library_shared { name: "libinputreader", host_supported: true, diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index f8b1b3fa6e..f04a6469ea 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -66,8 +66,7 @@ static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { return false; } const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action); - return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER; + return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType); } // --- InputReader --- diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h index 8d14d3c169..ff15e0c660 100644 --- a/services/inputflinger/reader/include/StylusState.h +++ b/services/inputflinger/reader/include/StylusState.h @@ -24,27 +24,19 @@ namespace android { struct StylusState { /* Time the stylus event was received. */ - nsecs_t when; - /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ - float pressure; + nsecs_t when{}; + /* + * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0]. + * The presence of a pressure value indicates that the stylus is able to tell whether it is + * touching the display. + */ + std::optional<float> pressure{}; /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ - uint32_t buttons; + uint32_t buttons{}; /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ - int32_t toolType; + int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN}; - void copyFrom(const StylusState& other) { - when = other.when; - pressure = other.pressure; - buttons = other.buttons; - toolType = other.toolType; - } - - void clear() { - when = LLONG_MAX; - pressure = 0.f; - buttons = 0; - toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - } + void clear() { *this = StylusState{}; } }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index a4f257c4b6..a1a2af9500 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -117,6 +117,10 @@ void CursorInputMapper::dump(std::string& dump) { toString(mCursorScrollAccumulator.haveRelativeVWheel())); dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", toString(mCursorScrollAccumulator.haveRelativeHWheel())); + dump += StringPrintf(INDENT3 "WheelYVelocityControlParameters: %s", + mWheelYVelocityControl.getParameters().dump().c_str()); + dump += StringPrintf(INDENT3 "WheelXVelocityControlParameters: %s", + mWheelXVelocityControl.getParameters().dump().c_str()); dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str()); @@ -296,10 +300,11 @@ std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) { mCursorScrollAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - const nsecs_t eventTime = + const auto [eventTime, readTime] = applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), - rawEvent->when, mLastEventTime); - out += sync(eventTime, rawEvent->readTime); + rawEvent->when, rawEvent->readTime, + mLastEventTime); + out += sync(eventTime, readTime); mLastEventTime = eventTime; } return out; diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 56fc5fa99a..2809939c86 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -32,8 +32,10 @@ uint32_t ExternalStylusInputMapper::getSources() const { void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f); + if (mRawPressureAxis.valid) { + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f); + } } void ExternalStylusInputMapper::dump(std::string& dump) { @@ -79,13 +81,12 @@ std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) { mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; } - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; + auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure()); + mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) / + static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue); + } else if (mTouchButtonAccumulator.hasButtonTouch()) { + mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f; } mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index 844afe0522..8e3539c3a6 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -19,6 +19,7 @@ #include "InputMapper.h" #include "InputDevice.h" +#include "input/PrintTools.h" namespace android { @@ -129,7 +130,7 @@ void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAx void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str()); dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 8e757a5bf7..b193dffcfc 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -119,6 +119,18 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; } + } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) { + mStylusMtToolSeen = true; + // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically + // re-configure this input device so that we add SOURCE_STYLUS if we haven't already. + // This is to cover the case where we cannot reliably detect whether a multi-touch + // device will ever produce stylus events when it is initially being configured. + if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) { + // Add the stylus source immediately so that it is included in any events generated + // before we have a chance to re-configure the device. + mSource |= AINPUT_SOURCE_STYLUS; + bumpGeneration(); + } } if (shouldSimulateStylusWithTouch() && outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { @@ -200,7 +212,7 @@ void MultiTouchInputMapper::configureRawPointerAxes() { } bool MultiTouchInputMapper::hasStylus() const { - return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() || + return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() || shouldSimulateStylusWithTouch(); } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index ddf9e80a6c..5f8bccf9e6 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -50,6 +50,8 @@ private: // Specifies the pointer id bits that are in use, and their associated tracking id. BitSet32 mPointerIdBits; int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; + + bool mStylusMtToolSeen{false}; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 29a1bda3ee..06d4dc342d 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -97,35 +97,34 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> out; - PointerCoords pointerCoords; - pointerCoords.clear(); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); bool scrolled = scroll != 0; - // This is not a pointer, so it's not associated with a display. - int32_t displayId = ADISPLAY_ID_NONE; - - // Moving the rotary encoder should wake the device (if specified). - uint32_t policyFlags = 0; - if (scrolled && getDeviceContext().isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mOrientation == DISPLAY_ORIENTATION_180) { - scroll = -scroll; - } - // Send motion event. if (scrolled) { int32_t metaState = getContext()->getGlobalMetaState(); + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + if (mOrientation == DISPLAY_ORIENTATION_180) { + scroll = -scroll; + } + + PointerCoords pointerCoords; + pointerCoords.clear(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + uint32_t policyFlags = 0; + if (getDeviceContext().isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + out.push_back( NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 0b7ff84c99..d8a4d34d20 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -110,10 +110,11 @@ static bool isPointerDown(int32_t buttonState) { // coordinates result in extremely large instantaneous velocities, which can negatively impact // user experience. To avoid this, we augment the timestamps so that subsequent event timestamps // differ by at least a minimum delta value. -static nsecs_t applyBluetoothTimestampSmoothening(const InputDeviceIdentifier& identifier, - nsecs_t currentEventTime, nsecs_t lastEventTime) { +static std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening( + const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime, + nsecs_t lastEventTime) { if (identifier.bus != BUS_BLUETOOTH) { - return currentEventTime; + return {currentEventTime, readTime}; } // Assume the fastest rate at which a Bluetooth touch device can report input events is one @@ -123,8 +124,14 @@ static nsecs_t applyBluetoothTimestampSmoothening(const InputDeviceIdentifier& i // We define a maximum smoothing time delta so that we don't generate events too far into the // future. constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); - return std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA), - currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA); + const nsecs_t smoothenedEventTime = + std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA), + currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA); + // If we are modifying the event time, treat this event as a synthetically generated event for + // latency tracking purposes and use the event time as the read time (zero read latency). + const nsecs_t smoothenedReadTime = + smoothenedEventTime != currentEventTime ? currentEventTime : readTime; + return {smoothenedEventTime, smoothenedReadTime}; } } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index bf73ce5db8..5631a10253 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -21,6 +21,7 @@ #include "TouchInputMapper.h" #include <ftl/enum.h> +#include <input/PrintTools.h> #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" @@ -31,13 +32,6 @@ namespace android { // --- Constants --- -// Maximum amount of latency to add to touch events while waiting for data from an -// external stylus. -static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); - -// Maximum amount of time to wait on touch data before pushing out new pressure data. -static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); - // Artificial latency on synthetic events created from stylus data without corresponding touch // data. static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); @@ -48,6 +42,19 @@ static const float MIN_FREEFORM_GESTURE_WIDTH_IN_MILLIMETER = 30; static const DisplayViewport kUninitializedViewport; +static std::string toString(const Rect& rect) { + return base::StringPrintf("Rect{%d, %d, %d, %d}", rect.left, rect.top, rect.right, rect.bottom); +} + +static std::string toString(const ui::Size& size) { + return base::StringPrintf("%dx%d", size.width, size.height); +} + +static bool isPointInRect(const Rect& rect, int32_t x, int32_t y) { + // Consider all four sides as "inclusive". + return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; +} + template <typename T> inline static void swap(T& a, T& b) { T temp = a; @@ -73,6 +80,33 @@ inline static int32_t signExtendNybble(int32_t value) { return value >= 8 ? value - 16 : value; } +static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo( + const DisplayViewport& viewport, int32_t naturalOrientation) { + const auto rotation = ui::toRotation(naturalOrientation); + + ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight}; + if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) { + std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height); + } + + ui::Transform rotate(ui::Transform::toRotationFlags(rotation), rotatedDisplaySize.width, + rotatedDisplaySize.height); + + Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight, + viewport.physicalBottom}; + physicalFrame = rotate.transform(physicalFrame); + + LOG_ALWAYS_FATAL_IF(!physicalFrame.isValid()); + if (physicalFrame.isEmpty()) { + ALOGE("Viewport is not set properly: %s", viewport.toString().c_str()); + physicalFrame.right = + physicalFrame.left + (physicalFrame.width() == 0 ? 1 : physicalFrame.width()); + physicalFrame.bottom = + physicalFrame.top + (physicalFrame.height() == 0 ? 1 : physicalFrame.height()); + } + return {rotatedDisplaySize, physicalFrame}; +} + // --- RawPointerData --- void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { @@ -99,12 +133,6 @@ TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) mTouchButtonAccumulator(deviceContext), mSource(0), mDeviceMode(DeviceMode::DISABLED), - mDisplayWidth(-1), - mDisplayHeight(-1), - mPhysicalWidth(-1), - mPhysicalHeight(-1), - mPhysicalLeft(0), - mPhysicalTop(0), mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {} TouchInputMapper::~TouchInputMapper() {} @@ -258,9 +286,12 @@ void TouchInputMapper::dump(std::string& dump) { dump += INDENT3 "Stylus Fusion:\n"; dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", toString(mExternalStylusConnected)); - dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); + dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n", + toString(mFusedStylusPointerId).c_str()); dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", mExternalStylusFusionTimeout); + dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x", + mExternalStylusButtonsApplied); dump += INDENT3 "External Stylus State:\n"; dumpStylusState(dump, mExternalStylusState); @@ -567,7 +598,7 @@ void TouchInputMapper::initializeSizeRanges() { } // Size of diagonal axis. - const float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight); + const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height); // Size factors. if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { @@ -650,8 +681,8 @@ void TouchInputMapper::initializeSizeRanges() { void TouchInputMapper::initializeOrientedRanges() { // Configure X and Y factors. - mXScale = float(mDisplayWidth) / mRawPointerAxes.getRawWidth(); - mYScale = float(mDisplayHeight) / mRawPointerAxes.getRawHeight(); + mXScale = float(mDisplayBounds.width) / mRawPointerAxes.getRawWidth(); + mYScale = float(mDisplayBounds.height) / mRawPointerAxes.getRawHeight(); mXPrecision = 1.0f / mXScale; mYPrecision = 1.0f / mYScale; @@ -787,13 +818,13 @@ void TouchInputMapper::initializeOrientedRanges() { mOrientedYPrecision = mXPrecision; mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mDisplayHeight - 1; + mOrientedRanges.x.max = mDisplayBounds.height - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mDisplayWidth - 1; + mOrientedRanges.y.max = mDisplayBounds.width - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; @@ -804,13 +835,13 @@ void TouchInputMapper::initializeOrientedRanges() { mOrientedYPrecision = mYPrecision; mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mDisplayWidth - 1; + mOrientedRanges.x.max = mDisplayBounds.width - 1; mOrientedRanges.x.flat = 0; mOrientedRanges.x.fuzz = 0; mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mDisplayHeight - 1; + mOrientedRanges.y.max = mDisplayBounds.height - 1; mOrientedRanges.y.flat = 0; mOrientedRanges.y.fuzz = 0; mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; @@ -871,8 +902,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) } // Raw width and height in the natural orientation. - const int32_t rawWidth = mRawPointerAxes.getRawWidth(); - const int32_t rawHeight = mRawPointerAxes.getRawHeight(); + const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()}; const int32_t rawXResolution = mRawPointerAxes.x.resolution; const int32_t rawYResolution = mRawPointerAxes.y.resolution; // Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0. @@ -888,67 +918,16 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mViewport = newViewport; if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) { - // Convert rotated viewport to the natural orientation. - int32_t naturalPhysicalWidth, naturalPhysicalHeight; - int32_t naturalPhysicalLeft, naturalPhysicalTop; - int32_t naturalDeviceWidth, naturalDeviceHeight; + const auto oldDisplayBounds = mDisplayBounds; // Apply the inverse of the input device orientation so that the input device is // configured in the same orientation as the viewport. The input device orientation will // be re-applied by mInputDeviceOrientation. const int32_t naturalDeviceOrientation = (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4; - switch (naturalDeviceOrientation) { - case DISPLAY_ORIENTATION_90: - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; - naturalPhysicalTop = mViewport.physicalLeft; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_180: - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; - naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - case DISPLAY_ORIENTATION_270: - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_0: - default: - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.physicalLeft; - naturalPhysicalTop = mViewport.physicalTop; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - } - - if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { - ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); - naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; - naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; - } - - mPhysicalWidth = naturalPhysicalWidth; - mPhysicalHeight = naturalPhysicalHeight; - mPhysicalLeft = naturalPhysicalLeft; - mPhysicalTop = naturalPhysicalTop; - const int32_t oldDisplayWidth = mDisplayWidth; - const int32_t oldDisplayHeight = mDisplayHeight; - mDisplayWidth = naturalDeviceWidth; - mDisplayHeight = naturalDeviceHeight; + std::tie(mDisplayBounds, mPhysicalFrameInDisplay) = + getNaturalDisplayInfo(mViewport, naturalDeviceOrientation); // InputReader works in the un-rotated display coordinate space, so we don't need to do // anything if the device is already orientation-aware. If the device is not @@ -961,20 +940,14 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) // For orientation-aware devices that work in the un-rotated coordinate space, the // viewport update should be skipped if it is only a change in the orientation. skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware && - mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight && - viewportOrientationChanged; + mDisplayBounds == oldDisplayBounds && viewportOrientationChanged; // Apply the input device orientation for the device. mInputDeviceOrientation = (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4; } else { - mPhysicalWidth = rawWidth; - mPhysicalHeight = rawHeight; - mPhysicalLeft = 0; - mPhysicalTop = 0; - - mDisplayWidth = rawWidth; - mDisplayHeight = rawHeight; + mDisplayBounds = rawSize; + mPhysicalFrameInDisplay = Rect{mDisplayBounds}; mInputDeviceOrientation = DISPLAY_ORIENTATION_0; } } @@ -1006,9 +979,9 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) } if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, " "display id %d", - getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight, + getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(), mInputDeviceOrientation, mDeviceMode, mViewport.displayId); configureVirtualKeys(); @@ -1020,8 +993,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) if (mDeviceMode == DeviceMode::POINTER) { // Compute pointer gesture detection parameters. - float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight); + float rawDiagonal = hypotf(rawSize.width, rawSize.height); + float displayDiagonal = hypotf(mDisplayBounds.width, mDisplayBounds.height); // Scale movements such that one whole swipe of the touch pad covers a // given area relative to the diagonal size of the display when no acceleration @@ -1057,12 +1030,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) void TouchInputMapper::dumpDisplay(std::string& dump) { dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth); - dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight); - dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); - dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); - dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); - dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); + dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str()); + dump += StringPrintf(INDENT3 "PhysicalFrame: %s\n", toString(mPhysicalFrameInDisplay).c_str()); dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation); } @@ -1101,17 +1070,17 @@ void TouchInputMapper::configureVirtualKeys() { int32_t halfWidth = virtualKeyDefinition.width / 2; int32_t halfHeight = virtualKeyDefinition.height / 2; - virtualKey.hitLeft = - (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth + + virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / + mDisplayBounds.width + touchScreenLeft; - virtualKey.hitRight = - (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth + + virtualKey.hitRight = (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / + mDisplayBounds.width + touchScreenLeft; - virtualKey.hitTop = - (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight + + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / + mDisplayBounds.height + touchScreenTop; - virtualKey.hitBottom = - (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight + + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / + mDisplayBounds.height + touchScreenTop; mVirtualKeys.push_back(virtualKey); } @@ -1417,9 +1386,10 @@ std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) { void TouchInputMapper::resetExternalStylus() { mExternalStylusState.clear(); - mExternalStylusId = -1; + mFusedStylusPointerId.reset(); mExternalStylusFusionTimeout = LLONG_MAX; mExternalStylusDataPending = false; + mExternalStylusButtonsApplied = 0; } void TouchInputMapper::clearStylusDataPendingFlags() { @@ -1469,8 +1439,9 @@ std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { const RawState& last = mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1]; - next.when = applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when, - last.when); + std::tie(next.when, next.readTime) = + applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when, + readTime, last.when); // Assign pointer ids. if (!mHavePointerIds) { @@ -1600,8 +1571,7 @@ std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t re uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + if (isStylusToolType(pointer.toolType)) { mCurrentCookedState.stylusIdBits.markBit(id); } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { @@ -1614,8 +1584,7 @@ std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t re uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || - pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + if (isStylusToolType(pointer.toolType)) { mCurrentCookedState.stylusIdBits.markBit(id); } } @@ -1693,29 +1662,41 @@ bool TouchInputMapper::isTouchScreen() { } void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { - if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) { - mCurrentRawState.buttonState |= mExternalStylusState.buttons; + if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) { + // If any of the external buttons are already pressed by the touch device, ignore them. + const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons; + const int32_t releasedButtons = + mExternalStylusButtonsApplied & ~mExternalStylusState.buttons; + + mCurrentRawState.buttonState |= pressedButtons; + mCurrentRawState.buttonState &= ~releasedButtons; + + mExternalStylusButtonsApplied |= pressedButtons; + mExternalStylusButtonsApplied &= ~releasedButtons; } } void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) { + return; + } - if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { - const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + float pressure = lastPointerData.isTouching(*mFusedStylusPointerId) + ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId) + .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) + : 0.f; + if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) { + pressure = *mExternalStylusState.pressure; + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { PointerProperties& properties = - currentPointerData.editPointerPropertiesWithId(mExternalStylusId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } + currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId); + properties.toolType = mExternalStylusState.toolType; } } @@ -1724,34 +1705,48 @@ bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeou return false; } + // Check if the stylus pointer has gone up. + if (mFusedStylusPointerId && + !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up"); + mFusedStylusPointerId.reset(); + return false; + } + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { - ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion"); - mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { - ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus."); - resetExternalStylus(); - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } - ALOGD_IF(DEBUG_STYLUS_FUSION, - "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)", - mExternalStylusFusionTimeout); - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } + if (!initialDown) { + return false; } - // Check if the stylus pointer has gone up. - if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { - ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up"); - mExternalStylusId = -1; + if (!mExternalStylusState.pressure) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed"); + return false; } - return false; + if (*mExternalStylusState.pressure != 0.0f) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion"); + mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + return false; + } + + if (timeout) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus."); + mFusedStylusPointerId.reset(); + mExternalStylusFusionTimeout = LLONG_MAX; + return false; + } + + // We are waiting for the external stylus to report a pressure value. Withhold touches from + // being processed until we either get pressure data or timeout. + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } + ALOGD_IF(DEBUG_STYLUS_FUSION, + "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)", + mExternalStylusFusionTimeout); + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; } std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) { @@ -1763,7 +1758,7 @@ std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) { out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/); } } else if (mDeviceMode == DeviceMode::DIRECT) { - if (mExternalStylusFusionTimeout < when) { + if (mExternalStylusFusionTimeout <= when) { out += processRawTouches(true /*timeout*/); } else if (mExternalStylusFusionTimeout != LLONG_MAX) { getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); @@ -1774,11 +1769,14 @@ std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) { std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) { std::list<NotifyArgs> out; - mExternalStylusState.copyFrom(state); - if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { - // We're either in the middle of a fused stream of data or we're waiting on data before - // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus - // data. + const bool buttonsChanged = mExternalStylusState.buttons != state.buttons; + mExternalStylusState = state; + if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) { + // The following three cases are handled here: + // - We're in the middle of a fused stream of data; + // - We're waiting on external stylus data before dispatching the initial down; or + // - Only the button state, which is not reported through a specific pointer, has changed. + // Go ahead and dispatch now that we have fresh stylus data. mExternalStylusDataPending = true; out += processRawTouches(false /*timeout*/); } @@ -3833,9 +3831,8 @@ bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const { const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale; return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight); + isPointInRect(mPhysicalFrameInDisplay, xScaled, yScaled); } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index c20f28bc19..3962b2a2fc 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -27,6 +27,13 @@ namespace android { +// Maximum amount of latency to add to touch events while waiting for data from an +// external stylus. +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); + +// Maximum amount of time to wait on touch data before pushing out new pressure data. +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); + /* Raw axis information from the driver. */ struct RawPointerAxes { RawAbsoluteAxisInfo x{}; @@ -355,9 +362,14 @@ protected: // State provided by an external stylus StylusState mExternalStylusState; - int64_t mExternalStylusId; + // If an external stylus is capable of reporting pointer-specific data like pressure, we will + // attempt to fuse the pointer data reported by the stylus to the first touch pointer. This is + // the id of the pointer to which the external stylus data is fused. + std::optional<uint32_t> mFusedStylusPointerId; nsecs_t mExternalStylusFusionTimeout; bool mExternalStylusDataPending; + // A subset of the buttons in mCurrentRawState that came from an external stylus. + int32_t mExternalStylusButtonsApplied; // True if we sent a HOVER_ENTER event. bool mSentHoverEnter; @@ -400,17 +412,14 @@ private: // The components of the viewport are specified in the display's rotated orientation. DisplayViewport mViewport; - // The width and height are obtained from the viewport and are specified - // in the natural orientation. - int32_t mDisplayWidth; - int32_t mDisplayHeight; + // We refer to the display as being in the "natural orientation" when there is no rotation + // applied. The display size obtained from the viewport in the natural orientation. + // Always starts at (0, 0). + ui::Size mDisplayBounds{ui::kInvalidSize}; - // The physical frame is the rectangle in the display's coordinate space that maps to the + // The physical frame is the rectangle in the natural display's coordinate space that maps to // the logical display frame. - int32_t mPhysicalWidth; - int32_t mPhysicalHeight; - int32_t mPhysicalLeft; - int32_t mPhysicalTop; + Rect mPhysicalFrameInDisplay{Rect::INVALID_RECT}; // The orientation of the input device relative to that of the display panel. It specifies // the rotation of the input device coordinates required to produce the display panel @@ -783,6 +792,8 @@ private: [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + // Attempts to assign a pointer id to the external stylus. Returns true if the state should be + // withheld from further processing while waiting for data from the stylus. bool assignExternalStylusId(const RawState& state, bool timeout); void applyExternalStylusButtonState(nsecs_t when); void applyExternalStylusTouchState(nsecs_t when); diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index b0cef676fb..8746729425 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -24,12 +24,11 @@ namespace android { // --- MultiTouchMotionAccumulator --- MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), mUsingSlotsProtocol(false), mHaveStylus(false) {} + : mCurrentSlot(-1), mUsingSlotsProtocol(false) {} void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol) { mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); mSlots = std::vector<Slot>(slotCount); mCurrentSlot = -1; @@ -146,10 +145,6 @@ void MultiTouchMotionAccumulator::finishSync() { } } -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { if (!slot.mInUse) { ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h index 625a00fdd4..62bc780df5 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -75,7 +75,6 @@ public: void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); void process(const RawEvent* rawEvent); void finishSync(); - bool hasStylus() const; inline size_t getSlotCount() const { return mSlots.size(); } inline const Slot& getSlot(size_t index) const { @@ -87,7 +86,6 @@ private: int32_t mCurrentSlot; std::vector<Slot> mSlots; bool mUsingSlotsProtocol; - bool mHaveStylus; void resetSlots(); void warnIfNotInUse(const RawEvent& event, const Slot& slot); diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp index 1891205ed6..bc23a8ec91 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -167,4 +167,8 @@ bool TouchButtonAccumulator::hasStylus() const { return mHaveStylus; } +bool TouchButtonAccumulator::hasButtonTouch() const { + return mHaveBtnTouch; +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h index 65b0a62747..c2de23cee4 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -40,6 +40,7 @@ public: bool isToolActive() const; bool isHovering() const; bool hasStylus() const; + bool hasButtonTouch() const; private: bool mHaveBtnTouch{}; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 611fe3d625..2e5bec9999 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -49,6 +49,7 @@ cc_test { "InputDispatcher_test.cpp", "InputReader_test.cpp", "LatencyTracker_test.cpp", + "NotifyArgs_test.cpp", "PreferStylusOverTouch_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", @@ -84,9 +85,6 @@ cc_test { "libc++fs", "libgmock", ], - shared_libs: [ - "libinputreader", - ], require_root: true, test_options: { unit_test: true, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index aaf50ce5ed..f3239caa6a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -22,6 +22,7 @@ #include <android-base/thread_annotations.h> #include <binder/Binder.h> #include <fcntl.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/Input.h> #include <linux/input.h> @@ -43,6 +44,7 @@ using android::os::InputEventInjectionSync; namespace android::inputdispatcher { using namespace ftl::flag_operators; +using testing::AllOf; // An arbitrary time value. static constexpr nsecs_t ARBITRARY_TIME = 1234; @@ -102,6 +104,28 @@ static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) { << MotionEvent::actionToString(receivedAction); } +MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") { + bool matches = action == arg.getAction(); + if (!matches) { + *result_listener << "expected action " << MotionEvent::actionToString(action) + << ", but got " << MotionEvent::actionToString(arg.getAction()); + } + if (action == AMOTION_EVENT_ACTION_CANCEL) { + if (!matches) { + *result_listener << "; "; + } + *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set"; + matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + } + return matches; +} + +MATCHER_P(WithSource, source, "InputEvent with specified source") { + *result_listener << "expected source " << inputEventSourceToString(source) << ", but got " + << inputEventSourceToString(arg.getSource()); + return arg.getSource() == source; +} + // --- FakeInputDispatcherPolicy --- class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { @@ -1205,6 +1229,12 @@ public: mInputReceiver->consumeCaptureEvent(hasCapture); } + void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { + MotionEvent* motionEvent = consumeMotion(); + ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event"; + ASSERT_THAT(*motionEvent, matcher); + } + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, std::optional<int32_t> expectedDisplayId, std::optional<int32_t> expectedFlags) { @@ -1892,6 +1922,64 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { wallpaperWindow->assertNoEvents(); } +TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + window->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> wallpaperWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setIsWallpaper(true); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + wallpaperWindow->setPreventSplitting(true); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionPointerDown(1); + wallpaperWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + const MotionEvent secondFingerUpEvent = + MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionPointerUp(1); + wallpaperWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionUp(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); +} + /** * On the display, have a single window, and also an area where there's no window. * First pointer touches the "no window" area of the screen. Second pointer touches the window. @@ -2207,6 +2295,101 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); } +/** + * Inject a mouse hover event followed by a tap from touchscreen. + * In the current implementation, the tap does not cause a HOVER_EXIT event. + */ +TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // Inject a hover_move from mouse. + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE, + ADISPLAY_ID_DEFAULT, {{50, 50}}); + motionArgs.xCursorPosition = 50; + motionArgs.yCursorPosition = 50; + mDispatcher->notifyMotion(&motionArgs); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithSource(AINPUT_SOURCE_MOUSE)))); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE)))); + + // Tap on the window + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {{10, 10}}); + mDispatcher->notifyMotion(&motionArgs); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_TOUCHSCREEN)))); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {{10, 10}}); + mDispatcher->notifyMotion(&motionArgs); + ASSERT_NO_FATAL_FAILURE( + window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithSource(AINPUT_SOURCE_TOUCHSCREEN)))); +} + +TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> windowDefaultDisplay = + sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay", + ADISPLAY_ID_DEFAULT); + windowDefaultDisplay->setFrame(Rect(0, 0, 600, 800)); + sp<FakeWindowHandle> windowSecondDisplay = + sp<FakeWindowHandle>::make(application, mDispatcher, "SecondDisplay", + SECOND_DISPLAY_ID); + windowSecondDisplay->setFrame(Rect(0, 0, 600, 800)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, + {SECOND_DISPLAY_ID, {windowSecondDisplay}}}); + + // Set cursor position in window in default display and check that hover enter and move + // events are generated. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .displayId(ADISPLAY_ID_DEFAULT) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(600)) + .build())); + windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + // Remove all windows in secondary display and check that no event happens on window in + // primary display. + mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); + windowDefaultDisplay->assertNoEvents(); + + // Move cursor position in window in default display and check that only hover move + // event is generated and not hover enter event. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, + {SECOND_DISPLAY_ID, {windowSecondDisplay}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .displayId(ADISPLAY_ID_DEFAULT) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(400) + .y(700)) + .build())); + windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowDefaultDisplay->assertNoEvents(); +} + TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -2400,6 +2583,43 @@ TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) { window->assertNoEvents(); } +TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, + "Fake Window", ADISPLAY_ID_DEFAULT); + // Ensure window is non-split and have some transform. + window->setPreventSplitting(true); + window->setWindowOffset(20, 40); + mDispatcher->onWindowInfosChanged({*window->getInfo()}, {}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(-30) + .y(-50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + const MotionEvent* event = window->consumeMotion(); + EXPECT_EQ(POINTER_1_DOWN, event->getAction()); + EXPECT_EQ(70, event->getX(0)); // 50 + 20 + EXPECT_EQ(90, event->getY(0)); // 50 + 40 + EXPECT_EQ(-10, event->getX(1)); // -30 + 20 + EXPECT_EQ(-10, event->getY(1)); // -50 + 40 +} + /** * Ensure the correct coordinate spaces are used by InputDispatcher. * diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8aaf066929..c72d01fad8 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -388,6 +388,8 @@ protected: int32_t mGlobalMetaState; bool mUpdateGlobalMetaStateWasCalled; int32_t mGeneration; + std::optional<nsecs_t> mRequestedTimeout; + std::vector<InputDeviceInfo> mExternalStylusDevices; public: FakeInputReaderContext(InputReader* reader) @@ -421,6 +423,29 @@ protected: mGeneration = ContextImpl::bumpGeneration(); return mGeneration; } + + void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; } + + void assertTimeoutWasRequested(nsecs_t when) { + ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when + << " but there was no timeout requested."; + ASSERT_EQ(when, *mRequestedTimeout); + mRequestedTimeout.reset(); + } + + void assertTimeoutWasNotRequested() { + ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested," + " but one was requested at time " + << *mRequestedTimeout; + } + + void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override { + outDevices = mExternalStylusDevices; + } + + void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) { + mExternalStylusDevices = devices; + } } mFakeContext; friend class InputReaderTest; @@ -1545,7 +1570,8 @@ TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) { ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode); } -// --- TouchProcessTest --- +// --- TouchIntegrationTest --- + class TouchIntegrationTest : public InputReaderIntegrationTest { protected: const std::string UNIQUE_ID = "local:0"; @@ -1592,6 +1618,14 @@ protected: InputDeviceInfo mDeviceInfo; }; +TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) { + // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus + // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button + // presses). + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, + mDeviceInfo.getSources()); +} + TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -1858,108 +1892,412 @@ TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) { ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId())); } -TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) { - mDevice->sendKey(BTN_STYLUS, 1); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( +// --- StylusButtonIntegrationTest --- + +// Verify the behavior of button presses reported by various kinds of styluses, including buttons +// reported by the touchscreen's device, by a fused external stylus, and by an un-fused external +// stylus. +template <typename UinputStylusDevice> +class StylusButtonIntegrationTest : public TouchIntegrationTest { +protected: + void SetUp() override { +#if !defined(__ANDROID__) + GTEST_SKIP(); +#endif + TouchIntegrationTest::SetUp(); + mTouchscreen = mDevice.get(); + mTouchscreenInfo = mDeviceInfo; + + setUpStylusDevice(); + } + + UinputStylusDevice* mStylus{nullptr}; + InputDeviceInfo mStylusInfo{}; + + UinputTouchScreen* mTouchscreen{nullptr}; + InputDeviceInfo mTouchscreenInfo{}; + +private: + // When we are attempting to test stylus button events that are sent from the touchscreen, + // use the same Uinput device for the touchscreen and the stylus. + template <typename T = UinputStylusDevice> + std::enable_if_t<std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() { + mStylus = mDevice.get(); + mStylusInfo = mDeviceInfo; + } + + // When we are attempting to stylus buttons from an external stylus being merged with touches + // from a touchscreen, create a new Uinput device through which stylus buttons can be injected. + template <typename T = UinputStylusDevice> + std::enable_if_t<!std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() { + mStylusDeviceLifecycleTracker = createUinputDevice<T>(); + mStylus = mStylusDeviceLifecycleTracker.get(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto info = findDeviceByName(mStylus->getName()); + ASSERT_TRUE(info); + mStylusInfo = *info; + } + + std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{}; + + // Hide the base class's device to expose it with a different name for readability. + using TouchIntegrationTest::mDevice; + using TouchIntegrationTest::mDeviceInfo; +}; + +using StylusButtonIntegrationTestTypes = + ::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>; +TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes); + +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) { + const auto stylusId = TestFixture::mStylusInfo.getId(); + + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), - WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); - mDevice->sendKey(BTN_STYLUS, 0); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), - WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); } -TEST_F(TouchIntegrationTest, StylusButtonsSurroundingTouchGesture) { - const Point centerPoint = mDevice->getCenterPoint(); +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) { + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); // Press the stylus button. - mDevice->sendKey(BTN_STYLUS, 1); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), - WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); // Start and finish a stylus gesture. - mDevice->sendSlot(FIRST_SLOT); - mDevice->sendTrackingId(FIRST_TRACKING_ID); - mDevice->sendToolType(MT_TOOL_PEN); - mDevice->sendDown(centerPoint); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), - WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), - WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); - mDevice->sendTrackingId(INVALID_TRACKING_ID); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); // Release the stylus button. - mDevice->sendKey(BTN_STYLUS, 0); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), - WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); } -TEST_F(TouchIntegrationTest, StylusButtonsWithinTouchGesture) { - const Point centerPoint = mDevice->getCenterPoint(); +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) { + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); + auto toolTypeDevice = + AllOf(WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithDeviceId(touchscreenId)); + + // Press the stylus button. + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + + // Start hovering with the stylus. + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendMove(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Touch down with the stylus. + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Stop touching with the stylus, and start hovering. + TestFixture::mTouchscreen->sendUp(); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendMove(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Stop hovering. + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0)))); + // TODO(b/257971675): Fix inconsistent button state when exiting hover. + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Release the stylus button. + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); +} + +TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) { + const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint(); + const auto touchscreenId = TestFixture::mTouchscreenInfo.getId(); + const auto stylusId = TestFixture::mStylusInfo.getId(); // Start a stylus gesture. - mDevice->sendSlot(FIRST_SLOT); - mDevice->sendTrackingId(FIRST_TRACKING_ID); - mDevice->sendToolType(MT_TOOL_PEN); - mDevice->sendDown(centerPoint); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + TestFixture::mTouchscreen->sendSlot(FIRST_SLOT); + TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID); + TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN); + TestFixture::mTouchscreen->sendDown(centerPoint); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); // Press and release a stylus button. Each change in button state also generates a MOVE event. - mDevice->sendKey(BTN_STYLUS, 1); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + TestFixture::mStylus->pressKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), - WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), - WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), - WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY), + WithDeviceId(touchscreenId)))); - mDevice->sendKey(BTN_STYLUS, 0); - mDevice->sendSync(); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + TestFixture::mStylus->releaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled( AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), - WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); // Finish the stylus gesture. + TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID); + TestFixture::mTouchscreen->sendSync(); + ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId)))); +} + +// --- ExternalStylusIntegrationTest --- + +// Verify the behavior of an external stylus. An external stylus can report pressure or button +// data independently of the touchscreen, which is then sent as a MotionEvent as part of an +// ongoing stylus gesture that is being emitted by the touchscreen. +using ExternalStylusIntegrationTest = TouchIntegrationTest; + +TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus capable of reporting pressure data that + // should be fused with a touch pointer. + std::unique_ptr<UinputExternalStylusWithPressure> stylus = + createUinputDevice<UinputExternalStylusWithPressure>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Set a pressure value on the stylus. It doesn't generate any events. + const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX; + stylus->setPressure(100); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Start a finger gesture, and ensure it shows up as stylus gesture + // with the pressure set by the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId), WithPressure(100.f / RAW_PRESSURE_MAX)))); + + // Change the pressure on the external stylus, and ensure the touchscreen generates a MOVE + // event with the updated pressure. + stylus->setPressure(200); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX)))); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); +} + +TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus capable of reporting pressure data that + // should be fused with a touch pointer. + std::unique_ptr<UinputExternalStylusWithPressure> stylus = + createUinputDevice<UinputExternalStylusWithPressure>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Set a pressure value of 0 on the stylus. It doesn't generate any events. + const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX; + // Send a non-zero value first to prevent the kernel from consuming the zero event. + stylus->setPressure(100); + stylus->setPressure(0); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Start a finger gesture. The touch device will withhold generating any touches for + // up to 72 milliseconds while waiting for pressure data from the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + auto waitUntil = std::chrono::system_clock::now() + + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntil)); + + // Since the external stylus did not report a pressure value within the timeout, + // it shows up as a finger pointer. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDeviceId(touchscreenId), + WithPressure(1.f)))); + + // Change the pressure on the external stylus. Since the pressure was not present at the start + // of the gesture, it is ignored for now. + stylus->setPressure(200); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Finish the finger gesture. mDevice->sendTrackingId(INVALID_TRACKING_ID); mDevice->sendSync(); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + // Start a new gesture. Since we have a valid pressure value, it shows up as a stylus. + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0), + WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX)))); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); +} + +TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus device that does not support pressure. It should not affect any + // touch pointers. + std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Start a finger gesture and ensure a finger pointer is generated for it, without waiting for + // pressure data from the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + auto waitUntil = std::chrono::system_clock::now() + + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener + ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType( + AMOTION_EVENT_TOOL_TYPE_FINGER), + WithButtonState(0), + WithDeviceId(touchscreenId), + WithPressure(1.f)), + waitUntil)); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); } // --- InputDeviceTest --- @@ -2413,6 +2751,16 @@ protected: mReader->loopOnce(); } + std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) { + std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when); + for (const NotifyArgs& args : generatedArgs) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); + return generatedArgs; + } + static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) { const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); @@ -6607,6 +6955,360 @@ TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) { } } +// --- ExternalStylusFusionTest --- + +class ExternalStylusFusionTest : public SingleTouchInputMapperTest { +public: + SingleTouchInputMapper& initializeInputMapperWithExternalStylus() { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); + prepareAxes(POSITION); + auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + mStylusState.when = ARBITRARY_TIME; + mStylusState.pressure = 0.f; + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo}); + configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); + processExternalStylusState(mapper); + return mapper; + } + + std::list<NotifyArgs> processExternalStylusState(InputMapper& mapper) { + std::list<NotifyArgs> generatedArgs = mapper.updateExternalStylusState(mStylusState); + for (const NotifyArgs& args : generatedArgs) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); + return generatedArgs; + } + +protected: + StylusState mStylusState{}; + static constexpr uint32_t EXPECTED_SOURCE = + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS; + + void testStartFusedStylusGesture(SingleTouchInputMapper& mapper) { + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + // The first pointer is withheld. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested( + ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT)); + + // The external stylus reports pressure. The withheld finger pointer is released as a + // stylus. + mStylusState.pressure = 1.f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // Subsequent pointer events are not withheld. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } + + void testSuccessfulFusionGesture(SingleTouchInputMapper& mapper) { + ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper)); + + // Releasing the touch pointer ends the gesture. + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + mStylusState.pressure = 0.f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } + + void testUnsuccessfulFusionGesture(SingleTouchInputMapper& mapper) { + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)); + + // The first pointer is withheld when an external stylus is connected, + // and a timeout is requested. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested( + ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT)); + + // If the timeout expires early, it is requested again. + handleTimeout(mapper, ARBITRARY_TIME + 1); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested( + ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT)); + + // When the timeout expires, the withheld touch is released as a finger pointer. + handleTimeout(mapper, ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN)))); + + // Subsequent pointer events are not withheld. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } + +private: + InputDeviceInfo mExternalStylusDeviceInfo{}; +}; + +TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + ASSERT_EQ(EXPECTED_SOURCE, mapper.getSources()); +} + +TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); +} + +TEST_F(ExternalStylusFusionTest, SuccessfulFusion_TouchFirst) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); +} + +// Test a successful stylus fusion gesture where the pressure is reported by the external +// before the touch is reported by the touchscreen. +TEST_F(ExternalStylusFusionTest, SuccessfulFusion_PressureFirst) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + // The external stylus reports pressure first. It is ignored for now. + mStylusState.pressure = 1.f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // When the touch goes down afterwards, it is reported as a stylus pointer. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))); + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + +TEST_F(ExternalStylusFusionTest, FusionIsRepeatedForEachNewGesture) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); + + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); + ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); +} + +TEST_F(ExternalStylusFusionTest, FusedPointerReportsPressureChanges) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + mStylusState.pressure = 0.8f; + processExternalStylusState(mapper); + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithPressure(0.8f)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // The external stylus reports a pressure change. We wait for some time for a touch event. + mStylusState.pressure = 0.6f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is reported within the timeout, it reports the updated pressure. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.6f)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // There is another pressure change. + mStylusState.pressure = 0.5f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is not reported within the timeout, a move event is generated to report + // the new pressure. + handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.5f)))); + + // If a zero pressure is reported before the touch goes up, the previous pressure value is + // repeated indefinitely. + mStylusState.pressure = 0.0f; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + processMove(mapper, 102, 202); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.5f)))); + processMove(mapper, 103, 203); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithPressure(0.5f)))); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + +TEST_F(ExternalStylusFusionTest, FusedPointerReportsToolTypeChanges) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto source = WithSource(EXPECTED_SOURCE); + + mStylusState.pressure = 1.f; + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_ERASER; + processExternalStylusState(mapper); + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_ERASER)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // The external stylus reports a tool change. We wait for some time for a touch event. + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is reported within the timeout, it reports the updated pressure. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // There is another tool type change. + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is not reported within the timeout, a move event is generated to report + // the new tool type. + handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + +TEST_F(ExternalStylusFusionTest, FusedPointerReportsButtons) { + SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); + auto toolTypeSource = + AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)); + + ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper)); + + // The external stylus reports a button change. We wait for some time for a touch event. + mStylusState.buttons = AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is reported within the timeout, it reports the updated button state. + processMove(mapper, 101, 201); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + + // The button is now released. + mStylusState.buttons = 0; + processExternalStylusState(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE( + mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT)); + + // If a touch is not reported within the timeout, a move event is generated to report + // the new button state. + handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(0)))); + + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0)))); + + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + // --- MultiTouchInputMapperTest --- class MultiTouchInputMapperTest : public TouchInputMapperTest { @@ -9127,6 +9829,54 @@ TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the + // ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source. + // Due to limitations in the evdev protocol, we cannot say for certain that a device is capable + // of reporting stylus events just because it supports ABS_MT_TOOL_TYPE. + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); + + // However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be + // reported with the stylus source. + processId(mapper, FIRST_TRACKING_ID); + processToolType(mapper, MT_TOOL_PEN); + processPosition(mapper, 100, 200); + processPressure(mapper, RAW_PRESSURE_MAX); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + + // Now that we know the device supports styluses, ensure that the device is re-configured with + // the stylus source. + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, mapper.getSources()); + { + const auto& devices = mReader->getInputDevices(); + auto deviceInfo = + std::find_if(devices.begin(), devices.end(), + [](const InputDeviceInfo& info) { return info.getId() == DEVICE_ID; }); + LOG_ALWAYS_FATAL_IF(deviceInfo == devices.end(), "Cannot find InputDevice"); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, deviceInfo->getSources()); + } + + // Ensure the device was not reset to prevent interruptions of any ongoing gestures. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); + + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); +} + // --- MultiTouchInputMapperTest_ExternalDevice --- class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { diff --git a/services/inputflinger/tests/NotifyArgs_test.cpp b/services/inputflinger/tests/NotifyArgs_test.cpp new file mode 100644 index 0000000000..671558509d --- /dev/null +++ b/services/inputflinger/tests/NotifyArgs_test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 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 <NotifyArgs.h> +#include <utils/Timers.h> + +#include <gtest/gtest.h> +#include "android/input.h" +#include "input/Input.h" +#include "input/TouchVideoFrame.h" + +namespace android { + +// --- NotifyArgsTest --- + +/** + * Validate basic copy assignment. + */ +TEST(NotifyMotionArgsTest, TestCopyAssignmentOperator) { + int32_t id = 123; + nsecs_t downTime = systemTime(); + nsecs_t eventTime = downTime++; + nsecs_t readTime = downTime++; + int32_t deviceId = 7; + uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; + int32_t displayId = 42; + uint32_t policyFlags = POLICY_FLAG_GESTURE; + int32_t action = AMOTION_EVENT_ACTION_HOVER_MOVE; + int32_t actionButton = AMOTION_EVENT_BUTTON_PRIMARY; + int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + int32_t metaState = AMETA_SCROLL_LOCK_ON; + uint32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY; + MotionClassification classification = MotionClassification::DEEP_PRESS; + int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + uint32_t pointerCount = 2; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + float x = 0; + float y = 10; + + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x++); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y++); + } + + float xPrecision = 1.2f; + float yPrecision = 3.4f; + float xCursorPosition = 5.6f; + float yCursorPosition = 7.8f; + + std::vector<int16_t> videoData = {1, 2, 3, 4}; + timeval timestamp = {5, 6}; + TouchVideoFrame frame(2, 2, std::move(videoData), timestamp); + std::vector<TouchVideoFrame> videoFrames = {frame}; + const NotifyMotionArgs args(id, eventTime, readTime, deviceId, source, displayId, policyFlags, + action, actionButton, flags, metaState, buttonState, classification, + edgeFlags, pointerCount, pointerProperties, pointerCoords, + xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, + videoFrames); + + NotifyMotionArgs otherArgs{}; + otherArgs = args; + + EXPECT_EQ(args, otherArgs); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 5e47b8019b..2801072b6a 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -69,22 +69,24 @@ void TestInputListener::assertNotifyKeyWasNotCalled() { ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called.")); } -void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) { +void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs, + std::optional<TimePoint> waitUntil) { ASSERT_NO_FATAL_FAILURE( assertCalled<NotifyMotionArgs>(outEventArgs, - "Expected notifyMotion() to have been called.")); + "Expected notifyMotion() to have been called.", + waitUntil)); } void TestInputListener::assertNotifyMotionWasCalled( - const ::testing::Matcher<NotifyMotionArgs>& matcher) { + const ::testing::Matcher<NotifyMotionArgs>& matcher, std::optional<TimePoint> waitUntil) { NotifyMotionArgs outEventArgs; - ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs)); + ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs, waitUntil)); ASSERT_THAT(outEventArgs, matcher); } -void TestInputListener::assertNotifyMotionWasNotCalled() { +void TestInputListener::assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil) { ASSERT_NO_FATAL_FAILURE( - assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.")); + assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.", waitUntil)); } void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) { @@ -119,15 +121,18 @@ void TestInputListener::assertNotifyCaptureWasNotCalled() { } template <class NotifyArgsType> -void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) { +void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message, + std::optional<TimePoint> waitUntil) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); if (queue.empty()) { - const bool eventReceived = - mCondition.wait_for(lock, mEventHappenedTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + const auto time = + waitUntil.value_or(std::chrono::system_clock::now() + mEventHappenedTimeout); + const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); if (!eventReceived) { FAIL() << "Timed out waiting for event: " << message.c_str(); } @@ -139,14 +144,16 @@ void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string m } template <class NotifyArgsType> -void TestInputListener::assertNotCalled(std::string message) { +void TestInputListener::assertNotCalled(std::string message, std::optional<TimePoint> waitUntil) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); - const bool eventReceived = - mCondition.wait_for(lock, mEventDidNotHappenTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + const auto time = + waitUntil.value_or(std::chrono::system_clock::now() + mEventDidNotHappenTimeout); + const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); if (eventReceived) { FAIL() << "Unexpected event: " << message.c_str(); } diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 87752e1487..9665f702a1 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -33,6 +33,8 @@ public: std::chrono::milliseconds eventDidNotHappenTimeout = 0ms); virtual ~TestInputListener(); + using TimePoint = std::chrono::time_point<std::chrono::system_clock>; + void assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs = nullptr); @@ -48,11 +50,13 @@ public: void assertNotifyKeyWasNotCalled(); - void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr); + void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr, + std::optional<TimePoint> waitUntil = {}); - void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher); + void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher, + std::optional<TimePoint> waitUntil = {}); - void assertNotifyMotionWasNotCalled(); + void assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil = {}); void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr); @@ -63,10 +67,11 @@ public: private: template <class NotifyArgsType> - void assertCalled(NotifyArgsType* outEventArgs, std::string message); + void assertCalled(NotifyArgsType* outEventArgs, std::string message, + std::optional<TimePoint> waitUntil = {}); template <class NotifyArgsType> - void assertNotCalled(std::string message); + void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {}); template <class NotifyArgsType> void addToQueue(const NotifyArgsType* args); diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index 9a47e3e4e7..9db34227f5 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -46,7 +46,8 @@ MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") { } MATCHER_P(WithSource, source, "InputEvent with specified source") { - *result_listener << "expected source " << source << ", but got " << arg.source; + *result_listener << "expected source " << inputEventSourceToString(source) << ", but got " + << inputEventSourceToString(arg.source); return arg.source == source; } @@ -55,6 +56,11 @@ MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") { return arg.displayId == displayId; } +MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") { + *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId; + return arg.deviceId == deviceId; +} + MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") { *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode; return arg.keyCode == keyCode; @@ -70,8 +76,8 @@ MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") { MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") { const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - *result_listener << "expected pressure " << pressure << ", but got " << pressure; - return argPressure; + *result_listener << "expected pressure " << pressure << ", but got " << argPressure; + return argPressure == pressure; } MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") { diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index bc695b8bd8..97a26141e0 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -138,14 +138,36 @@ UinputSteamController::UinputSteamController() UinputExternalStylus::UinputExternalStylus() : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} +// --- UinputExternalStylusWithPressure --- + +UinputExternalStylusWithPressure::UinputExternalStylusWithPressure() + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} + +void UinputExternalStylusWithPressure::configureDevice(int fd, uinput_user_dev* device) { + UinputKeyboard::configureDevice(fd, device); + + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE); + device->absmin[ABS_PRESSURE] = RAW_PRESSURE_MIN; + device->absmax[ABS_PRESSURE] = RAW_PRESSURE_MAX; +} + +void UinputExternalStylusWithPressure::setPressure(int32_t pressure) { + injectEvent(EV_ABS, ABS_PRESSURE, pressure); + injectEvent(EV_SYN, SYN_REPORT, 0); +} + // --- UinputTouchScreen --- UinputTouchScreen::UinputTouchScreen(const Rect& size) - : UinputDevice(DEVICE_NAME, PRODUCT_ID), mSize(size) {} + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, + {BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}), + mSize(size) {} void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { + UinputKeyboard::configureDevice(fd, device); + // Setup the touch screen device - ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_EVBIT, EV_ABS); ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT); @@ -155,10 +177,6 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); - ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); - ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS); - ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2); - ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS3); device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN; device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX; @@ -210,10 +228,6 @@ void UinputTouchScreen::sendSync() { injectEvent(EV_SYN, SYN_REPORT, 0); } -void UinputTouchScreen::sendKey(int32_t scanCode, int32_t value) { - injectEvent(EV_KEY, scanCode, value); -} - // Get the center x, y base on the range definition. const Point UinputTouchScreen::getCenterPoint() { return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2); diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index d661bd36d3..51e331df8f 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -89,9 +89,9 @@ protected: explicit UinputKeyboard(const char* name, int16_t productId = PRODUCT_ID, std::initializer_list<int> keys = {}); -private: void configureDevice(int fd, uinput_user_dev* device) override; +private: std::set<int> mKeys; }; @@ -143,13 +143,35 @@ private: explicit UinputExternalStylus(); }; +// --- UinputExternalStylusWithPressure --- + +// A stylus that reports button presses and pressure values. +class UinputExternalStylusWithPressure : public UinputKeyboard { +public: + static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus With Pressure"; + static constexpr int16_t PRODUCT_ID = 46; + + static constexpr int32_t RAW_PRESSURE_MIN = 0; + static constexpr int32_t RAW_PRESSURE_MAX = 255; + + void setPressure(int32_t pressure); + + template <class D, class... Ts> + friend std::unique_ptr<D> createUinputDevice(Ts... args); + +private: + void configureDevice(int fd, uinput_user_dev* device) override; + + explicit UinputExternalStylusWithPressure(); +}; + // --- UinputTouchScreen --- // A multi-touch touchscreen device with specific size that also supports styluses. -class UinputTouchScreen : public UinputDevice { +class UinputTouchScreen : public UinputKeyboard { public: static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen"; - static constexpr int16_t PRODUCT_ID = 46; + static constexpr int16_t PRODUCT_ID = 47; static const int32_t RAW_TOUCH_MIN = 0; static const int32_t RAW_TOUCH_MAX = 31; @@ -171,7 +193,6 @@ public: void sendUp(); void sendToolType(int32_t toolType); void sendSync(); - void sendKey(int32_t scanCode, int32_t value); const Point getCenterPoint(); diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index f67c610550..f5b360f3b6 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -23,6 +23,7 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android-base/logging.h> #include <android/binder_manager.h> +#include <aidl/sensors/convert.h> using ::aidl::android::hardware::sensors::AdditionalInfo; using ::aidl::android::hardware::sensors::DynamicSensorInfo; @@ -34,469 +35,16 @@ using ::aidl::android::hardware::sensors::SensorType; using ::android::AidlMessageQueue; using ::android::hardware::EventFlag; using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT; +using ::android::hardware::sensors::implementation::convertToStatus; +using ::android::hardware::sensors::implementation::convertToSensor; +using ::android::hardware::sensors::implementation::convertToSensorEvent; +using ::android::hardware::sensors::implementation::convertFromSensorEvent; + namespace android { namespace { -status_t convertToStatus(ndk::ScopedAStatus status) { - if (status.isOk()) { - return OK; - } else { - switch (status.getExceptionCode()) { - case EX_ILLEGAL_ARGUMENT: { - return BAD_VALUE; - } - case EX_SECURITY: { - return PERMISSION_DENIED; - } - case EX_UNSUPPORTED_OPERATION: { - return INVALID_OPERATION; - } - case EX_SERVICE_SPECIFIC: { - switch (status.getServiceSpecificError()) { - case ISensors::ERROR_BAD_VALUE: { - return BAD_VALUE; - } - case ISensors::ERROR_NO_MEMORY: { - return NO_MEMORY; - } - default: { - return UNKNOWN_ERROR; - } - } - } - default: { - return UNKNOWN_ERROR; - } - } - } -} - -void convertToSensor(const SensorInfo &src, sensor_t *dst) { - dst->name = strdup(src.name.c_str()); - dst->vendor = strdup(src.vendor.c_str()); - dst->version = src.version; - dst->handle = src.sensorHandle; - dst->type = (int)src.type; - dst->maxRange = src.maxRange; - dst->resolution = src.resolution; - dst->power = src.power; - dst->minDelay = src.minDelayUs; - dst->fifoReservedEventCount = src.fifoReservedEventCount; - dst->fifoMaxEventCount = src.fifoMaxEventCount; - dst->stringType = strdup(src.typeAsString.c_str()); - dst->requiredPermission = strdup(src.requiredPermission.c_str()); - dst->maxDelay = src.maxDelayUs; - dst->flags = src.flags; - dst->reserved[0] = dst->reserved[1] = 0; -} - -void convertToSensorEvent(const Event &src, sensors_event_t *dst) { - *dst = {.version = sizeof(sensors_event_t), - .sensor = src.sensorHandle, - .type = (int32_t)src.sensorType, - .reserved0 = 0, - .timestamp = src.timestamp}; - - switch (src.sensorType) { - case SensorType::META_DATA: { - // Legacy HALs expect the handle reference in the meta data field. - // Copy it over from the handle of the event. - dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what; - dst->meta_data.sensor = src.sensorHandle; - // Set the sensor handle to 0 to maintain compatibility. - dst->sensor = 0; - break; - } - - case SensorType::ACCELEROMETER: - case SensorType::MAGNETIC_FIELD: - case SensorType::ORIENTATION: - case SensorType::GYROSCOPE: - case SensorType::GRAVITY: - case SensorType::LINEAR_ACCELERATION: { - dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x; - dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y; - dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z; - dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status; - break; - } - - case SensorType::GAME_ROTATION_VECTOR: { - dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x; - dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y; - dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z; - dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w; - break; - } - - case SensorType::ROTATION_VECTOR: - case SensorType::GEOMAGNETIC_ROTATION_VECTOR: { - dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0]; - dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1]; - dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2]; - dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3]; - dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4]; - break; - } - - case SensorType::MAGNETIC_FIELD_UNCALIBRATED: - case SensorType::GYROSCOPE_UNCALIBRATED: - case SensorType::ACCELEROMETER_UNCALIBRATED: { - dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x; - dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y; - dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z; - dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias; - dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias; - dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias; - break; - } - - case SensorType::HINGE_ANGLE: - case SensorType::DEVICE_ORIENTATION: - case SensorType::LIGHT: - case SensorType::PRESSURE: - case SensorType::PROXIMITY: - case SensorType::RELATIVE_HUMIDITY: - case SensorType::AMBIENT_TEMPERATURE: - case SensorType::SIGNIFICANT_MOTION: - case SensorType::STEP_DETECTOR: - case SensorType::TILT_DETECTOR: - case SensorType::WAKE_GESTURE: - case SensorType::GLANCE_GESTURE: - case SensorType::PICK_UP_GESTURE: - case SensorType::WRIST_TILT_GESTURE: - case SensorType::STATIONARY_DETECT: - case SensorType::MOTION_DETECT: - case SensorType::HEART_BEAT: - case SensorType::LOW_LATENCY_OFFBODY_DETECT: { - dst->data[0] = src.payload.get<Event::EventPayload::scalar>(); - break; - } - - case SensorType::STEP_COUNTER: { - dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>(); - break; - } - - case SensorType::HEART_RATE: { - dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm; - dst->heart_rate.status = - (int8_t)src.payload.get<Event::EventPayload::heartRate>().status; - break; - } - - case SensorType::POSE_6DOF: { // 15 floats - for (size_t i = 0; i < 15; ++i) { - dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i]; - } - break; - } - - case SensorType::DYNAMIC_SENSOR_META: { - dst->dynamic_sensor_meta.connected = - src.payload.get<Event::EventPayload::dynamic>().connected; - dst->dynamic_sensor_meta.handle = - src.payload.get<Event::EventPayload::dynamic>().sensorHandle; - dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later - - memcpy(dst->dynamic_sensor_meta.uuid, - src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16); - - break; - } - - case SensorType::ADDITIONAL_INFO: { - const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>(); - - additional_info_event_t *dstInfo = &dst->additional_info; - dstInfo->type = (int32_t)srcInfo.type; - dstInfo->serial = srcInfo.serial; - - switch (srcInfo.payload.getTag()) { - case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: { - const auto &values = - srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>() - .values; - CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32)); - memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32)); - break; - } - case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: { - const auto &values = - srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>() - .values; - CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float)); - memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float)); - break; - } - default: { - ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag()); - } - } - break; - } - - case SensorType::HEAD_TRACKER: { - const auto &ht = src.payload.get<Event::EventPayload::headTracker>(); - dst->head_tracker.rx = ht.rx; - dst->head_tracker.ry = ht.ry; - dst->head_tracker.rz = ht.rz; - dst->head_tracker.vx = ht.vx; - dst->head_tracker.vy = ht.vy; - dst->head_tracker.vz = ht.vz; - dst->head_tracker.discontinuity_count = ht.discontinuityCount; - break; - } - - case SensorType::ACCELEROMETER_LIMITED_AXES: - case SensorType::GYROSCOPE_LIMITED_AXES: - dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x; - dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y; - dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z; - dst->limited_axes_imu.x_supported = - src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported; - dst->limited_axes_imu.y_supported = - src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported; - dst->limited_axes_imu.z_supported = - src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported; - break; - - case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: - case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: - dst->limited_axes_imu_uncalibrated.x_uncalib = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x; - dst->limited_axes_imu_uncalibrated.y_uncalib = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y; - dst->limited_axes_imu_uncalibrated.z_uncalib = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z; - dst->limited_axes_imu_uncalibrated.x_bias = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias; - dst->limited_axes_imu_uncalibrated.y_bias = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias; - dst->limited_axes_imu_uncalibrated.z_bias = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias; - dst->limited_axes_imu_uncalibrated.x_supported = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported; - dst->limited_axes_imu_uncalibrated.y_supported = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported; - dst->limited_axes_imu_uncalibrated.z_supported = - src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported; - break; - - case SensorType::HEADING: - dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading; - dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy; - break; - - default: { - CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); - - memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(), - 16 * sizeof(float)); - break; - } - } -} - -void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { - *dst = { - .timestamp = src.timestamp, - .sensorHandle = src.sensor, - .sensorType = (SensorType)src.type, - }; - - switch (dst->sensorType) { - case SensorType::META_DATA: { - Event::EventPayload::MetaData meta; - meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what; - // Legacy HALs contain the handle reference in the meta data field. - // Copy that over to the handle of the event. In legacy HALs this - // field was expected to be 0. - dst->sensorHandle = src.meta_data.sensor; - dst->payload.set<Event::EventPayload::Tag::meta>(meta); - break; - } - - case SensorType::ACCELEROMETER: - case SensorType::MAGNETIC_FIELD: - case SensorType::ORIENTATION: - case SensorType::GYROSCOPE: - case SensorType::GRAVITY: - case SensorType::LINEAR_ACCELERATION: { - Event::EventPayload::Vec3 vec3; - vec3.x = src.acceleration.x; - vec3.y = src.acceleration.y; - vec3.z = src.acceleration.z; - vec3.status = (SensorStatus)src.acceleration.status; - dst->payload.set<Event::EventPayload::Tag::vec3>(vec3); - break; - } - - case SensorType::GAME_ROTATION_VECTOR: { - Event::EventPayload::Vec4 vec4; - vec4.x = src.data[0]; - vec4.y = src.data[1]; - vec4.z = src.data[2]; - vec4.w = src.data[3]; - dst->payload.set<Event::EventPayload::Tag::vec4>(vec4); - break; - } - - case SensorType::ROTATION_VECTOR: - case SensorType::GEOMAGNETIC_ROTATION_VECTOR: { - Event::EventPayload::Data data; - memcpy(data.values.data(), src.data, 5 * sizeof(float)); - dst->payload.set<Event::EventPayload::Tag::data>(data); - break; - } - - case SensorType::MAGNETIC_FIELD_UNCALIBRATED: - case SensorType::GYROSCOPE_UNCALIBRATED: - case SensorType::ACCELEROMETER_UNCALIBRATED: { - Event::EventPayload::Uncal uncal; - uncal.x = src.uncalibrated_gyro.x_uncalib; - uncal.y = src.uncalibrated_gyro.y_uncalib; - uncal.z = src.uncalibrated_gyro.z_uncalib; - uncal.xBias = src.uncalibrated_gyro.x_bias; - uncal.yBias = src.uncalibrated_gyro.y_bias; - uncal.zBias = src.uncalibrated_gyro.z_bias; - dst->payload.set<Event::EventPayload::Tag::uncal>(uncal); - break; - } - - case SensorType::DEVICE_ORIENTATION: - case SensorType::LIGHT: - case SensorType::PRESSURE: - case SensorType::PROXIMITY: - case SensorType::RELATIVE_HUMIDITY: - case SensorType::AMBIENT_TEMPERATURE: - case SensorType::SIGNIFICANT_MOTION: - case SensorType::STEP_DETECTOR: - case SensorType::TILT_DETECTOR: - case SensorType::WAKE_GESTURE: - case SensorType::GLANCE_GESTURE: - case SensorType::PICK_UP_GESTURE: - case SensorType::WRIST_TILT_GESTURE: - case SensorType::STATIONARY_DETECT: - case SensorType::MOTION_DETECT: - case SensorType::HEART_BEAT: - case SensorType::LOW_LATENCY_OFFBODY_DETECT: - case SensorType::HINGE_ANGLE: { - dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]); - break; - } - - case SensorType::STEP_COUNTER: { - dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter); - break; - } - - case SensorType::HEART_RATE: { - Event::EventPayload::HeartRate heartRate; - heartRate.bpm = src.heart_rate.bpm; - heartRate.status = (SensorStatus)src.heart_rate.status; - dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate); - break; - } - - case SensorType::POSE_6DOF: { // 15 floats - Event::EventPayload::Pose6Dof pose6DOF; - for (size_t i = 0; i < 15; ++i) { - pose6DOF.values[i] = src.data[i]; - } - dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF); - break; - } - - case SensorType::DYNAMIC_SENSOR_META: { - DynamicSensorInfo dynamic; - dynamic.connected = src.dynamic_sensor_meta.connected; - dynamic.sensorHandle = src.dynamic_sensor_meta.handle; - - memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16); - dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic); - break; - } - - case SensorType::ADDITIONAL_INFO: { - AdditionalInfo info; - const additional_info_event_t &srcInfo = src.additional_info; - info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type; - info.serial = srcInfo.serial; - - AdditionalInfo::AdditionalInfoPayload::Int32Values data; - CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32)); - memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32)); - info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data); - - dst->payload.set<Event::EventPayload::Tag::additional>(info); - break; - } - - case SensorType::HEAD_TRACKER: { - Event::EventPayload::HeadTracker headTracker; - headTracker.rx = src.head_tracker.rx; - headTracker.ry = src.head_tracker.ry; - headTracker.rz = src.head_tracker.rz; - headTracker.vx = src.head_tracker.vx; - headTracker.vy = src.head_tracker.vy; - headTracker.vz = src.head_tracker.vz; - headTracker.discontinuityCount = src.head_tracker.discontinuity_count; - - dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker); - break; - } - - case SensorType::ACCELEROMETER_LIMITED_AXES: - case SensorType::GYROSCOPE_LIMITED_AXES: { - Event::EventPayload::LimitedAxesImu limitedAxesImu; - limitedAxesImu.x = src.limited_axes_imu.x; - limitedAxesImu.y = src.limited_axes_imu.y; - limitedAxesImu.z = src.limited_axes_imu.z; - limitedAxesImu.xSupported = src.limited_axes_imu.x_supported; - limitedAxesImu.ySupported = src.limited_axes_imu.y_supported; - limitedAxesImu.zSupported = src.limited_axes_imu.z_supported; - dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu); - break; - } - - case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: - case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: { - Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal; - limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib; - limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib; - limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib; - limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias; - limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias; - limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias; - limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported; - limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported; - limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported; - dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal); - break; - } - - case SensorType::HEADING: { - Event::EventPayload::Heading heading; - heading.heading = src.heading.heading; - heading.accuracy = src.heading.accuracy; - dst->payload.set<Event::EventPayload::heading>(heading); - break; - } - - default: { - CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); - - Event::EventPayload::Data data; - memcpy(data.values.data(), src.data, 16 * sizeof(float)); - dst->payload.set<Event::EventPayload::Tag::data>(data); - break; - } - } -} - void serviceDied(void *cookie) { ALOGW("Sensors HAL died, attempting to reconnect."); ((AidlSensorHalWrapper *)cookie)->prepareForReconnect(); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 5ad4815a89..0eeb820b87 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -75,6 +75,7 @@ cc_library { static_libs: [ "libaidlcommonsupport", "android.hardware.sensors@1.0-convert", + "android.hardware.sensors-V1-convert", "android.hardware.sensors-V1-ndk", ], diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp index 46f00e8329..398cdf9a0e 100644 --- a/services/sensorservice/SensorInterface.cpp +++ b/services/sensorservice/SensorInterface.cpp @@ -87,6 +87,42 @@ VirtualSensor::VirtualSensor() : // --------------------------------------------------------------------------- +RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback) + : BaseSensor(sensor), mCallback(std::move(callback)) { +} + +status_t RuntimeSensor::activate(void*, bool enabled) { + if (enabled != mEnabled) { + mEnabled = enabled; + mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs); + } + return OK; +} + +status_t RuntimeSensor::batch(void*, int, int, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { + if (mSamplingPeriodNs != samplingPeriodNs || mBatchReportLatencyNs != maxBatchReportLatencyNs) { + mSamplingPeriodNs = samplingPeriodNs; + mBatchReportLatencyNs = maxBatchReportLatencyNs; + if (mEnabled) { + mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs); + } + } + return OK; +} + +status_t RuntimeSensor::setDelay(void*, int, int64_t ns) { + if (mSamplingPeriodNs != ns) { + mSamplingPeriodNs = ns; + if (mEnabled) { + mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs); + } + } + return OK; +} + +// --------------------------------------------------------------------------- + ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service) : HardwareSensor(sensor), mSensorService(service) { } diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h index 57043592c5..5ee5e1224a 100644 --- a/services/sensorservice/SensorInterface.h +++ b/services/sensorservice/SensorInterface.h @@ -104,6 +104,32 @@ protected: // --------------------------------------------------------------------------- +class RuntimeSensor : public BaseSensor { +public: + static constexpr int DEFAULT_DEVICE_ID = 0; + + class StateChangeCallback : public virtual RefBase { + public: + virtual void onStateChanged(bool enabled, int64_t samplingPeriodNs, + int64_t batchReportLatencyNs) = 0; + }; + RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback); + virtual status_t activate(void* ident, bool enabled) override; + virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) override; + virtual status_t setDelay(void* ident, int handle, int64_t ns) override; + virtual bool process(sensors_event_t*, const sensors_event_t&) { return false; } + virtual bool isVirtual() const override { return false; } + +private: + bool mEnabled = false; + int64_t mSamplingPeriodNs = 0; + int64_t mBatchReportLatencyNs = 0; + sp<StateChangeCallback> mCallback; +}; + +// --------------------------------------------------------------------------- + class ProximitySensor : public HardwareSensor { public: explicit ProximitySensor(const sensor_t& sensor, SensorService& service); diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp index 85ce0f0018..6d36b4789b 100644 --- a/services/sensorservice/SensorList.cpp +++ b/services/sensorservice/SensorList.cpp @@ -29,12 +29,12 @@ namespace SensorServiceUtil { const Sensor SensorList::mNonSensor = Sensor("unknown"); bool SensorList::add( - int handle, SensorInterface* si, bool isForDebug, bool isVirtual) { + int handle, SensorInterface* si, bool isForDebug, bool isVirtual, int deviceId) { std::lock_guard<std::mutex> lk(mLock); if (handle == si->getSensor().getHandle() && mUsedHandle.insert(handle).second) { // will succeed as the mUsedHandle does not have this handle - mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual)); + mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual, deviceId)); return true; } // handle exist already or handle mismatch @@ -79,7 +79,8 @@ const Vector<Sensor> SensorList::getUserSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) { + if (!e.isForDebug && !e.si->getSensor().isDynamicSensor() + && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { sensors.add(e.si->getSensor()); } return true; @@ -92,7 +93,8 @@ const Vector<Sensor> SensorList::getUserDebugSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (!e.si->getSensor().isDynamicSensor()) { + if (!e.si->getSensor().isDynamicSensor() + && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { sensors.add(e.si->getSensor()); } return true; @@ -105,7 +107,8 @@ const Vector<Sensor> SensorList::getDynamicSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) { + if (!e.isForDebug && e.si->getSensor().isDynamicSensor() + && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { sensors.add(e.si->getSensor()); } return true; @@ -118,7 +121,20 @@ const Vector<Sensor> SensorList::getVirtualSensors() const { Vector<Sensor> sensors; forEachEntry( [&sensors] (const Entry& e) -> bool { - if (e.isVirtual) { + if (e.isVirtual && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { + sensors.add(e.si->getSensor()); + } + return true; + }); + return sensors; +} + +const Vector<Sensor> SensorList::getRuntimeSensors(int deviceId) const { + // lock in forEachEntry + Vector<Sensor> sensors; + forEachEntry( + [&sensors, deviceId] (const Entry& e) -> bool { + if (!e.isForDebug && e.deviceId == deviceId) { sensors.add(e.si->getSensor()); } return true; diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h index 049ae7c855..79f6701922 100644 --- a/services/sensorservice/SensorList.h +++ b/services/sensorservice/SensorList.h @@ -40,14 +40,16 @@ public: sp<SensorInterface> si; const bool isForDebug; const bool isVirtual; - Entry(SensorInterface* si_, bool debug_, bool virtual_) : - si(si_), isForDebug(debug_), isVirtual(virtual_) { + const int deviceId; + Entry(SensorInterface* si_, bool debug_, bool virtual_, int deviceId_) : + si(si_), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) { } }; // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the // object it pointed to and the object should not be released elsewhere. - bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false); + bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false, + int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID); // After a handle is removed, the object that SensorInterface * pointing to may get deleted if // no more sp<> of the same object exist. @@ -60,6 +62,7 @@ public: const Vector<Sensor> getUserDebugSensors() const; const Vector<Sensor> getDynamicSensors() const; const Vector<Sensor> getVirtualSensors() const; + const Vector<Sensor> getRuntimeSensors(int deviceId) const; String8 getName(int handle) const; String8 getStringType(int handle) const; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 21d6b6b16f..0c9fef5652 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -102,6 +102,33 @@ static const String16 sDumpPermission("android.permission.DUMP"); static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE"); static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS"); +namespace { + +// TODO(b/259227294): Move the sensor ranges to the HAL. +int32_t nextRuntimeSensorHandle() { + static constexpr int32_t kRuntimeHandleBase = 0x5F000000; + static constexpr int32_t kRuntimeHandleEnd = 0x5FFFFFFF; + static int32_t nextHandle = kRuntimeHandleBase; + if (nextHandle == kRuntimeHandleEnd) { + return -1; + } + return nextHandle++; +} + +class RuntimeSensorCallbackProxy : public RuntimeSensor::StateChangeCallback { + public: + RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorStateChangeCallback> callback) + : mCallback(std::move(callback)) {} + void onStateChanged(bool enabled, int64_t samplingPeriodNs, + int64_t batchReportLatencyNs) override { + mCallback->onStateChanged(enabled, samplingPeriodNs, batchReportLatencyNs); + } + private: + sp<SensorService::RuntimeSensorStateChangeCallback> mCallback; +}; + +} // namespace + static bool isAutomotive() { sp<IServiceManager> serviceManager = defaultServiceManager(); if (serviceManager.get() == nullptr) { @@ -137,6 +164,60 @@ SensorService::SensorService() mMicSensorPrivacyPolicy = new MicrophonePrivacyPolicy(this); } +int SensorService::registerRuntimeSensor( + const sensor_t& sensor, int deviceId, sp<RuntimeSensorStateChangeCallback> callback) { + int handle = 0; + while (handle == 0 || !mSensors.isNewHandle(handle)) { + handle = nextRuntimeSensorHandle(); + if (handle < 0) { + // Ran out of the dedicated range for runtime sensors. + return handle; + } + } + + ALOGI("Registering runtime sensor handle 0x%x, type %d, name %s", + handle, sensor.type, sensor.name); + + sp<RuntimeSensor::StateChangeCallback> runtimeSensorCallback( + new RuntimeSensorCallbackProxy(std::move(callback))); + sensor_t runtimeSensor = sensor; + // force the handle to be consistent + runtimeSensor.handle = handle; + SensorInterface *si = new RuntimeSensor(runtimeSensor, std::move(runtimeSensorCallback)); + + Mutex::Autolock _l(mLock); + const Sensor& s = registerSensor(si, /* isDebug= */ false, /* isVirtual= */ false, deviceId); + + if (s.getHandle() != handle) { + // The registration was unsuccessful. + return s.getHandle(); + } + return handle; +} + +status_t SensorService::unregisterRuntimeSensor(int handle) { + ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle); + { + Mutex::Autolock _l(mLock); + if (!unregisterDynamicSensorLocked(handle)) { + ALOGE("Runtime sensor release error."); + return UNKNOWN_ERROR; + } + } + + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) { + connection->removeSensor(handle); + } + return OK; +} + +status_t SensorService::sendRuntimeSensorEvent(const sensors_event_t& event) { + Mutex::Autolock _l(mLock); + mRuntimeSensorEventQueue.push(event); + return OK; +} + bool SensorService::initializeHmacKey() { int fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_RDONLY|O_CLOEXEC); if (fd != -1) { @@ -407,10 +488,11 @@ bool SensorService::hasSensorAccessLocked(uid_t uid, const String16& opPackageNa && isUidActive(uid) && !isOperationRestrictedLocked(opPackageName); } -const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) { +const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual, + int deviceId) { int handle = s->getSensor().getHandle(); int type = s->getSensor().getType(); - if (mSensors.add(handle, s, isDebug, isVirtual)){ + if (mSensors.add(handle, s, isDebug, isVirtual, deviceId)) { mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type)); return s->getSensor(); } else { @@ -1003,6 +1085,7 @@ bool SensorService::threadLoop() { recordLastValueLocked(mSensorEventBuffer, count); // handle virtual sensors + bool bufferNeedsSorting = false; if (count && vcount) { sensors_event_t const * const event = mSensorEventBuffer; if (!mActiveVirtualSensors.empty()) { @@ -1038,12 +1121,37 @@ bool SensorService::threadLoop() { // record the last synthesized values recordLastValueLocked(&mSensorEventBuffer[count], k); count += k; - // sort the buffer by time-stamps - sortEventBuffer(mSensorEventBuffer, count); + bufferNeedsSorting = true; } } } + // handle runtime sensors + { + size_t k = 0; + while (!mRuntimeSensorEventQueue.empty()) { + if (count + k >= minBufferSize) { + ALOGE("buffer too small to hold all events: count=%zd, k=%zu, size=%zu", + count, k, minBufferSize); + break; + } + mSensorEventBuffer[count + k] = mRuntimeSensorEventQueue.front(); + mRuntimeSensorEventQueue.pop(); + k++; + } + if (k) { + // record the last synthesized values + recordLastValueLocked(&mSensorEventBuffer[count], k); + count += k; + bufferNeedsSorting = true; + } + } + + if (bufferNeedsSorting) { + // sort the buffer by time-stamps + sortEventBuffer(mSensorEventBuffer, count); + } + // handle backward compatibility for RotationVector sensor if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) { for (int i = 0; i < count; i++) { @@ -1342,19 +1450,37 @@ Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) { return accessibleSensorList; } +void SensorService::addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor, + Vector<Sensor>& accessibleSensorList) { + if (canAccessSensor(sensor, "can't see", opPackageName)) { + accessibleSensorList.add(sensor); + } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) { + ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32, + sensor.getName().string(), sensor.getRequiredPermission().string(), + sensor.getRequiredAppOp()); + } +} + Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) { Vector<Sensor> accessibleSensorList; mSensors.forEachSensor( [this, &opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool { if (sensor.isDynamicSensor()) { - if (canAccessSensor(sensor, "can't see", opPackageName)) { - accessibleSensorList.add(sensor); - } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) { - ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32, - sensor.getName().string(), - sensor.getRequiredPermission().string(), - sensor.getRequiredAppOp()); - } + addSensorIfAccessible(opPackageName, sensor, accessibleSensorList); + } + return true; + }); + makeUuidsIntoIdsForSensorList(accessibleSensorList); + return accessibleSensorList; +} + +Vector<Sensor> SensorService::getRuntimeSensorList(const String16& opPackageName, int deviceId) { + Vector<Sensor> accessibleSensorList; + mSensors.forEachEntry( + [this, &opPackageName, deviceId, &accessibleSensorList] ( + const SensorServiceUtil::SensorList::Entry& e) -> bool { + if (e.deviceId == deviceId) { + addSensorIfAccessible(opPackageName, e.si->getSensor(), accessibleSensorList); } return true; }); diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 4ba3c51985..e4903987bc 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -42,6 +42,7 @@ #include <stdint.h> #include <sys/types.h> +#include <queue> #include <unordered_map> #include <unordered_set> #include <vector> @@ -143,6 +144,14 @@ public: virtual void onProximityActive(bool isActive) = 0; }; + class RuntimeSensorStateChangeCallback : public virtual RefBase { + public: + // Note that the callback is invoked from an async thread and can interact with the + // SensorService directly. + virtual void onStateChanged(bool enabled, int64_t samplingPeriodNanos, + int64_t batchReportLatencyNanos) = 0; + }; + static char const* getServiceName() ANDROID_API { return "sensorservice"; } SensorService() ANDROID_API; @@ -169,6 +178,11 @@ public: status_t addProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API; status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API; + int registerRuntimeSensor(const sensor_t& sensor, int deviceId, + sp<RuntimeSensorStateChangeCallback> callback) ANDROID_API; + status_t unregisterRuntimeSensor(int handle) ANDROID_API; + status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API; + // Returns true if a sensor should be throttled according to our rate-throttling rules. static bool isSensorInCappedSet(int sensorType); @@ -346,6 +360,7 @@ private: // ISensorServer interface virtual Vector<Sensor> getSensorList(const String16& opPackageName); virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName); + virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId); virtual sp<ISensorEventConnection> createSensorEventConnection( const String8& packageName, int requestedMode, const String16& opPackageName, const String16& attributionTag); @@ -364,8 +379,9 @@ private: bool isWakeUpSensor(int type) const; void recordLastValueLocked(sensors_event_t const* buffer, size_t count); static void sortEventBuffer(sensors_event_t* buffer, size_t count); - const Sensor& registerSensor(SensorInterface* sensor, - bool isDebug = false, bool isVirtual = false); + const Sensor& registerSensor(SensorInterface* sensor, bool isDebug = false, + bool isVirtual = false, + int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID); const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false); const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false); bool unregisterDynamicSensorLocked(int handle); @@ -375,6 +391,8 @@ private: sensors_event_t const* buffer, const int count); bool canAccessSensor(const Sensor& sensor, const char* operation, const String16& opPackageName); + void addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor, + Vector<Sensor>& accessibleSensorList); static bool hasPermissionForSensor(const Sensor& sensor); static int getTargetSdkVersion(const String16& opPackageName); static void resetTargetSdkVersionCache(const String16& opPackageName); @@ -492,6 +510,7 @@ private: wp<const SensorEventConnection> * mMapFlushEventsToConnections; std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent; Mode mCurrentOperatingMode; + std::queue<sensors_event_t> mRuntimeSensorEventQueue; // true if the head tracker sensor type is currently restricted to system usage only // (can only be unrestricted for testing, via shell cmd) diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp new file mode 100644 index 0000000000..34d1de72f9 --- /dev/null +++ b/services/sensorservice/aidl/Android.bp @@ -0,0 +1,44 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library { + name: "libsensorserviceaidl", + srcs: [ + "EventQueue.cpp", + "DirectReportChannel.cpp", + "SensorManager.cpp", + "utils.cpp", + ], + host_supported: true, + cflags: [ + "-Wall", + "-Werror", + ], + header_libs: ["jni_headers"], + shared_libs: [ + "libbase", + "libutils", + "libcutils", + "libbinder_ndk", + "libsensor", + "android.frameworks.sensorservice-V1-ndk", + "android.hardware.sensors-V1-ndk", + ], + export_include_dirs: [ + "include/", + ], + static_libs: [ + "android.hardware.sensors-V1-convert", + ], + + export_header_lib_headers: ["jni_headers"], + local_include_dirs: [ + "include/sensorserviceaidl/", + ], +} diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp new file mode 100644 index 0000000000..cab53c17f5 --- /dev/null +++ b/services/sensorservice/aidl/DirectReportChannel.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 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 "DirectReportChannel.h" + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId) + : mManager(manager), mId(channelId) {} + +DirectReportChannel::~DirectReportChannel() { + mManager.destroyDirectChannel(mId); +} + +ndk::ScopedAStatus DirectReportChannel::configure( + int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate, + int32_t* _aidl_return) { + int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate)); + if (token <= 0) { + return ndk::ScopedAStatus::fromServiceSpecificError(token); + } + *_aidl_return = token; + return ndk::ScopedAStatus::ok(); +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/DirectReportChannel.h b/services/sensorservice/aidl/DirectReportChannel.h new file mode 100644 index 0000000000..d9ea73db9e --- /dev/null +++ b/services/sensorservice/aidl/DirectReportChannel.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 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 <aidl/android/frameworks/sensorservice/BnDirectReportChannel.h> +#include <aidl/android/hardware/sensors/ISensors.h> +#include <sensor/SensorManager.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +class DirectReportChannel final + : public ::aidl::android::frameworks::sensorservice::BnDirectReportChannel { +public: + DirectReportChannel(::android::SensorManager& manager, int channelId); + ~DirectReportChannel(); + + ndk::ScopedAStatus configure(int32_t sensorHandle, + ::aidl::android::hardware::sensors::ISensors::RateLevel rate, + int32_t* _aidl_return) override; + +private: + ::android::SensorManager& mManager; + const int mId; +}; + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp new file mode 100644 index 0000000000..c3947098c3 --- /dev/null +++ b/services/sensorservice/aidl/EventQueue.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 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 "EventQueue.h" +#include "utils.h" + +#include <android-base/logging.h> +#include <utils/Looper.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +using ::aidl::android::frameworks::sensorservice::IEventQueueCallback; +using ::aidl::android::hardware::sensors::Event; + +class EventQueueLooperCallback : public ::android::LooperCallback { +public: + EventQueueLooperCallback(sp<::android::SensorEventQueue> queue, + std::shared_ptr<IEventQueueCallback> callback) + : mQueue(queue), mCallback(callback) {} + + int handleEvent(int /* fd */, int /* events */, void* /* data */) { + ASensorEvent event; + ssize_t actual; + + auto internalQueue = mQueue.promote(); + if (internalQueue == nullptr) { + return 1; + } + + while ((actual = internalQueue->read(&event, 1)) > 0) { + internalQueue->sendAck(&event, actual); + ndk::ScopedAStatus ret = mCallback->onEvent(convertEvent(event)); + if (!ret.isOk()) { + LOG(ERROR) << "Failed to envoke EventQueueCallback: " << ret; + } + } + + return 1; // continue to receive callbacks + } + +private: + wp<::android::SensorEventQueue> mQueue; + std::shared_ptr<IEventQueueCallback> mCallback; +}; + +EventQueue::EventQueue(std::shared_ptr<IEventQueueCallback> callback, sp<::android::Looper> looper, + sp<::android::SensorEventQueue> internalQueue) + : mLooper(looper), mInternalQueue(internalQueue) { + mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, + new EventQueueLooperCallback(internalQueue, callback), nullptr); +} + +EventQueue::~EventQueue() { + mLooper->removeFd(mInternalQueue->getFd()); +} + +ndk::ScopedAStatus EventQueue::enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs, + int64_t in_maxBatchReportLatencyUs) { + return convertResult(mInternalQueue->enableSensor(in_sensorHandle, in_samplingPeriodUs, + in_maxBatchReportLatencyUs, 0)); +} + +ndk::ScopedAStatus EventQueue::disableSensor(int32_t in_sensorHandle) { + return convertResult(mInternalQueue->disableSensor(in_sensorHandle)); +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/EventQueue.h b/services/sensorservice/aidl/EventQueue.h new file mode 100644 index 0000000000..0ae1eba764 --- /dev/null +++ b/services/sensorservice/aidl/EventQueue.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 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 "SensorManagerAidl.h" + +#include <aidl/android/frameworks/sensorservice/BnEventQueue.h> +#include <sensor/SensorManager.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +struct EventQueue final : public aidl::android::frameworks::sensorservice::BnEventQueue { + EventQueue( + std::shared_ptr<aidl::android::frameworks::sensorservice::IEventQueueCallback> callback, + sp<::android::Looper> looper, sp<::android::SensorEventQueue> internalQueue); + ~EventQueue(); + + ndk::ScopedAStatus enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs, + int64_t in_maxBatchReportLatencyUs) override; + ndk::ScopedAStatus disableSensor(int32_t sensorHandle) override; + +private: + friend class EventQueueLooperCallback; + sp<::android::Looper> mLooper; + sp<::android::SensorEventQueue> mInternalQueue; +}; + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp new file mode 100644 index 0000000000..6d8d5741af --- /dev/null +++ b/services/sensorservice/aidl/SensorManager.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2022 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. + */ + +// LOG_TAG defined via build flag. +#ifndef LOG_TAG +#define LOG_TAG "AidlSensorManager" +#endif + +#include "DirectReportChannel.h" +#include "EventQueue.h" +#include "SensorManagerAidl.h" +#include "utils.h" + +#include <aidl/android/hardware/sensors/ISensors.h> +#include <android-base/logging.h> +#include <android/binder_ibinder.h> +#include <sched.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +using ::aidl::android::frameworks::sensorservice::IDirectReportChannel; +using ::aidl::android::frameworks::sensorservice::IEventQueue; +using ::aidl::android::frameworks::sensorservice::IEventQueueCallback; +using ::aidl::android::frameworks::sensorservice::ISensorManager; +using ::aidl::android::hardware::common::Ashmem; +using ::aidl::android::hardware::sensors::ISensors; +using ::aidl::android::hardware::sensors::SensorInfo; +using ::aidl::android::hardware::sensors::SensorType; +using ::android::frameworks::sensorservice::implementation::SensorManagerAidl; + +static const char* POLL_THREAD_NAME = "aidl_ssvc_poll"; + +SensorManagerAidl::SensorManagerAidl(JavaVM* vm) + : mLooper(new Looper(false)), mStopThread(true), mJavaVm(vm) {} +SensorManagerAidl::~SensorManagerAidl() { + // Stops pollAll inside the thread. + std::lock_guard<std::mutex> lock(mThreadMutex); + + mStopThread = true; + if (mLooper != nullptr) { + mLooper->wake(); + } + if (mPollThread.joinable()) { + mPollThread.join(); + } +} + +ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type, + const native_handle_t* handle, + std::shared_ptr<IDirectReportChannel>* chan) { + int channelId = manager.createDirectChannel(size, type, handle); + if (channelId < 0) { + return convertResult(channelId); + } + if (channelId == 0) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + *chan = ndk::SharedRefBase::make<DirectReportChannel>(manager, channelId); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus SensorManagerAidl::createAshmemDirectChannel( + const Ashmem& in_mem, int64_t in_size, + std::shared_ptr<IDirectReportChannel>* _aidl_return) { + if (in_size > in_mem.size || in_size < ISensors::DIRECT_REPORT_SENSOR_EVENT_TOTAL_LENGTH) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE); + } + native_handle_t* handle = native_handle_create(1, 0); + handle->data[0] = dup(in_mem.fd.get()); + + auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_ASHMEM, + handle, _aidl_return); + int result = native_handle_close(handle); + CHECK(result == 0) << "Failed to close the native_handle_t: " << result; + result = native_handle_delete(handle); + CHECK(result == 0) << "Failed to delete the native_handle_t: " << result; + + return status; +} + +ndk::ScopedAStatus SensorManagerAidl::createGrallocDirectChannel( + const ndk::ScopedFileDescriptor& in_mem, int64_t in_size, + std::shared_ptr<IDirectReportChannel>* _aidl_return) { + native_handle_t* handle = native_handle_create(1, 0); + handle->data[0] = dup(in_mem.get()); + + auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_GRALLOC, + handle, _aidl_return); + int result = native_handle_close(handle); + CHECK(result == 0) << "Failed to close the native_handle_t: " << result; + result = native_handle_delete(handle); + CHECK(result == 0) << "Failed to delete the native_handle_t: " << result; + + return status; +} + +ndk::ScopedAStatus SensorManagerAidl::createEventQueue( + const std::shared_ptr<IEventQueueCallback>& in_callback, + std::shared_ptr<IEventQueue>* _aidl_return) { + if (in_callback == nullptr) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE); + } + + sp<::android::Looper> looper = getLooper(); + if (looper == nullptr) { + LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue cannot initialize looper"; + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + + String8 package(String8::format("aidl_client_pid_%d", AIBinder_getCallingPid())); + sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue(package); + if (internalQueue == nullptr) { + LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue returns nullptr."; + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + + *_aidl_return = ndk::SharedRefBase::make<EventQueue>(in_callback, looper, internalQueue); + + return ndk::ScopedAStatus::ok(); +} + +SensorInfo convertSensor(Sensor src) { + SensorInfo dst; + dst.sensorHandle = src.getHandle(); + dst.name = src.getName(); + dst.vendor = src.getVendor(); + dst.version = src.getVersion(); + dst.type = static_cast<SensorType>(src.getType()); + dst.typeAsString = src.getStringType(); + // maxRange uses maxValue because ::android::Sensor wraps the + // internal sensor_t in this way. + dst.maxRange = src.getMaxValue(); + dst.resolution = src.getResolution(); + dst.power = src.getPowerUsage(); + dst.minDelayUs = src.getMinDelay(); + dst.fifoReservedEventCount = src.getFifoReservedEventCount(); + dst.fifoMaxEventCount = src.getFifoMaxEventCount(); + dst.requiredPermission = src.getRequiredPermission(); + dst.maxDelayUs = src.getMaxDelay(); + dst.flags = src.getFlags(); + return dst; +} + +ndk::ScopedAStatus SensorManagerAidl::getDefaultSensor(SensorType in_type, + SensorInfo* _aidl_return) { + ::android::Sensor const* sensor = + getInternalManager().getDefaultSensor(static_cast<int>(in_type)); + if (!sensor) { + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_NOT_EXIST); + } + *_aidl_return = convertSensor(*sensor); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus SensorManagerAidl::getSensorList(std::vector<SensorInfo>* _aidl_return) { + Sensor const* const* list; + _aidl_return->clear(); + ssize_t count = getInternalManager().getSensorList(&list); + if (count < 0 || list == nullptr) { + LOG(ERROR) << "SensorMAanger::getSensorList failed with count: " << count; + return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR); + } + _aidl_return->reserve(static_cast<size_t>(count)); + for (ssize_t i = 0; i < count; ++i) { + _aidl_return->push_back(convertSensor(*list[i])); + } + + return ndk::ScopedAStatus::ok(); +} + +::android::SensorManager& SensorManagerAidl::getInternalManager() { + std::lock_guard<std::mutex> lock(mInternalManagerMutex); + if (mInternalManager == nullptr) { + mInternalManager = &::android::SensorManager::getInstanceForPackage( + String16(ISensorManager::descriptor)); + } + return *mInternalManager; +} + +/* One global looper for all event queues created from this SensorManager. */ +sp<Looper> SensorManagerAidl::getLooper() { + std::lock_guard<std::mutex> lock(mThreadMutex); + + if (!mPollThread.joinable()) { + // if thread not initialized, start thread + mStopThread = false; + std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] { + struct sched_param p = {0}; + p.sched_priority = 10; + if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) { + LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno); + } + + // set looper + Looper::setForThread(looper); + + // Attach the thread to JavaVM so that pollAll do not crash if the thread + // eventually calls into Java. + JavaVMAttachArgs args{.version = JNI_VERSION_1_2, + .name = POLL_THREAD_NAME, + .group = nullptr}; + JNIEnv* env; + if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) { + LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM."; + } + + LOG(INFO) << POLL_THREAD_NAME << " started."; + for (;;) { + int pollResult = looper->pollAll(-1 /* timeout */); + if (pollResult == Looper::POLL_WAKE) { + if (stopThread == true) { + LOG(INFO) << POLL_THREAD_NAME << ": requested to stop"; + break; + } else { + LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work"; + } + } else { + LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected " + << pollResult; + break; + } + } + + if (javaVm->DetachCurrentThread() != JNI_OK) { + LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM."; + } + + LOG(INFO) << POLL_THREAD_NAME << " is terminated."; + }}; + mPollThread = std::move(pollThread); + } + return mLooper; +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp new file mode 100644 index 0000000000..0d6e476e70 --- /dev/null +++ b/services/sensorservice/aidl/fuzzer/Android.bp @@ -0,0 +1,52 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_fuzz { + name: "libsensorserviceaidl_fuzzer", + defaults: [ + "service_fuzzer_defaults", + ], + host_supported: true, + static_libs: [ + "libsensorserviceaidl", + "libpermission", + "android.frameworks.sensorservice-V1-ndk", + "android.hardware.sensors-V1-convert", + "android.hardware.sensors-V1-ndk", + "android.hardware.common-V2-ndk", + "libsensor", + "libfakeservicemanager", + "libcutils", + "liblog", + ], + srcs: [ + "fuzzer.cpp", + ], + fuzz_config: { + cc: [ + "android-sensors@google.com", + "devinmoore@google.com", + ], + }, + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + diag: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + }, + address: true, + integer_overflow: true, + }, + +} diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp new file mode 100644 index 0000000000..1b63d76953 --- /dev/null +++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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 <fuzzbinder/libbinder_ndk_driver.h> +#include <fuzzer/FuzzedDataProvider.h> + +#include <ServiceManager.h> +#include <android-base/logging.h> +#include <android/binder_interface_utils.h> +#include <fuzzbinder/random_binder.h> +#include <sensorserviceaidl/SensorManagerAidl.h> + +using android::fuzzService; +using android::frameworks::sensorservice::implementation::SensorManagerAidl; +using ndk::SharedRefBase; + +[[clang::no_destroy]] static std::once_flag gSmOnce; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager(); + std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); }); + fakeServiceManager->clear(); + + FuzzedDataProvider fdp(data, size); + android::sp<android::IBinder> binder = android::getRandomBinder(&fdp); + if (binder == nullptr) { + // Nothing to do if we get a null binder. It will cause SensorManager to + // hang while trying to get sensorservice. + return 0; + } + + CHECK(android::NO_ERROR == fakeServiceManager->addService(android::String16("sensorservice"), + binder)); + + std::shared_ptr<SensorManagerAidl> sensorService = + ndk::SharedRefBase::make<SensorManagerAidl>(nullptr); + + fuzzService(sensorService->asBinder().get(), std::move(fdp)); + + return 0; +} diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h new file mode 100644 index 0000000000..c77ee880dd --- /dev/null +++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 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 <aidl/android/frameworks/sensorservice/BnSensorManager.h> +#include <jni.h> +#include <sensor/SensorManager.h> +#include <utils/Looper.h> +#include <mutex> +#include <thread> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +class SensorManagerAidl : public ::aidl::android::frameworks::sensorservice::BnSensorManager { +public: + explicit SensorManagerAidl(JavaVM* vm); + ~SensorManagerAidl(); + + ::ndk::ScopedAStatus createAshmemDirectChannel( + const ::aidl::android::hardware::common::Ashmem& in_mem, int64_t in_size, + std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>* + _aidl_return) override; + ::ndk::ScopedAStatus createEventQueue( + const std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueueCallback>& + in_callback, + std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueue>* _aidl_return) + override; + ::ndk::ScopedAStatus createGrallocDirectChannel( + const ::ndk::ScopedFileDescriptor& in_buffer, int64_t in_size, + std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>* + _aidl_return) override; + ::ndk::ScopedAStatus getDefaultSensor( + ::aidl::android::hardware::sensors::SensorType in_type, + ::aidl::android::hardware::sensors::SensorInfo* _aidl_return) override; + ::ndk::ScopedAStatus getSensorList( + std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override; + +private: + // Block until ::android::SensorManager is initialized. + ::android::SensorManager& getInternalManager(); + sp<Looper> getLooper(); + + std::mutex mInternalManagerMutex; + ::android::SensorManager* mInternalManager = nullptr; // does not own + sp<Looper> mLooper; + + volatile bool mStopThread; + std::mutex mThreadMutex; // protects mPollThread + std::thread mPollThread; + + JavaVM* mJavaVm; +}; + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp new file mode 100644 index 0000000000..26bcdc5b24 --- /dev/null +++ b/services/sensorservice/aidl/utils.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 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 "utils.h" + +#include <aidl/android/frameworks/sensorservice/ISensorManager.h> +#include <aidl/sensors/convert.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +ndk::ScopedAStatus convertResult(status_t src) { + using ::aidl::android::frameworks::sensorservice::ISensorManager; + + int err = 0; + switch (src) { + case OK: + return ndk::ScopedAStatus::ok(); + case NAME_NOT_FOUND: + err = ISensorManager::RESULT_NOT_EXIST; + break; + case NO_MEMORY: + err = ISensorManager::RESULT_NO_MEMORY; + break; + case NO_INIT: + err = ISensorManager::RESULT_NO_INIT; + break; + case PERMISSION_DENIED: + err = ISensorManager::RESULT_PERMISSION_DENIED; + break; + case BAD_VALUE: + err = ISensorManager::RESULT_BAD_VALUE; + break; + case INVALID_OPERATION: + err = ISensorManager::RESULT_INVALID_OPERATION; + break; + default: + err = ISensorManager::RESULT_UNKNOWN_ERROR; + } + return ndk::ScopedAStatus::fromServiceSpecificError(err); +} + +::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) { + ::aidl::android::hardware::sensors::Event dst; + ::android::hardware::sensors::implementation:: + convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst); + return dst; +} + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/sensorservice/aidl/utils.h b/services/sensorservice/aidl/utils.h new file mode 100644 index 0000000000..06ba59e5aa --- /dev/null +++ b/services/sensorservice/aidl/utils.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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 <aidl/android/hardware/sensors/Event.h> +#include <sensor/Sensor.h> + +namespace android { +namespace frameworks { +namespace sensorservice { +namespace implementation { + +::ndk::ScopedAStatus convertResult(status_t status); +::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& event); + +} // namespace implementation +} // namespace sensorservice +} // namespace frameworks +} // namespace android diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 14fdd126dd..b1bd705f19 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -157,6 +157,8 @@ filegroup { "EventLog/EventLog.cpp", "FrontEnd/LayerCreationArgs.cpp", "FrontEnd/LayerHandle.cpp", + "FrontEnd/LayerLifecycleManager.cpp", + "FrontEnd/RequestedLayerState.cpp", "FrontEnd/TransactionHandler.cpp", "FlagManager.cpp", "FpsReporter.cpp", diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index c1460cfc95..30d34a581b 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -140,6 +140,11 @@ cc_test { "libgmock", "libgtest", ], + // For some reason, libvulkan isn't picked up from librenderengine + // Probably ASAN related? + shared_libs: [ + "libvulkan", + ], sanitize: { // By using the address sanitizer, we not only uncover any issues // with the test, but also any issues with the code under test. diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 758b346a33..33caa7a9b8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -139,8 +139,8 @@ public: MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), (const, override)); MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); - MOCK_METHOD(status_t, getOverlaySupport, - (aidl::android::hardware::graphics::composer3::OverlayProperties*)); + MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, + getOverlaySupport, (), (const, override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index e220541461..c555b39db1 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -34,7 +34,7 @@ public: MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected), (override)); MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override)); - MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override)); MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h index ac25fe08a5..d07cdf55d2 100644 --- a/services/surfaceflinger/Display/DisplayModeRequest.h +++ b/services/surfaceflinger/Display/DisplayModeRequest.h @@ -18,19 +18,19 @@ #include <ftl/non_null.h> -#include "DisplayHardware/DisplayMode.h" +#include <scheduler/FrameRateMode.h> namespace android::display { struct DisplayModeRequest { - ftl::NonNull<DisplayModePtr> modePtr; + scheduler::FrameRateMode mode; // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE. bool emitEvent = false; }; inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) { - return lhs.modePtr == rhs.modePtr && lhs.emitEvent == rhs.emitEvent; + return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent; } } // namespace android::display diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 9868c8ead8..269a5ea561 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -41,6 +41,7 @@ #include "Display/DisplaySnapshot.h" #include "DisplayDevice.h" +#include "FrontEnd/DisplayInfo.h" #include "Layer.h" #include "RefreshRateOverlay.h" #include "SurfaceFlinger.h" @@ -67,6 +68,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mCompositionDisplay{args.compositionDisplay}, mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())), mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())), + mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())), mPhysicalOrientation(args.physicalOrientation), mIsPrimary(args.isPrimary), mRefreshRateSelector(std::move(args.refreshRateSelector)) { @@ -103,7 +105,9 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mCompositionDisplay->getRenderSurface()->initialize(); - if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value()); + if (const auto powerModeOpt = args.initialPowerMode) { + setPowerMode(*powerModeOpt); + } // initialize the display orientation transform. setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT); @@ -131,7 +135,7 @@ void DisplayDevice::setDisplayName(const std::string& displayName) { } } -auto DisplayDevice::getInputInfo() const -> InputInfo { +auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo { gui::DisplayInfo info; info.displayId = getLayerStack().id; @@ -160,7 +164,9 @@ auto DisplayDevice::getInputInfo() const -> InputInfo { return {.info = info, .transform = displayTransform, .receivesInput = receivesInput(), - .isSecure = isSecure()}; + .isSecure = isSecure(), + .isPrimary = isPrimary(), + .rotationFlags = ui::Transform::toRotationFlags(mOrientation)}; } void DisplayDevice::setPowerMode(hal::PowerMode mode) { @@ -175,8 +181,7 @@ void DisplayDevice::setPowerMode(hal::PowerMode mode) { mPowerMode = mode; - getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() && - *mPowerMode != hal::PowerMode::OFF); + getCompositionDisplay()->setCompositionEnabled(isPoweredOn()); } void DisplayDevice::enableLayerCaching(bool enable) { @@ -191,35 +196,32 @@ bool DisplayDevice::isPoweredOn() const { return mPowerMode && *mPowerMode != hal::PowerMode::OFF; } -void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) { - const auto fpsOpt = snapshot.displayModes().get(modeId).transform( - [](const DisplayModePtr& mode) { return mode->getFps(); }); - - LOG_ALWAYS_FATAL_IF(!fpsOpt, "Unknown mode"); - const Fps fps = *fpsOpt; - - ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue()); +void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) { + ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue()); + ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue()); - mRefreshRateSelector->setActiveModeId(modeId); + mRefreshRateSelector->setActiveMode(modeId, renderFps); if (mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(fps); + mRefreshRateOverlay->changeRefreshRate(displayFps); } } status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline* outTimeline) { - if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) { + if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) { ALOGE("Trying to initiate a mode change to invalid mode %s on display %s", - info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null", + info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str() + : "null", to_string(getId()).c_str()); return BAD_VALUE; } mUpcomingActiveMode = info; - ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue()); - return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(), - constraints, outTimeline); + ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue()); + return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), + info.modeOpt->modePtr->getHwcId(), constraints, + outTimeline); } nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { @@ -234,7 +236,7 @@ nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { return vsyncPeriod; } - return refreshRateSelector().getActiveModePtr()->getVsyncPeriod(); + return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod(); } ui::Dataspace DisplayDevice::getCompositionDataSpace() const { @@ -418,7 +420,7 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) { mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner); mRefreshRateOverlay->setLayerStack(getLayerStack()); mRefreshRateOverlay->setViewport(getSize()); - mRefreshRateOverlay->changeRefreshRate(getActiveMode().getFps()); + mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps()); } bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId, @@ -441,13 +443,14 @@ void DisplayDevice::animateRefreshRateOverlay() { } } -bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { +auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction { ATRACE_CALL(); - LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided"); - LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch"); + LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided"); + LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(), + "DisplayId mismatch"); - ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str()); + ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str()); std::scoped_lock lock(mActiveModeLock); if (mDesiredActiveModeChanged) { @@ -455,18 +458,25 @@ bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { const auto prevConfig = mDesiredActiveMode.event; mDesiredActiveMode = info; mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig; - return false; + return DesiredActiveModeAction::None; } + const auto& desiredMode = *info.modeOpt->modePtr; + // Check if we are already at the desired mode - if (refreshRateSelector().getActiveModePtr()->getId() == info.mode->getId()) { - return false; + if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) { + if (refreshRateSelector().getActiveMode() == info.modeOpt) { + return DesiredActiveModeAction::None; + } + + setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps); + return DesiredActiveModeAction::InitiateRenderRateSwitch; } // Initiate a mode change. mDesiredActiveModeChanged = true; mDesiredActiveMode = info; - return true; + return DesiredActiveModeAction::InitiateDisplayModeSwitch; } std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 1602a71709..852a8a297d 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -45,11 +45,11 @@ #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/Hal.h" #include "DisplayHardware/PowerAdvisor.h" +#include "FrontEnd/DisplayInfo.h" #include "Scheduler/RefreshRateSelector.h" #include "ThreadContext.h" #include "TracedOrdinal.h" #include "Utils/Dumper.h" - namespace android { class Fence; @@ -167,14 +167,7 @@ public: void setDisplayName(const std::string& displayName); const std::string& getDisplayName() const { return mDisplayName; } - struct InputInfo { - gui::DisplayInfo info; - ui::Transform transform; - bool receivesInput; - bool isSecure; - }; - - InputInfo getInputInfo() const; + surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const; /* ------------------------------------------------------------------------ * Display power mode management. @@ -197,33 +190,38 @@ public: using Event = scheduler::DisplayModeEvent; ActiveModeInfo() = default; - ActiveModeInfo(DisplayModePtr mode, Event event) : mode(std::move(mode)), event(event) {} + ActiveModeInfo(scheduler::FrameRateMode mode, Event event) + : modeOpt(std::move(mode)), event(event) {} explicit ActiveModeInfo(display::DisplayModeRequest&& request) - : ActiveModeInfo(std::move(request.modePtr).take(), + : ActiveModeInfo(std::move(request.mode), request.emitEvent ? Event::Changed : Event::None) {} - DisplayModePtr mode; + ftl::Optional<scheduler::FrameRateMode> modeOpt; Event event = Event::None; bool operator!=(const ActiveModeInfo& other) const { - return mode != other.mode || event != other.event; + return modeOpt != other.modeOpt || event != other.event; } }; - bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock); + enum class DesiredActiveModeAction { + None, + InitiateDisplayModeSwitch, + InitiateRenderRateSwitch + }; + DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock); std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock); void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock); ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) { return mUpcomingActiveMode; } - const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) { + scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) { return mRefreshRateSelector->getActiveMode(); } - // Precondition: DisplaySnapshot must contain a mode with DisplayModeId. - void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext); + void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps); status_t initiateModeChange(const ActiveModeInfo&, const hal::VsyncPeriodChangeConstraints& constraints, @@ -261,6 +259,7 @@ private: std::string mDisplayName; std::string mActiveModeFPSTrace; std::string mActiveModeFPSHwcTrace; + std::string mRenderFrameRateFPSTrace; const ui::Rotation mPhysicalOrientation; ui::Rotation mOrientation = ui::ROTATION_0; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index eff5130f5a..3782c6c236 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -542,8 +542,12 @@ Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp return Error::NONE; } -Error AidlComposer::getOverlaySupport(AidlOverlayProperties* /*outProperties*/) { - // TODO(b/242588489): implement details +Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) { + const auto status = mAidlComposerClient->getOverlaySupport(outProperties); + if (!status.isOk()) { + ALOGE("getOverlaySupport failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } return Error::NONE; } diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index a9337d8d88..e264570a87 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -334,9 +334,9 @@ Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) const return Error::NONE; } -Error Display::getOverlaySupport(OverlayProperties* /*outProperties*/) const { - // TODO(b/242588489): implement details - return Error::NONE; +Error Display::getOverlaySupport(OverlayProperties* outProperties) const { + auto intError = mComposer.getOverlaySupport(outProperties); + return static_cast<Error>(intError); } Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat, diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 5f11cb8e30..10fde2af8e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -96,6 +96,7 @@ HWComposer::~HWComposer() { void HWComposer::setCallback(HWC2::ComposerCallback& callback) { loadCapabilities(); loadLayerMetadataSupport(); + loadOverlayProperties(); if (mRegisteredCallback) { ALOGW("Callback already registered. Ignored extra registration attempt."); @@ -652,9 +653,9 @@ status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* return NO_ERROR; } -status_t HWComposer::getOverlaySupport( - aidl::android::hardware::graphics::composer3::OverlayProperties* /*outProperties*/) { - return NO_ERROR; +const aidl::android::hardware::graphics::composer3::OverlayProperties& +HWComposer::getOverlaySupport() const { + return mOverlayProperties; } int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const { @@ -974,6 +975,10 @@ void HWComposer::loadCapabilities() { } } +void HWComposer::loadOverlayProperties() { + mComposer->getOverlaySupport(&mOverlayProperties); +} + status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId, std::chrono::milliseconds timeout) { ATRACE_CALL(); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 8235b881d6..78d4a68617 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -179,8 +179,8 @@ public: // Fetches the HDR capabilities of the given display virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0; - virtual status_t getOverlaySupport( - aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties) = 0; + virtual const aidl::android::hardware::graphics::composer3::OverlayProperties& + getOverlaySupport() const = 0; virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0; @@ -366,8 +366,8 @@ public: // Fetches the HDR capabilities of the given display status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override; - status_t getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties* - outProperties) override; + const aidl::android::hardware::graphics::composer3::OverlayProperties& getOverlaySupport() + const override; int32_t getSupportedPerFrameMetadata(HalDisplayId) const override; @@ -489,11 +489,13 @@ private: void loadCapabilities(); void loadLayerMetadataSupport(); + void loadOverlayProperties(); std::unordered_map<HalDisplayId, DisplayData> mDisplayData; std::unique_ptr<android::Hwc2::Composer> mComposer; std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities; + aidl::android::hardware::graphics::composer3::OverlayProperties mOverlayProperties; std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata; bool mRegisteredCallback = false; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index cb2c8c53ef..f05223cce0 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -57,6 +57,7 @@ using android::hardware::power::Boost; using android::hardware::power::IPower; using android::hardware::power::IPowerHintSession; using android::hardware::power::Mode; +using android::hardware::power::SessionHint; using android::hardware::power::WorkDuration; PowerAdvisor::~PowerAdvisor() = default; @@ -140,7 +141,7 @@ void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expec } } -void PowerAdvisor::notifyDisplayUpdateImminent() { +void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() { // Only start sending this notification once the system has booted so we don't introduce an // early-boot dependency on Power HAL if (!mBootFinished.load()) { @@ -154,7 +155,7 @@ void PowerAdvisor::notifyDisplayUpdateImminent() { return; } - if (!halWrapper->notifyDisplayUpdateImminent()) { + if (!halWrapper->notifyDisplayUpdateImminentAndCpuReset()) { // The HAL has become unavailable; attempt to reconnect later mReconnectPowerHal = true; return; @@ -599,7 +600,7 @@ public: return ret.isOk(); } - bool notifyDisplayUpdateImminent() override { + bool notifyDisplayUpdateImminentAndCpuReset() override { // Power HAL 1.x doesn't have a notification for this ALOGV("HIDL notifyUpdateImminent received but can't send"); return true; @@ -675,8 +676,12 @@ bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) { return ret.isOk(); } -bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { - ALOGV("AIDL notifyDisplayUpdateImminent"); +bool AidlPowerHalWrapper::notifyDisplayUpdateImminentAndCpuReset() { + ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset"); + if (isPowerHintSessionRunning()) { + mPowerHintSession->sendHint(SessionHint::CPU_LOAD_RESET); + } + if (!mHasDisplayUpdateImminent) { ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); return true; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 1c9d123a1f..d45e7cb572 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -48,7 +48,7 @@ public: virtual void onBootFinished() = 0; virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0; virtual bool isUsingExpensiveRendering() = 0; - virtual void notifyDisplayUpdateImminent() = 0; + virtual void notifyDisplayUpdateImminentAndCpuReset() = 0; // Checks both if it supports and if it's enabled virtual bool usePowerHintSession() = 0; virtual bool supportsPowerHintSession() = 0; @@ -106,7 +106,7 @@ public: virtual ~HalWrapper() = default; virtual bool setExpensiveRendering(bool enabled) = 0; - virtual bool notifyDisplayUpdateImminent() = 0; + virtual bool notifyDisplayUpdateImminentAndCpuReset() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; virtual void restartPowerHintSession() = 0; @@ -126,7 +126,7 @@ public: void onBootFinished() override; void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override; bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }; - void notifyDisplayUpdateImminent() override; + void notifyDisplayUpdateImminentAndCpuReset() override; bool usePowerHintSession() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; @@ -289,7 +289,7 @@ public: static std::unique_ptr<HalWrapper> connect(); bool setExpensiveRendering(bool enabled) override; - bool notifyDisplayUpdateImminent() override; + bool notifyDisplayUpdateImminentAndCpuReset() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; void restartPowerHintSession() override; diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h new file mode 100644 index 0000000000..0c7b24a2c4 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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 <gui/DisplayInfo.h> + +namespace android::surfaceflinger::frontend { + +// Display information needed to populate input and calculate layer geometry. +struct DisplayInfo { + gui::DisplayInfo info; + ui::Transform transform; + bool receivesInput; + bool isSecure; + // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed. + bool isPrimary; + ui::Transform::RotationFlags rotationFlags; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp new file mode 100644 index 0000000000..7afa1444df --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp @@ -0,0 +1,321 @@ +/* + * Copyright 2022 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#undef LOG_TAG +#define LOG_TAG "LayerLifecycleManager" + +#include "LayerLifecycleManager.h" +#include "Layer.h" // temporarily needed for LayerHandle +#include "LayerHandle.h" +#include "SwapErase.h" + +namespace android::surfaceflinger::frontend { + +using namespace ftl::flag_operators; + +void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) { + if (newLayers.empty()) { + return; + } + + mGlobalChanges |= RequestedLayerState::Changes::Hierarchy; + for (auto& newLayer : newLayers) { + RequestedLayerState& layer = *newLayer.get(); + auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer}); + if (!inserted) { + LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id, + it->second.owner.getDebugString().c_str()); + } + + linkLayer(layer.parentId, layer.id); + linkLayer(layer.relativeParentId, layer.id); + linkLayer(layer.mirrorId, layer.id); + linkLayer(layer.touchCropId, layer.id); + + mLayers.emplace_back(std::move(newLayer)); + } +} + +void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles) { + std::vector<uint32_t> layersToBeDestroyed; + for (const auto& layerId : destroyedHandles) { + auto it = mIdToLayer.find(layerId); + if (it == mIdToLayer.end()) { + LOG_ALWAYS_FATAL("%s Layerid not found %d", __func__, layerId); + continue; + } + RequestedLayerState& layer = it->second.owner; + layer.handleAlive = false; + if (!layer.canBeDestroyed()) { + continue; + } + layer.changes |= RequestedLayerState::Changes::Destroyed; + layersToBeDestroyed.emplace_back(layerId); + } + + if (layersToBeDestroyed.empty()) { + return; + } + + mGlobalChanges |= RequestedLayerState::Changes::Hierarchy; + for (size_t i = 0; i < layersToBeDestroyed.size(); i++) { + uint32_t layerId = layersToBeDestroyed[i]; + auto it = mIdToLayer.find(layerId); + if (it == mIdToLayer.end()) { + LOG_ALWAYS_FATAL("%s Layer with id %d not found", __func__, layerId); + continue; + } + + RequestedLayerState& layer = it->second.owner; + + unlinkLayer(layer.parentId, layer.id); + unlinkLayer(layer.relativeParentId, layer.id); + unlinkLayer(layer.mirrorId, layer.id); + unlinkLayer(layer.touchCropId, layer.id); + + auto& references = it->second.references; + for (uint32_t linkedLayerId : references) { + RequestedLayerState* linkedLayer = getLayerFromId(linkedLayerId); + if (!linkedLayer) { + LOG_ALWAYS_FATAL("%s Layerid reference %d not found for %d", __func__, + linkedLayerId, layer.id); + continue; + }; + if (linkedLayer->parentId == layer.id) { + linkedLayer->parentId = UNASSIGNED_LAYER_ID; + if (linkedLayer->canBeDestroyed()) { + linkedLayer->changes |= RequestedLayerState::Changes::Destroyed; + layersToBeDestroyed.emplace_back(linkedLayer->id); + } + } + if (linkedLayer->relativeParentId == layer.id) { + linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID; + } + if (linkedLayer->mirrorId == layer.id) { + linkedLayer->mirrorId = UNASSIGNED_LAYER_ID; + } + if (linkedLayer->touchCropId == layer.id) { + linkedLayer->touchCropId = UNASSIGNED_LAYER_ID; + } + } + mIdToLayer.erase(it); + } + + auto it = mLayers.begin(); + while (it != mLayers.end()) { + RequestedLayerState* layer = it->get(); + if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) { + ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str()); + std::iter_swap(it, mLayers.end() - 1); + mDestroyedLayers.emplace_back(std::move(mLayers.back())); + if (it == mLayers.end() - 1) { + it = mLayers.erase(mLayers.end() - 1); + } else { + mLayers.erase(mLayers.end() - 1); + } + } else { + it++; + } + } +} + +void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions) { + for (const auto& transaction : transactions) { + for (const auto& resolvedComposerState : transaction.states) { + const auto& clientState = resolvedComposerState.state; + uint32_t layerId = LayerHandle::getLayerId(clientState.surface); + if (layerId == UNASSIGNED_LAYER_ID) { + ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get()); + continue; + } + + RequestedLayerState* layer = getLayerFromId(layerId); + if (layer == nullptr) { + LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__, + clientState.surface.get(), layerId); + continue; + } + + if (!layer->handleAlive) { + LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of " + "order LayerLifecycleManager updates", + __func__, clientState.surface.get(), layerId); + continue; + } + + uint32_t oldParentId = layer->parentId; + uint32_t oldRelativeParentId = layer->relativeParentId; + uint32_t oldTouchCropId = layer->touchCropId; + layer->merge(resolvedComposerState); + + if (layer->what & layer_state_t::eBackgroundColorChanged) { + if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColorAlpha != 0) { + LayerCreationArgs backgroundLayerArgs{nullptr, + nullptr, + layer->name + "BackgroundColorLayer", + ISurfaceComposerClient::eFXSurfaceEffect, + {}}; + std::vector<std::unique_ptr<RequestedLayerState>> newLayers; + newLayers.emplace_back( + std::make_unique<RequestedLayerState>(backgroundLayerArgs)); + RequestedLayerState* backgroundLayer = newLayers.back().get(); + backgroundLayer->handleAlive = false; + backgroundLayer->parentId = layer->id; + backgroundLayer->z = std::numeric_limits<int32_t>::min(); + backgroundLayer->color.rgb = layer->color.rgb; + backgroundLayer->color.a = layer->bgColorAlpha; + backgroundLayer->dataspace = layer->bgColorDataspace; + + layer->bgColorLayerId = backgroundLayer->id; + addLayers({std::move(newLayers)}); + } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && + layer->bgColorAlpha == 0) { + RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId); + bgColorLayer->parentId = UNASSIGNED_LAYER_ID; + onHandlesDestroyed({layer->bgColorLayerId}); + } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) { + RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId); + bgColorLayer->color.rgb = layer->color.rgb; + bgColorLayer->color.a = layer->bgColorAlpha; + bgColorLayer->dataspace = layer->bgColorDataspace; + mGlobalChanges |= RequestedLayerState::Changes::Content; + } + } + + if (oldParentId != layer->parentId) { + unlinkLayer(oldParentId, layer->id); + linkLayer(layer->parentId, layer->id); + } + if (oldRelativeParentId != layer->relativeParentId) { + unlinkLayer(oldRelativeParentId, layer->id); + linkLayer(layer->relativeParentId, layer->id); + } + if (oldTouchCropId != layer->touchCropId) { + unlinkLayer(oldTouchCropId, layer->id); + linkLayer(layer->touchCropId, layer->id); + } + + mGlobalChanges |= layer->changes & + (RequestedLayerState::Changes::Hierarchy | + RequestedLayerState::Changes::Geometry | + RequestedLayerState::Changes::Content); + } + } +} + +void LayerLifecycleManager::commitChanges() { + for (auto& layer : mLayers) { + if (layer->changes.test(RequestedLayerState::Changes::Created)) { + for (auto listener : mListeners) { + listener->onLayerAdded(*layer); + } + } + layer->what = 0; + layer->changes.clear(); + } + + for (auto& destroyedLayer : mDestroyedLayers) { + if (destroyedLayer->changes.test(RequestedLayerState::Changes::Created)) { + for (auto listener : mListeners) { + listener->onLayerAdded(*destroyedLayer); + } + } + + for (auto listener : mListeners) { + listener->onLayerDestroyed(*destroyedLayer); + } + } + mDestroyedLayers.clear(); + mGlobalChanges.clear(); +} + +void LayerLifecycleManager::addLifecycleListener(std::shared_ptr<ILifecycleListener> listener) { + mListeners.emplace_back(std::move(listener)); +} + +void LayerLifecycleManager::removeLifecycleListener(std::shared_ptr<ILifecycleListener> listener) { + swapErase(mListeners, listener); +} + +const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getLayers() const { + return mLayers; +} + +const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getDestroyedLayers() + const { + return mDestroyedLayers; +} + +const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const { + return mGlobalChanges; +} + +RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) { + if (id == UNASSIGNED_LAYER_ID) { + return nullptr; + } + auto it = mIdToLayer.find(id); + if (it == mIdToLayer.end()) { + return nullptr; + } + return &it->second.owner; +} + +std::vector<uint32_t>* LayerLifecycleManager::getLinkedLayersFromId(uint32_t id) { + if (id == UNASSIGNED_LAYER_ID) { + return nullptr; + } + auto it = mIdToLayer.find(id); + if (it == mIdToLayer.end()) { + return nullptr; + } + return &it->second.references; +} + +void LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) { + if (layerToLink && layerId != UNASSIGNED_LAYER_ID) { + std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId); + if (!linkedLayers) { + LOG_ALWAYS_FATAL("Could not find layer id %d to link %d", layerId, layerToLink); + return; + } + linkedLayers->emplace_back(layerToLink); + } +} + +void LayerLifecycleManager::unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer) { + uint32_t layerId = inOutLayerId; + inOutLayerId = UNASSIGNED_LAYER_ID; + + std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId); + if (!linkedLayers) { + return; + } + swapErase(*linkedLayers, linkedLayer); +} + +std::string LayerLifecycleManager::References::getDebugString() const { + std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:"; + std::for_each(references.begin(), references.end(), + [&debugInfo = debugInfo](const uint32_t& reference) mutable { + debugInfo += std::to_string(reference) + ","; + }); + return debugInfo; +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h new file mode 100644 index 0000000000..ad70d3f8e9 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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 "RequestedLayerState.h" +#include "TransactionState.h" + +namespace android::surfaceflinger::frontend { + +// Owns a collection of RequestedLayerStates and manages their lifecycle +// and state changes. +// +// RequestedLayerStates are tracked and destroyed if they have no parent and +// no handle left to keep them alive. The handle does not keep a reference to +// the RequestedLayerState but a layer id associated with the RequestedLayerState. +// If the handle is destroyed and the RequestedLayerState does not have a parent, +// the LayerLifecycleManager destroys the RequestedLayerState. +// +// Threading: This class is not thread safe, it requires external synchronization. +// +// Typical usage: Input states (new layers, transactions, destroyed layer handles) +// are collected in the background passed into the LayerLifecycleManager to update +// layer lifecycle and layer state at start of composition. +class LayerLifecycleManager { +public: + // External state changes should be updated in the following order: + void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>); + void applyTransactions(const std::vector<TransactionState>&); + void onHandlesDestroyed(const std::vector<uint32_t>&); + + // Destroys RequestedLayerStates that are marked to be destroyed. Invokes all + // ILifecycleListener callbacks and clears any change flags from previous state + // updates. This function should be called outside the hot path since it's not + // critical to composition. + void commitChanges(); + + class ILifecycleListener { + public: + virtual ~ILifecycleListener() = default; + // Called on commitChanges when a layer is added. The callback includes + // the layer state the client was created with as well as any state updates + // until changes were committed. + virtual void onLayerAdded(const RequestedLayerState&) = 0; + // Called on commitChanges when a layer has been destroyed. The callback + // includes the final state before the layer was destroyed. + virtual void onLayerDestroyed(const RequestedLayerState&) = 0; + }; + void addLifecycleListener(std::shared_ptr<ILifecycleListener>); + void removeLifecycleListener(std::shared_ptr<ILifecycleListener>); + const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const; + const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const; + const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const; + +private: + friend class LayerLifecycleManagerTest; + friend class HierarchyBuilderTest; + friend class android::SurfaceFlinger; + + RequestedLayerState* getLayerFromId(uint32_t); + std::vector<uint32_t>* getLinkedLayersFromId(uint32_t); + void linkLayer(uint32_t layerId, uint32_t layerToLink); + void unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer); + + struct References { + // Lifetime tied to mLayers + RequestedLayerState& owner; + std::vector<uint32_t> references; + std::string getDebugString() const; + }; + std::unordered_map<uint32_t, References> mIdToLayer; + // Listeners are invoked once changes are committed. + std::vector<std::shared_ptr<ILifecycleListener>> mListeners; + + // Aggregation of changes since last commit. + ftl::Flags<RequestedLayerState::Changes> mGlobalChanges; + std::vector<std::unique_ptr<RequestedLayerState>> mLayers; + // Layers pending destruction. Layers will be destroyed once changes are committed. + std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp new file mode 100644 index 0000000000..45058d900f --- /dev/null +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -0,0 +1,358 @@ +/* + * Copyright 2022 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 "FrontEnd/LayerCreationArgs.h" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#undef LOG_TAG +#define LOG_TAG "RequestedLayerState" + +#include <private/android_filesystem_config.h> +#include <sys/types.h> + +#include "Layer.h" +#include "LayerHandle.h" +#include "RequestedLayerState.h" + +namespace android::surfaceflinger::frontend { +using ftl::Flags; +using namespace ftl::flag_operators; + +namespace { +uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) { + if (!surfaceControl) { + return UNASSIGNED_LAYER_ID; + } + + return LayerHandle::getLayerId(surfaceControl->getHandle()); +} + +std::string layerIdToString(uint32_t layerId) { + return layerId == UNASSIGNED_LAYER_ID ? std::to_string(layerId) : "none"; +} + +} // namespace + +RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) + : id(args.sequence), + name(args.name), + canBeRoot(args.addToRoot), + layerCreationFlags(args.flags), + textureName(args.textureName), + ownerUid(args.ownerUid), + ownerPid(args.ownerPid) { + layerId = static_cast<int32_t>(args.sequence); + changes |= RequestedLayerState::Changes::Created; + metadata.merge(args.metadata); + changes |= RequestedLayerState::Changes::Metadata; + handleAlive = true; + parentId = LayerHandle::getLayerId(args.parentHandle.promote()); + mirrorId = LayerHandle::getLayerId(args.mirrorLayerHandle.promote()); + if (mirrorId != UNASSIGNED_LAYER_ID) { + changes |= RequestedLayerState::Changes::Mirror; + } + + flags = 0; + if (args.flags & ISurfaceComposerClient::eHidden) flags |= layer_state_t::eLayerHidden; + if (args.flags & ISurfaceComposerClient::eOpaque) flags |= layer_state_t::eLayerOpaque; + if (args.flags & ISurfaceComposerClient::eSecure) flags |= layer_state_t::eLayerSecure; + if (args.flags & ISurfaceComposerClient::eSkipScreenshot) { + flags |= layer_state_t::eLayerSkipScreenshot; + } + premultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); + potentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; + protectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; + if (args.flags & ISurfaceComposerClient::eNoColorFill) { + // Set an invalid color so there is no color fill. + // (b/259981098) use an explicit flag instead of relying on invalid values. + color.r = -1.0_hf; + color.g = -1.0_hf; + color.b = -1.0_hf; + } else { + color.rgb = {0.0_hf, 0.0_hf, 0.0_hf}; + } + color.a = 1.0f; + + crop.makeInvalid(); + z = 0; + layerStack = ui::DEFAULT_LAYER_STACK; + transformToDisplayInverse = false; + dataspace = ui::Dataspace::UNKNOWN; + dataspaceRequested = false; + hdrMetadata.validTypes = 0; + surfaceDamageRegion = Region::INVALID_REGION; + cornerRadius = 0.0f; + backgroundBlurRadius = 0; + api = -1; + hasColorTransform = false; + bufferTransform = 0; + requestedTransform.reset(); + bufferData = std::make_shared<BufferData>(); + bufferData->frameNumber = 0; + bufferData->acquireFence = sp<Fence>::make(-1); + acquireFenceTime = std::make_shared<FenceTime>(bufferData->acquireFence); + colorSpaceAgnostic = false; + frameRateSelectionPriority = Layer::PRIORITY_UNSET; + shadowRadius = 0.f; + fixedTransformHint = ui::Transform::ROT_INVALID; + destinationFrame.makeInvalid(); + isTrustedOverlay = false; + dropInputMode = gui::DropInputMode::NONE; + dimmingEnabled = true; + defaultFrameRateCompatibility = + static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default); + dataspace = ui::Dataspace::V0_SRGB; +} + +void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) { + bool oldFlags = flags; + Rect oldBufferSize = getBufferSize(); + const layer_state_t& clientState = resolvedComposerState.state; + + uint64_t clientChanges = what | layer_state_t::diff(clientState); + layer_state_t::merge(clientState); + what = clientChanges; + + if (clientState.what & layer_state_t::eFlagsChanged) { + if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) { + changes |= RequestedLayerState::Changes::Visibility; + } + if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) { + changes |= RequestedLayerState::Changes::Geometry; + } + } + if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize()) { + changes |= RequestedLayerState::Changes::Geometry; + } + if (clientChanges & layer_state_t::HIERARCHY_CHANGES) + changes |= RequestedLayerState::Changes::Hierarchy; + if (clientChanges & layer_state_t::CONTENT_CHANGES) + changes |= RequestedLayerState::Changes::Content; + if (clientChanges & layer_state_t::GEOMETRY_CHANGES) + changes |= RequestedLayerState::Changes::Geometry; + + if (clientState.what & layer_state_t::eColorTransformChanged) { + static const mat4 identityMatrix = mat4(); + hasColorTransform = colorTransform != identityMatrix; + } + if (clientState.what & layer_state_t::eLayerChanged) { + changes |= RequestedLayerState::Changes::Z; + } + if (clientState.what & layer_state_t::eReparent) { + changes |= RequestedLayerState::Changes::Parent; + parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild); + parentSurfaceControlForChild = nullptr; + } + if (clientState.what & layer_state_t::eRelativeLayerChanged) { + changes |= RequestedLayerState::Changes::RelativeParent; + relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl); + isRelativeOf = true; + relativeLayerSurfaceControl = nullptr; + } + if ((clientState.what & layer_state_t::eLayerChanged || + (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) && + isRelativeOf) { + // clear out relz data + relativeParentId = UNASSIGNED_LAYER_ID; + isRelativeOf = false; + changes |= RequestedLayerState::Changes::RelativeParent; + } + if (clientState.what & layer_state_t::eReparent && parentId == relativeParentId) { + // provide a hint that we are are now a direct child and not a relative child. + changes |= RequestedLayerState::Changes::RelativeParent; + } + if (clientState.what & layer_state_t::eInputInfoChanged) { + wp<IBinder>& touchableRegionCropHandle = + windowInfoHandle->editInfo()->touchableRegionCropHandle; + touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote()); + changes |= RequestedLayerState::Changes::Input; + touchableRegionCropHandle.clear(); + } + if (clientState.what & layer_state_t::eStretchChanged) { + stretchEffect.sanitize(); + } + + if (clientState.what & layer_state_t::eHasListenerCallbacksChanged) { + // TODO(b/238781169) handle callbacks + } + + if (clientState.what & layer_state_t::eBufferChanged) { + externalTexture = resolvedComposerState.externalTexture; + hwcBufferSlot = resolvedComposerState.hwcBufferSlot; + } + + if (clientState.what & layer_state_t::ePositionChanged) { + requestedTransform.set(x, y); + } + + if (clientState.what & layer_state_t::eMatrixChanged) { + requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + } +} + +ui::Transform RequestedLayerState::getTransform() const { + if ((flags & layer_state_t::eIgnoreDestinationFrame) || destinationFrame.isEmpty()) { + // If destination frame is not set, use the requested transform set via + // Transaction::setPosition and Transaction::setMatrix. + return requestedTransform; + } + + Rect destRect = destinationFrame; + int32_t destW = destRect.width(); + int32_t destH = destRect.height(); + if (destRect.left < 0) { + destRect.left = 0; + destRect.right = destW; + } + if (destRect.top < 0) { + destRect.top = 0; + destRect.bottom = destH; + } + + if (!externalTexture) { + ui::Transform transform; + transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top)); + return transform; + } + + uint32_t bufferWidth = externalTexture->getWidth(); + uint32_t bufferHeight = externalTexture->getHeight(); + // Undo any transformations on the buffer. + if (bufferTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + // TODO(b/238781169) remove dep + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (transformToDisplayInverse) { + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + float sx = static_cast<float>(destW) / static_cast<float>(bufferWidth); + float sy = static_cast<float>(destH) / static_cast<float>(bufferHeight); + ui::Transform transform; + transform.set(sx, 0, 0, sy); + transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top)); + return transform; +} + +std::string RequestedLayerState::getDebugString() const { + return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) + + ",relativeParent=" + layerIdToString(relativeParentId) + + ",isRelativeOf=" + std::to_string(isRelativeOf) + + ",mirrorId=" + layerIdToString(mirrorId) + + ",handleAlive=" + std::to_string(handleAlive); +} + +std::string RequestedLayerState::getDebugStringShort() const { + return "[" + std::to_string(id) + "]" + name; +} + +bool RequestedLayerState::canBeDestroyed() const { + return !handleAlive && parentId == UNASSIGNED_LAYER_ID; +} +bool RequestedLayerState::isRoot() const { + return canBeRoot && parentId == UNASSIGNED_LAYER_ID; +} +bool RequestedLayerState::isHiddenByPolicy() const { + return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden; +}; +half4 RequestedLayerState::getColor() const { + if ((sidebandStream != nullptr) || (externalTexture != nullptr)) { + return {0._hf, 0._hf, 0._hf, color.a}; + } + return color; +} +Rect RequestedLayerState::getBufferSize() const { + // for buffer state layers we use the display frame size as the buffer size. + if (!externalTexture) { + return Rect::INVALID_RECT; + } + + uint32_t bufWidth = externalTexture->getWidth(); + uint32_t bufHeight = externalTexture->getHeight(); + + // Undo any transformations on the buffer and return the result. + if (bufferTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + + if (transformToDisplayInverse) { + // TODO(b/238781169) pass in display metrics (would be useful for input info as well + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + } + + return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); +} + +Rect RequestedLayerState::getCroppedBufferSize() const { + Rect size = getBufferSize(); + if (!crop.isEmpty() && size.isValid()) { + size.intersect(crop, &size); + } else if (!crop.isEmpty()) { + size = crop; + } + return size; +} + +Rect RequestedLayerState::getBufferCrop() const { + // this is the crop rectangle that applies to the buffer + // itself (as opposed to the window) + if (!bufferCrop.isEmpty()) { + // if the buffer crop is defined, we use that + return bufferCrop; + } else if (externalTexture != nullptr) { + // otherwise we use the whole buffer + return externalTexture->getBounds(); + } else { + // if we don't have a buffer yet, we use an empty/invalid crop + return Rect(); + } +} + +aidl::android::hardware::graphics::composer3::Composition RequestedLayerState::getCompositionType() + const { + using aidl::android::hardware::graphics::composer3::Composition; + // TODO(b/238781169) check about sidestream ready flag + if (sidebandStream.get()) { + return Composition::SIDEBAND; + } + if (!externalTexture) { + return Composition::SOLID_COLOR; + } + if (flags & layer_state_t::eLayerIsDisplayDecoration) { + return Composition::DISPLAY_DECORATION; + } + if (potentialCursor) { + return Composition::CURSOR; + } + return Composition::DEVICE; +} + +Rect RequestedLayerState::reduce(const Rect& win, const Region& exclude) { + if (CC_LIKELY(exclude.isEmpty())) { + return win; + } + if (exclude.isRect()) { + return win.reduce(exclude.getBounds()); + } + return Region(win).subtract(exclude).getBounds(); +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h new file mode 100644 index 0000000000..0ddf5e2895 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -0,0 +1,102 @@ +/* + * Copyright 2022 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 <aidl/android/hardware/graphics/composer3/Composition.h> +#include <ftl/flags.h> +#include <gui/LayerState.h> +#include <renderengine/ExternalTexture.h> + +#include "LayerCreationArgs.h" +#include "TransactionState.h" + +namespace android::surfaceflinger::frontend { + +// Stores client requested states for a layer. +// This struct does not store any other states or states pertaining to +// other layers. Links to other layers that are part of the client +// requested state such as parent are translated to layer id so +// we can avoid extending the lifetime of layer handles. +struct RequestedLayerState : layer_state_t { + // Changes in state after merging with new state. This includes additional state + // changes found in layer_state_t::what. + enum class Changes : uint32_t { + Created = 1u << 0, + Destroyed = 1u << 1, + Hierarchy = 1u << 2, + Geometry = 1u << 3, + Content = 1u << 4, + Input = 1u << 5, + Z = 1u << 6, + Mirror = 1u << 7, + Parent = 1u << 8, + RelativeParent = 1u << 9, + Metadata = 1u << 10, + Visibility = 1u << 11, + }; + static Rect reduce(const Rect& win, const Region& exclude); + RequestedLayerState(const LayerCreationArgs&); + void merge(const ResolvedComposerState&); + ui::Transform getTransform() const; + bool canBeDestroyed() const; + bool isRoot() const; + bool isHiddenByPolicy() const; + half4 getColor() const; + Rect getBufferSize() const; + Rect getCroppedBufferSize() const; + Rect getBufferCrop() const; + std::string getDebugString() const; + std::string getDebugStringShort() const; + aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; + + // Layer serial number. This gives layers an explicit ordering, so we + // have a stable sort order when their layer stack and Z-order are + // the same. + const uint32_t id; + const std::string name; + const bool canBeRoot = false; + const uint32_t layerCreationFlags; + const uint32_t textureName; + // The owner of the layer. If created from a non system process, it will be the calling uid. + // If created from a system process, the value can be passed in. + const uid_t ownerUid; + // The owner pid of the layer. If created from a non system process, it will be the calling pid. + // If created from a system process, the value can be passed in. + const pid_t ownerPid; + bool dataspaceRequested; + bool hasColorTransform; + bool premultipliedAlpha{true}; + // This layer can be a cursor on some displays. + bool potentialCursor{false}; + bool protectedByApp{false}; // application requires protected path to external sink + ui::Transform requestedTransform; + std::shared_ptr<FenceTime> acquireFenceTime; + std::shared_ptr<renderengine::ExternalTexture> externalTexture; + int hwcBufferSlot = 0; + + // book keeping states + bool handleAlive = true; + bool isRelativeOf = false; + uint32_t parentId = UNASSIGNED_LAYER_ID; + uint32_t relativeParentId = UNASSIGNED_LAYER_ID; + uint32_t mirrorId = UNASSIGNED_LAYER_ID; + uint32_t touchCropId = UNASSIGNED_LAYER_ID; + uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID; + ftl::Flags<RequestedLayerState::Changes> changes; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h new file mode 100644 index 0000000000..f672f998d9 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/SwapErase.h @@ -0,0 +1,45 @@ +/* + * Copyright 2022 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 <vector> + +namespace android::surfaceflinger::frontend { +// Erases the first element in vec that matches value. This is a more optimal way to +// remove an element from a vector that avoids relocating all the elements after the one +// that is erased. +template <typename T> +void swapErase(std::vector<T>& vec, const T& value) { + auto it = std::find(vec.begin(), vec.end(), value); + if (it != vec.end()) { + std::iter_swap(it, vec.end() - 1); + vec.erase(vec.end() - 1); + } +} + +// Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element +// that returns true for predicate. +template <typename T, class P> +void swapErase(std::vector<T>& vec, P predicate) { + auto it = std::find_if(vec.begin(), vec.end(), predicate); + if (it != vec.end()) { + std::iter_swap(it, vec.end() - 1); + vec.erase(vec.end() - 1); + } +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp index 95961fe6a9..8629671214 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp @@ -24,7 +24,7 @@ #include "TransactionHandler.h" -namespace android { +namespace android::surfaceflinger::frontend { void TransactionHandler::queueTransaction(TransactionState&& state) { mLocklessTransactionQueue.push(std::move(state)); @@ -186,4 +186,4 @@ void TransactionHandler::removeFromStalledTransactions(uint64_t id) { mStalledTransactions.erase(it); } } -} // namespace android +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h index 2b6f07d49a..a06b870549 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.h +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h @@ -18,9 +18,6 @@ #include <semaphore.h> #include <cstdint> -#include <mutex> -#include <queue> -#include <thread> #include <vector> #include <LocklessQueue.h> @@ -30,6 +27,10 @@ #include <ftl/small_vector.h> namespace android { + +class TestableSurfaceFlinger; +namespace surfaceflinger::frontend { + class TransactionHandler { public: struct TransactionFlushState { @@ -60,7 +61,7 @@ public: private: // For unit tests - friend class TestableSurfaceFlinger; + friend class ::android::TestableSurfaceFlinger; int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&); TransactionReadiness applyFilters(TransactionFlushState&); @@ -71,5 +72,5 @@ private: ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters; std::vector<uint64_t> mStalledTransactions; }; - +} // namespace surfaceflinger::frontend } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 2a18521b5b..0017af0476 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -195,8 +195,7 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.color.b = -1.0_hf; } - mFrameTracker.setDisplayRefreshPeriod( - args.flinger->mScheduler->getVsyncPeriodFromRefreshRateSelector()); + mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod()); mOwnerUid = args.ownerUid; mOwnerPid = args.ownerPid; @@ -1124,7 +1123,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for // the same reason we are allowing touch boost for those layers. See - // RefreshRateSelector::rankRefreshRates for details. + // RefreshRateSelector::rankFrameRates for details. const auto layerVotedWithDefaultCompatibility = frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default; const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote; @@ -2841,7 +2840,7 @@ bool Layer::setPosition(float x, float y) { bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime, - const FrameTimelineInfo& info) { + const FrameTimelineInfo& info, int hwcBufferSlot) { ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false")); if (!buffer) { return false; @@ -2887,7 +2886,7 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, mDrawingState.releaseBufferListener = bufferData.releaseBufferListener; mDrawingState.buffer = std::move(buffer); mDrawingState.clientCacheId = bufferData.cachedBuffer; - + mDrawingState.hwcBufferSlot = hwcBufferSlot; mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) ? bufferData.acquireFence : Fence::NO_FENCE; @@ -3186,7 +3185,7 @@ void Layer::gatherBufferInfo() { mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata; mBufferInfo.mApi = mDrawingState.api; mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse; - mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(mDrawingState.clientCacheId); + mBufferInfo.mBufferSlot = mDrawingState.hwcBufferSlot; } Rect Layer::computeBufferCrop(const State& s) { @@ -3205,7 +3204,6 @@ sp<Layer> Layer::createClone() { LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); args.textureName = mTextureName; sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args); - layer->mHwcSlotGenerator = mHwcSlotGenerator; layer->setInitialValuesForClone(sp<Layer>::fromExisting(this)); return layer; } @@ -3599,7 +3597,7 @@ void Layer::onPostComposition(const DisplayDevice* display, } if (display) { - const Fps refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps(); + const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps; const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); @@ -3970,18 +3968,19 @@ void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMet } } +int Layer::getHwcCacheSlot(const client_cache_t& clientCacheId) { + return mHwcSlotGenerator->getHwcCacheSlot(clientCacheId); +} + LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) { - LOG_ALWAYS_FATAL_IF(!mLayer, "LayerSnapshotGuard received a null layer."); - mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot); - LOG_ALWAYS_FATAL_IF(!mLayer->mLayerFE->mSnapshot, - "LayerFE snapshot null after taking ownership from layer"); + if (mLayer) { + mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot); + } } LayerSnapshotGuard::~LayerSnapshotGuard() { if (mLayer) { mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot); - LOG_ALWAYS_FATAL_IF(!mLayer->mSnapshot, - "Layer snapshot null after taking ownership from LayerFE"); } } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 9585fa9b30..f743896c03 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -83,6 +83,7 @@ class SurfaceFrame; } // namespace frametimeline class Layer : public virtual RefBase { +public: // The following constants represent priority of the window. SF uses this information when // deciding which window has a priority when deciding about the refresh rate of the screen. // Priority 0 is considered the highest priority. -1 means that the priority is unset. @@ -94,7 +95,6 @@ class Layer : public virtual RefBase { // Windows that are not in focus, but voted for a specific mode ID. static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2; -public: enum { // flags for doTransaction() eDontUpdateGeometryState = 0x00000001, eVisibleRegion = 0x00000002, @@ -146,6 +146,7 @@ public: bool transformToDisplayInverse; Region transparentRegionHint; std::shared_ptr<renderengine::ExternalTexture> buffer; + int hwcBufferSlot; client_cache_t clientCacheId; sp<Fence> acquireFence; std::shared_ptr<FenceTime> acquireFenceTime; @@ -297,7 +298,8 @@ public: bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */, const BufferData& /* bufferData */, nsecs_t /* postTime */, nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, - std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/); + std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/, + int /* hwcBufferSlot */); bool setDataspace(ui::Dataspace /*dataspace*/); bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/); bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/); @@ -811,6 +813,7 @@ public: void updateMetadataSnapshot(const LayerMetadata& parentMetadata); void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata, std::unordered_set<Layer*>& visited); + int getHwcCacheSlot(const client_cache_t& clientCacheId); protected: // For unit tests diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 8d4ea0520a..4be1ac7c6f 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -24,6 +24,7 @@ #include <chrono> #include <cmath> #include <deque> +#include <map> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -31,6 +32,7 @@ #include <ftl/fake_guard.h> #include <ftl/match.h> #include <ftl/unit.h> +#include <scheduler/FrameRateMode.h> #include <utils/Trace.h> #include "../SurfaceFlingerProperties.h" @@ -43,7 +45,7 @@ namespace android::scheduler { namespace { struct RefreshRateScore { - DisplayModeIterator modeIt; + FrameRateMode frameRateMode; float overallScore; struct { float modeBelowThreshold; @@ -77,19 +79,11 @@ std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) { return knownFrameRates; } -// The Filter is a `bool(const DisplayMode&)` predicate. -template <typename Filter> -std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) { +std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) { std::vector<DisplayModeIterator> sortedModes; sortedModes.reserve(modes.size()); - for (auto it = modes.begin(); it != modes.end(); ++it) { - const auto& [id, mode] = *it; - - if (filter(*mode)) { - ALOGV("%s: including mode %d", __func__, id.value()); - sortedModes.push_back(it); - } + sortedModes.push_back(it); } std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) { @@ -106,6 +100,21 @@ std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Fi return sortedModes; } +std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range, + RefreshRateSelector::Config::FrameRateOverride config) { + if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) { + return {1, 1}; + } + + using fps_approx_ops::operator/; + const auto start = std::max(1u, fps / range.max - 1); + const auto end = fps / + std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate, + fps_approx_ops::operator<); + + return {start, end}; +} + bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) { for (const auto it1 : sortedModes) { const auto& mode1 = it1->second; @@ -136,27 +145,109 @@ std::string toString(const RefreshRateSelector::PolicyVariant& policy) { } // namespace +auto RefreshRateSelector::createFrameRateModes( + std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const + -> std::vector<FrameRateMode> { + struct Key { + Fps fps; + int32_t group; + }; + + struct KeyLess { + bool operator()(const Key& a, const Key& b) const { + using namespace fps_approx_ops; + if (a.fps != b.fps) { + return a.fps < b.fps; + } + + // For the same fps the order doesn't really matter, but we still + // want the behaviour of a strictly less operator. + // We use the group id as the secondary ordering for that. + return a.group < b.group; + } + }; + + std::map<Key, DisplayModeIterator, KeyLess> ratesMap; + for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) { + const auto& [id, mode] = *it; + + if (!filterModes(*mode)) { + continue; + } + const auto [start, end] = + divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride); + for (auto divisor = start; divisor <= end; divisor++) { + const auto fps = mode->getFps() / divisor; + using fps_approx_ops::operator<; + if (fps < kMinSupportedFrameRate) { + break; + } + + if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled && + !renderRange.includes(fps)) { + continue; + } + + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::AppOverrideNativeRefreshRates && + !isNativeRefreshRate(fps)) { + continue; + } + + const auto [existingIter, emplaceHappened] = + ratesMap.try_emplace(Key{fps, mode->getGroup()}, it); + if (emplaceHappened) { + ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(), + to_string(mode->getFps()).c_str()); + } else { + // We might need to update the map as we found a lower refresh rate + if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) { + existingIter->second = it; + ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(), + to_string(mode->getFps()).c_str()); + } + } + } + } + + std::vector<FrameRateMode> frameRateModes; + frameRateModes.reserve(ratesMap.size()); + for (const auto& [key, mode] : ratesMap) { + frameRateModes.emplace_back(FrameRateMode{key.fps, ftl::as_non_null(mode->second)}); + } + + // We always want that the lowest frame rate will be corresponding to the + // lowest mode for power saving. + const auto lowestRefreshRateIt = + std::min_element(frameRateModes.begin(), frameRateModes.end(), + [](const FrameRateMode& lhs, const FrameRateMode& rhs) { + return isStrictlyLess(lhs.modePtr->getFps(), + rhs.modePtr->getFps()); + }); + frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt); + + return frameRateModes; +} + struct RefreshRateSelector::RefreshRateScoreComparator { bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const { - const auto& [modeIt, overallScore, _] = lhs; + const auto& [frameRateMode, overallScore, _] = lhs; - std::string name = to_string(modeIt->second->getFps()); - ALOGV("%s sorting scores %.2f", name.c_str(), overallScore); + std::string name = to_string(frameRateMode); + ALOGV("%s sorting scores %.2f", name.c_str(), overallScore); ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100))); - if (!ScoredRefreshRate::scoresEqual(overallScore, rhs.overallScore)) { + if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) { return overallScore > rhs.overallScore; } - // If overallScore tie we will pick the higher refresh rate if - // high refresh rate is the priority else the lower refresh rate. if (refreshRateOrder == RefreshRateOrder::Descending) { using fps_approx_ops::operator>; - return modeIt->second->getFps() > rhs.modeIt->second->getFps(); + return frameRateMode.fps > rhs.frameRateMode.fps; } else { using fps_approx_ops::operator<; - return modeIt->second->getFps() < rhs.modeIt->second->getFps(); + return frameRateMode.fps < rhs.frameRateMode.fps; } } @@ -210,7 +301,15 @@ float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const Layer if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || layer.vote == LayerVoteType::Heuristic) { - if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) { + const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue(); + + // We only want to score this layer as a fractional pair if the content is not + // significantly faster than the display rate, at it would cause a significant frame drop. + // It is more appropriate to choose a higher display rate even if + // a pull-down will be required. + constexpr float kMinMultiplier = 0.25f; + if (multiplier >= kMinMultiplier && + isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) { return kScoreForFractionalPairs; } @@ -245,9 +344,9 @@ float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const Layer return 0; } -float RefreshRateSelector::calculateRefreshRateScoreForFps(Fps refreshRate) const { - const float ratio = - refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue(); +float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const { + const auto& maxFps = mAppRequestFrameRates.back().fps; + const float ratio = refreshRate.getValue() / maxFps.getValue(); // Use ratio^2 to get a lower score the more we get further from peak return ratio * ratio; } @@ -260,12 +359,12 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay // If the layer wants Max, give higher score to the higher refresh rate if (layer.vote == LayerVoteType::Max) { - return calculateRefreshRateScoreForFps(refreshRate); + return calculateDistanceScoreFromMax(refreshRate); } if (layer.vote == LayerVoteType::ExplicitExact) { const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate); - if (supportsFrameRateOverrideByContent()) { + if (supportsAppFrameRateOverrideByContent()) { // Since we support frame rate override, allow refresh rates which are // multiples of the layer's request, as those apps would be throttled // down to run at the desired refresh rate. @@ -289,33 +388,33 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay kNonExactMatchingPenalty; } -auto RefreshRateSelector::getRankedRefreshRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const -> RankedRefreshRates { +auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const -> RankedFrameRates { std::lock_guard lock(mLock); - if (mGetRankedRefreshRatesCache && - mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) { - return mGetRankedRefreshRatesCache->result; + if (mGetRankedFrameRatesCache && + mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) { + return mGetRankedFrameRatesCache->result; } - const auto result = getRankedRefreshRatesLocked(layers, signals); - mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result}; + const auto result = getRankedFrameRatesLocked(layers, signals); + mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result}; return result; } -auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const - -> RankedRefreshRates { +auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const + -> RankedFrameRates { using namespace fps_approx_ops; ATRACE_CALL(); ALOGV("%s: %zu layers", __func__, layers.size()); - const auto& activeMode = *getActiveModeItLocked()->second; + const auto& activeMode = *getActiveModeLocked().modePtr; - // Keep the display at max refresh rate for the duration of powering on the display. + // Keep the display at max frame rate for the duration of powering on the display. if (signals.powerOnImminent) { ALOGV("Power On Imminent"); - return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Descending), + return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending), GlobalSignals{.powerOnImminent = true}}; } @@ -375,7 +474,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq // selected a refresh rate to see if we should apply touch boost. if (signals.touch && !hasExplicitVoteLayers) { ALOGV("Touch Boost"); - return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), + return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), GlobalSignals{.touch = true}}; } @@ -387,27 +486,27 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { ALOGV("Idle"); - return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending), + return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), GlobalSignals{.idle = true}}; } if (layers.empty() || noVoteLayers == layers.size()) { ALOGV("No layers with votes"); - return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals}; + return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals}; } // Only if all layers want Min we should return Min if (noVoteLayers + minVoteLayers == layers.size()) { ALOGV("All layers Min"); - return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals}; + return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals}; } // Find the best refresh rate based on score std::vector<RefreshRateScore> scores; - scores.reserve(mAppRequestRefreshRates.size()); + scores.reserve(mAppRequestFrameRates.size()); - for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) { - scores.emplace_back(RefreshRateScore{modeIt, 0.0f}); + for (const FrameRateMode& it : mAppRequestFrameRates) { + scores.emplace_back(RefreshRateScore{it, 0.0f}); } for (const auto& layer : layers) { @@ -420,13 +519,13 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq const auto weight = layer.weight; - for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) { - const auto& [id, mode] = *modeIt; - const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup(); + for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) { + const auto& [fps, modePtr] = mode; + const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup(); if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) { ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s", - formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(), + formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(), to_string(activeMode).c_str()); continue; } @@ -435,7 +534,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq !layer.focused) { ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed." " Current mode = %s", - formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(), + formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(), to_string(activeMode).c_str()); continue; } @@ -445,14 +544,14 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq // mode group otherwise. In second case, if the current mode group is different // from the default, this means a layer with seamlessness=SeamedAndSeamless has just // disappeared. - const bool isInPolicyForDefault = mode->getGroup() == anchorGroup; + const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup; if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) { ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(), - to_string(*mode).c_str(), to_string(activeMode).c_str()); + to_string(*modePtr).c_str(), to_string(activeMode).c_str()); continue; } - const bool inPrimaryRange = policy->primaryRanges.physical.includes(mode->getFps()); + const bool inPrimaryRange = policy->primaryRanges.physical.includes(modePtr->getFps()); if ((primaryRangeIsSingleRate || !inPrimaryRange) && !(layer.focused && (layer.vote == LayerVoteType::ExplicitDefault || @@ -462,8 +561,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq continue; } - const float layerScore = - calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch); + const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch); const float weightedLayerScore = weight * layerScore; // Layer with fixed source has a special consideration which depends on the @@ -491,21 +589,21 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq Fps::fromValue(mConfig.frameRateMultipleThreshold / 2); if (fixedSourceLayer && layerBelowThreshold) { const bool modeAboveThreshold = - mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold); + modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold); if (modeAboveThreshold) { - ALOGV("%s gives %s fixed source (above threshold) score of %.4f", - formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(), - layerScore); + ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f", + formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(), + to_string(modePtr->getFps()).c_str(), layerScore); fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore; } else { - ALOGV("%s gives %s fixed source (below threshold) score of %.4f", - formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(), - layerScore); + ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f", + formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(), + to_string(modePtr->getFps()).c_str(), layerScore); fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore; } } else { - ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(), - to_string(mode->getFps()).c_str(), layerScore); + ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(), + to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore); overallScore += weightedLayerScore; } } @@ -524,25 +622,26 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq const auto maxScoreIt = std::max_element(scores.begin(), scores.end(), [](RefreshRateScore max, RefreshRateScore current) { - const auto& [modeIt, overallScore, _] = current; - return overallScore > max.overallScore; + return current.overallScore > max.overallScore; }); - ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for " + ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the " + "threshold for " "refresh rate multiples", - to_string(maxScoreIt->modeIt->second->getFps()).c_str(), + to_string(maxScoreIt->frameRateMode.fps).c_str(), + to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(), maxScoreAboveThreshold ? "above" : "below"); - return maxScoreIt->modeIt->second->getFps() >= + return maxScoreIt->frameRateMode.modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold); }(); // Now we can add the fixed rate layers score - for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) { + for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) { overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold; if (maxScoreAboveThreshold) { overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold; } - ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(), - overallScore); + ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(), + to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore); } // Now that we scored all the refresh rates we need to pick the one that got the highest @@ -552,12 +651,12 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq std::sort(scores.begin(), scores.end(), RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder}); - RefreshRateRanking ranking; + FrameRateRanking ranking; ranking.reserve(scores.size()); std::transform(scores.begin(), scores.end(), back_inserter(ranking), [](const RefreshRateScore& score) { - return ScoredRefreshRate{score.modeIt->second, score.overallScore}; + return ScoredFrameRate{score.frameRateMode, score.overallScore}; }); const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) { @@ -569,7 +668,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq // range instead of picking a random score from the app range. if (noLayerScore) { ALOGV("Layers not scored"); - return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals}; + return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals}; } else { return {ranking, kNoSignals}; } @@ -580,7 +679,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq // vote we should not change it if we get a touch event. Only apply touch boost if it will // actually increase the refresh rate over the normal selection. const bool touchBoostForExplicitExact = [&] { - if (supportsFrameRateOverrideByContent()) { + if (supportsAppFrameRateOverrideByContent()) { // Enable touch boost if there are other layers besides exact return explicitExact + noVoteLayers != layers.size(); } else { @@ -589,12 +688,11 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq } }(); - const auto touchRefreshRates = rankRefreshRates(anchorGroup, RefreshRateOrder::Descending); - + const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && - scores.front().modeIt->second->getFps() < touchRefreshRates.front().modePtr->getFps()) { + scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost"); return {touchRefreshRates, GlobalSignals{.touch = true}}; } @@ -603,7 +701,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq // current config if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) { const auto preferredDisplayMode = activeMode.getId(); - return {rankRefreshRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode), + return {rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode), kNoSignals}; } @@ -649,20 +747,13 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme GlobalSignals globalSignals) const -> UidToFrameRateOverride { ATRACE_CALL(); - ALOGV("%s: %zu layers", __func__, layers.size()); + if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) { + return {}; + } + ALOGV("%s: %zu layers", __func__, layers.size()); std::lock_guard lock(mLock); - // Prepare a set of supported display refresh rates for easy lookup - constexpr size_t kStaticCapacity = 8; - ftl::SmallMap<Fps, ftl::Unit, kStaticCapacity, FpsApproxEqual> supportedDisplayRefreshRates; - if (mConfig.enableFrameRateOverride == - Config::FrameRateOverride::EnabledForNativeRefreshRates) { - for (const auto& [_, modePtr] : mDisplayModes) { - supportedDisplayRefreshRates.try_emplace(modePtr->getFps(), ftl::unit); - } - } - const auto* policyPtr = getCurrentPolicyLocked(); // We don't want to run lower than 30fps const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess); @@ -676,8 +767,8 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme for (unsigned n = numMultiples; n > 0; n--) { const Fps divisor = displayRefreshRate / n; if (mConfig.enableFrameRateOverride == - Config::FrameRateOverride::EnabledForNativeRefreshRates && - !supportedDisplayRefreshRates.contains(divisor)) { + Config::FrameRateOverride::AppOverrideNativeRefreshRates && + !isNativeRefreshRate(divisor)) { continue; } @@ -736,7 +827,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme [](const auto& lhsPair, const auto& rhsPair) { const float lhs = lhsPair.second; const float rhs = rhsPair.second; - return lhs < rhs && !ScoredRefreshRate::scoresEqual(lhs, rhs); + return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs); }); ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid); frameRateOverrides.emplace(uid, overrideFps); @@ -751,7 +842,7 @@ std::optional<Fps> RefreshRateSelector::onKernelTimerChanged( const DisplayModePtr& current = desiredActiveModeId ? mDisplayModes.get(*desiredActiveModeId)->get() - : getActiveModeItLocked()->second; + : getActiveModeLocked().modePtr.get(); const DisplayModePtr& min = mMinRefreshRateModeIt->second; if (current == min) { @@ -763,12 +854,11 @@ std::optional<Fps> RefreshRateSelector::onKernelTimerChanged( } const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const { - const auto& activeMode = *getActiveModeItLocked()->second; + const auto& activeMode = *getActiveModeLocked().modePtr; - for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) { - const auto& mode = modeIt->second; - if (activeMode.getGroup() == mode->getGroup()) { - return mode; + for (const FrameRateMode& mode : mPrimaryFrameRates) { + if (activeMode.getGroup() == mode.modePtr->getGroup()) { + return mode.modePtr.get(); } } @@ -776,55 +866,72 @@ const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() con to_string(activeMode).c_str()); // Default to the lowest refresh rate. - return mPrimaryRefreshRates.front()->second; + return mPrimaryFrameRates.front().modePtr.get(); } const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const { - for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) { - const auto& mode = (*it)->second; - if (anchorGroup == mode->getGroup()) { - return mode; + const ftl::NonNull<DisplayModePtr>* maxByAnchor = &mPrimaryFrameRates.back().modePtr; + const ftl::NonNull<DisplayModePtr>* max = &mPrimaryFrameRates.back().modePtr; + + bool maxByAnchorFound = false; + for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) { + using namespace fps_approx_ops; + if (it->modePtr->getFps() > (*max)->getFps()) { + max = &it->modePtr; + } + + if (anchorGroup == it->modePtr->getGroup() && + it->modePtr->getFps() >= (*maxByAnchor)->getFps()) { + maxByAnchorFound = true; + maxByAnchor = &it->modePtr; } } + if (maxByAnchorFound) { + return maxByAnchor->get(); + } + ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup); // Default to the highest refresh rate. - return mPrimaryRefreshRates.back()->second; + return max->get(); } -auto RefreshRateSelector::rankRefreshRates( - std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder, - std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking { - std::deque<ScoredRefreshRate> ranking; - - const auto rankRefreshRate = [&](DisplayModeIterator it) REQUIRES(mLock) { - const auto& mode = it->second; - if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) { +auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt, + RefreshRateOrder refreshRateOrder, + std::optional<DisplayModeId> preferredDisplayModeOpt) const + -> FrameRateRanking { + const char* const whence = __func__; + std::deque<ScoredFrameRate> ranking; + const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) { + const auto& modePtr = frameRateMode.modePtr; + if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) { return; } - float score = calculateRefreshRateScoreForFps(mode->getFps()); + float score = calculateDistanceScoreFromMax(frameRateMode.fps); const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending); if (inverseScore) { score = 1.0f / score; } if (preferredDisplayModeOpt) { - if (*preferredDisplayModeOpt == mode->getId()) { + if (*preferredDisplayModeOpt == modePtr->getId()) { constexpr float kScore = std::numeric_limits<float>::max(); - ranking.push_front(ScoredRefreshRate{mode, kScore}); + ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore}); return; } constexpr float kNonPreferredModePenalty = 0.95f; score *= kNonPreferredModePenalty; } - ranking.push_back(ScoredRefreshRate{mode, score}); + ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(), + to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score); + ranking.emplace_back(ScoredFrameRate{frameRateMode, score}); }; if (refreshRateOrder == RefreshRateOrder::Ascending) { - std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), rankRefreshRate); + std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate); } else { - std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), rankRefreshRate); + std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate); } if (!ranking.empty() || !anchorGroupOpt) { @@ -836,34 +943,29 @@ auto RefreshRateSelector::rankRefreshRates( refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value()); constexpr std::optional<int> kNoAnchorGroup = std::nullopt; - return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt); + return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt); } -DisplayModePtr RefreshRateSelector::getActiveModePtr() const { +FrameRateMode RefreshRateSelector::getActiveMode() const { std::lock_guard lock(mLock); - return getActiveModeItLocked()->second; -} - -const DisplayMode& RefreshRateSelector::getActiveMode() const { - // Reads from kMainThreadContext do not require mLock. - ftl::FakeGuard guard(mLock); - return *mActiveModeIt->second; + return getActiveModeLocked(); } -DisplayModeIterator RefreshRateSelector::getActiveModeItLocked() const { - // Reads under mLock do not require kMainThreadContext. - return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt); +const FrameRateMode& RefreshRateSelector::getActiveModeLocked() const { + return *mActiveModeOpt; } -void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) { +void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRate) { std::lock_guard lock(mLock); - // Invalidate the cached invocation to getRankedRefreshRates. This forces - // the refresh rate to be recomputed on the next call to getRankedRefreshRates. - mGetRankedRefreshRatesCache.reset(); + // Invalidate the cached invocation to getRankedFrameRates. This forces + // the refresh rate to be recomputed on the next call to getRankedFrameRates. + mGetRankedFrameRatesCache.reset(); - mActiveModeIt = mDisplayModes.find(modeId); - LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end()); + const auto activeModeOpt = mDisplayModes.get(modeId); + LOG_ALWAYS_FATAL_IF(!activeModeOpt); + + mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())}); } RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId, @@ -895,16 +997,17 @@ void RefreshRateSelector::initializeIdleTimer() { void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) { std::lock_guard lock(mLock); - // Invalidate the cached invocation to getRankedRefreshRates. This forces - // the refresh rate to be recomputed on the next call to getRankedRefreshRates. - mGetRankedRefreshRatesCache.reset(); + // Invalidate the cached invocation to getRankedFrameRates. This forces + // the refresh rate to be recomputed on the next call to getRankedFrameRates. + mGetRankedFrameRatesCache.reset(); mDisplayModes = std::move(modes); - mActiveModeIt = mDisplayModes.find(activeModeId); - LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end()); + const auto activeModeOpt = mDisplayModes.get(activeModeId); + LOG_ALWAYS_FATAL_IF(!activeModeOpt); + mActiveModeOpt = + FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())}; - const auto sortedModes = - sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; }); + const auto sortedModes = sortByRefreshRate(mDisplayModes); mMinRefreshRateModeIt = sortedModes.front(); mMaxRefreshRateModeIt = sortedModes.back(); @@ -915,15 +1018,23 @@ void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId a mFrameRateOverrideConfig = [&] { switch (mConfig.enableFrameRateOverride) { case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverride: case Config::FrameRateOverride::Enabled: return mConfig.enableFrameRateOverride; - case Config::FrameRateOverride::EnabledForNativeRefreshRates: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: return shouldEnableFrameRateOverride(sortedModes) - ? Config::FrameRateOverride::EnabledForNativeRefreshRates + ? Config::FrameRateOverride::AppOverrideNativeRefreshRates : Config::FrameRateOverride::Disabled; } }(); + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::AppOverrideNativeRefreshRates) { + for (const auto& [_, mode] : mDisplayModes) { + mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit); + } + } + constructAvailableRefreshRates(); } @@ -939,13 +1050,18 @@ bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const { return false; } - using namespace fps_approx_ops; - return policy.appRequestRanges.physical.min <= policy.primaryRanges.physical.min && - policy.appRequestRanges.physical.max >= policy.primaryRanges.physical.max; + const auto& primaryRanges = policy.primaryRanges; + const auto& appRequestRanges = policy.appRequestRanges; + ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical), + "Physical range is invalid"); + ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render), "Render range is invalid"); + + return primaryRanges.valid() && appRequestRanges.valid(); } auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult { Policy oldPolicy; + PhysicalDisplayId displayId; { std::lock_guard lock(mLock); oldPolicy = *getCurrentPolicyLocked(); @@ -979,15 +1095,16 @@ auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyRes return SetPolicyResult::Invalid; } - mGetRankedRefreshRatesCache.reset(); + mGetRankedFrameRatesCache.reset(); if (*getCurrentPolicyLocked() == oldPolicy) { return SetPolicyResult::Unchanged; } constructAvailableRefreshRates(); + + displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId(); } - const auto displayId = getActiveMode().getPhysicalDisplayId(); const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u); ALOGI("Display %s policy changed\n" @@ -1014,12 +1131,10 @@ auto RefreshRateSelector::getDisplayManagerPolicy() const -> Policy { return mDisplayManagerPolicy; } -bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const { +bool RefreshRateSelector::isModeAllowed(const FrameRateMode& mode) const { std::lock_guard lock(mLock); - return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(), - [modeId](DisplayModeIterator modeIt) { - return modeIt->second->getId() == modeId; - }); + return std::find(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(), mode) != + mAppRequestFrameRates.end(); } void RefreshRateSelector::constructAvailableRefreshRates() { @@ -1029,33 +1144,35 @@ void RefreshRateSelector::constructAvailableRefreshRates() { const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get(); - const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) { - const auto filter = [&](const DisplayMode& mode) { + const auto filterRefreshRates = [&](const FpsRanges& ranges, + const char* rangeName) REQUIRES(mLock) { + const auto filterModes = [&](const DisplayMode& mode) { return mode.getResolution() == defaultMode->getResolution() && mode.getDpi() == defaultMode->getDpi() && (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) && - range.includes(mode.getFps()); + ranges.physical.includes(mode.getFps()) && + (supportsFrameRateOverride() || ranges.render.includes(mode.getFps())); }; - const auto modes = sortByRefreshRate(mDisplayModes, filter); - LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName, - to_string(range).c_str()); + const auto frameRateModes = createFrameRateModes(filterModes, ranges.render); + LOG_ALWAYS_FATAL_IF(frameRateModes.empty(), + "No matching frame rate modes for %s physicalRange %s", rangeName, + to_string(ranges.physical).c_str()); const auto stringifyModes = [&] { std::string str; - for (const auto modeIt : modes) { - str += to_string(modeIt->second->getFps()); - str.push_back(' '); + for (const auto& frameRateMode : frameRateModes) { + str += to_string(frameRateMode) + " "; } return str; }; - ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str()); + ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str()); - return modes; + return frameRateModes; }; - mPrimaryRefreshRates = filterRefreshRates(policy->primaryRanges.physical, "primary"); - mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRanges.physical, "app request"); + mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary"); + mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request"); } Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const { @@ -1091,7 +1208,7 @@ auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction { } const DisplayModePtr& maxByPolicy = - getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup()); + getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()); if (minByPolicy == maxByPolicy) { // Turn on the timer when the min of the primary range is below the device min. if (const Policy* currentPolicy = getCurrentPolicyLocked(); @@ -1137,8 +1254,8 @@ void RefreshRateSelector::dump(utils::Dumper& dumper) const { std::lock_guard lock(mLock); - const auto activeModeId = getActiveModeItLocked()->first; - dumper.dump("activeModeId"sv, std::to_string(activeModeId.value())); + const auto activeMode = getActiveModeLocked(); + dumper.dump("activeMode"sv, to_string(activeMode)); dumper.dump("displayModes"sv); { diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 0e80817521..1ed16c6a52 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -24,9 +24,11 @@ #include <ftl/concat.h> #include <ftl/optional.h> +#include <ftl/unit.h> #include <gui/DisplayEventReceiver.h> #include <scheduler/Fps.h> +#include <scheduler/FrameRateMode.h> #include <scheduler/Seamlessness.h> #include "DisplayHardware/DisplayMode.h" @@ -58,6 +60,9 @@ public: static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION = std::chrono::nanoseconds(800us).count(); + // The lowest Render Frame Rate that will ever be selected + static constexpr Fps kMinSupportedFrameRate = 20_Hz; + class Policy { static constexpr int kAllowGroupSwitchingDefault = false; @@ -128,7 +133,7 @@ public: Policy getDisplayManagerPolicy() const EXCLUDES(mLock); // Returns true if mode is allowed by the current policy. - bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock); + bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock); // Describes the different options the layer voted for refresh rate enum class LayerVoteType { @@ -196,12 +201,12 @@ public: } }; - struct ScoredRefreshRate { - DisplayModePtr modePtr; + struct ScoredFrameRate { + FrameRateMode frameRateMode; float score = 0.0f; - bool operator==(const ScoredRefreshRate& other) const { - return modePtr == other.modePtr && score == other.score; + bool operator==(const ScoredFrameRate& other) const { + return frameRateMode == other.frameRateMode && score == other.score; } static bool scoresEqual(float lhs, float rhs) { @@ -210,25 +215,25 @@ public: } struct DescendingScore { - bool operator()(const ScoredRefreshRate& lhs, const ScoredRefreshRate& rhs) const { + bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const { return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score); } }; }; - using RefreshRateRanking = std::vector<ScoredRefreshRate>; + using FrameRateRanking = std::vector<ScoredFrameRate>; - struct RankedRefreshRates { - RefreshRateRanking ranking; // Ordered by descending score. + struct RankedFrameRates { + FrameRateRanking ranking; // Ordered by descending score. GlobalSignals consideredSignals; - bool operator==(const RankedRefreshRates& other) const { + bool operator==(const RankedFrameRates& other) const { return ranking == other.ranking && consideredSignals == other.consideredSignals; } }; - RankedRefreshRates getRankedRefreshRates(const std::vector<LayerRequirement>&, - GlobalSignals) const EXCLUDES(mLock); + RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const + EXCLUDES(mLock); FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) { std::lock_guard lock(mLock); @@ -238,11 +243,10 @@ public: std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const EXCLUDES(mLock); - void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext); + void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock); - // See mActiveModeIt for thread safety. - DisplayModePtr getActiveModePtr() const EXCLUDES(mLock); - const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext); + // See mActiveModeOpt for thread safety. + FrameRateMode getActiveMode() const EXCLUDES(mLock); // Returns a known frame rate that is the closest to frameRate Fps findClosestKnownFrameRate(Fps frameRate) const; @@ -257,9 +261,12 @@ public: // Override the frame rate for an app to a value which is also // a display refresh rate - EnabledForNativeRefreshRates, + AppOverrideNativeRefreshRates, // Override the frame rate for an app to any value + AppOverride, + + // Override the frame rate for all apps and all values. Enabled, ftl_last = Enabled @@ -291,10 +298,13 @@ public: // Returns whether switching modes (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only - // differ in resolution. + // differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default, + // we can probably remove canSwitch altogether since all devices will be able + // to switch to a frame rate divisor. bool canSwitch() const EXCLUDES(mLock) { std::lock_guard lock(mLock); - return mDisplayModes.size() > 1; + return mDisplayModes.size() > 1 || + mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled; } // Class to enumerate options around toggling the kernel timer on and off. @@ -307,10 +317,14 @@ public: // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; - bool supportsFrameRateOverrideByContent() const { + bool supportsAppFrameRateOverrideByContent() const { return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled; } + bool supportsFrameRateOverride() const { + return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled; + } + // Return the display refresh rate divisor to match the layer // frame rate, or 0 if the display refresh rate is not a multiple of the // layer refresh rate. @@ -363,14 +377,16 @@ public: } } - void resetIdleTimer(bool kernelOnly) { - if (!mIdleTimer) { - return; + void resetKernelIdleTimer() { + if (mIdleTimer && mConfig.kernelIdleTimerController) { + mIdleTimer->reset(); } - if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) { - return; + } + + void resetIdleTimer() { + if (mIdleTimer) { + mIdleTimer->reset(); } - mIdleTimer->reset(); } void dump(utils::Dumper&) const EXCLUDES(mLock); @@ -382,11 +398,11 @@ private: void constructAvailableRefreshRates() REQUIRES(mLock); - // See mActiveModeIt for thread safety. - DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock); + // See mActiveModeOpt for thread safety. + const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock); - RankedRefreshRates getRankedRefreshRatesLocked(const std::vector<LayerRequirement>&, - GlobalSignals) const REQUIRES(mLock); + RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const REQUIRES(mLock); // Returns number of display frames and remainder when dividing the layer refresh period by // display refresh period. @@ -402,18 +418,24 @@ private: struct RefreshRateScoreComparator; - enum class RefreshRateOrder { Ascending, Descending }; + enum class RefreshRateOrder { + Ascending, + Descending, + + ftl_last = Descending + }; // Only uses the primary range, not the app request range. - RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder, - std::optional<DisplayModeId> preferredDisplayModeOpt = - std::nullopt) const REQUIRES(mLock); + FrameRateRanking rankFrameRates( + std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder, + std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const + REQUIRES(mLock); const Policy* getCurrentPolicyLocked() const REQUIRES(mLock); bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1. - float calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock); + float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock); // calculates a score for a layer. Used to determine the display refresh rate // and the frame rate override for certains applications. float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate, @@ -434,21 +456,35 @@ private: : mIdleTimerCallbacks->platform; } + bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) { + LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride != + Config::FrameRateOverride::AppOverrideNativeRefreshRates, + "should only be called when " + "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used"); + return mAppOverrideNativeRefreshRates.contains(fps); + } + + std::vector<FrameRateMode> createFrameRateModes( + std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const + REQUIRES(mLock); + // The display modes of the active display. The DisplayModeIterators below are pointers into // this container, so must be invalidated whenever the DisplayModes change. The Policy below // is also dependent, so must be reset as well. DisplayModes mDisplayModes GUARDED_BY(mLock); - // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext - // need not be under mLock. - DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext); + // Set of supported display refresh rates for easy lookup + // when FrameRateOverride::AppOverrideNativeRefreshRates is in use. + ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates; + + ftl::Optional<FrameRateMode> mActiveModeOpt GUARDED_BY(mLock); DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock); DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock); // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate. - std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock); - std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock); + std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock); + std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock); Policy mDisplayManagerPolicy GUARDED_BY(mLock); std::optional<Policy> mOverridePolicy GUARDED_BY(mLock); @@ -464,11 +500,11 @@ private: const Config mConfig; Config::FrameRateOverride mFrameRateOverrideConfig; - struct GetRankedRefreshRatesCache { + struct GetRankedFrameRatesCache { std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments; - RankedRefreshRates result; + RankedFrameRates result; }; - mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock); + mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock); // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed. std::mutex mIdleTimerCallbacksMutex; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 6108d92a50..7f8f600d30 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -70,7 +70,7 @@ Scheduler::~Scheduler() { mTouchTimer.reset(); // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler. - setRefreshRateSelector(nullptr); + demoteLeaderDisplay(); } void Scheduler::startTimers() { @@ -95,40 +95,29 @@ void Scheduler::startTimers() { } } -void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) { - // No need to lock for reads on kMainThreadContext. - if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) { - unbindIdleTimer(*selectorPtr); - } - - { - // Clear state that depends on the current RefreshRateSelector. - std::scoped_lock lock(mPolicyLock); - mPolicy = {}; - } +void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) { + demoteLeaderDisplay(); - std::scoped_lock lock(mRefreshRateSelectorLock); - mRefreshRateSelector = std::move(newSelectorPtr); - - if (mRefreshRateSelector) { - bindIdleTimer(*mRefreshRateSelector); - } + std::scoped_lock lock(mDisplayLock); + promoteLeaderDisplay(leaderIdOpt); } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - if (!mLeaderDisplayId) { - mLeaderDisplayId = displayId; - } + demoteLeaderDisplay(); + std::scoped_lock lock(mDisplayLock); mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr)); + + promoteLeaderDisplay(); } void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { - if (mLeaderDisplayId == displayId) { - mLeaderDisplayId.reset(); - } + demoteLeaderDisplay(); + std::scoped_lock lock(mDisplayLock); mRefreshRateSelectors.erase(displayId); + + promoteLeaderDisplay(); } void Scheduler::run() { @@ -163,7 +152,7 @@ std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { const bool supportsFrameRateOverrideByContent = - holdRefreshRateSelector()->supportsFrameRateOverrideByContent(); + leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); return mFrameRateOverrideMappings .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent); } @@ -178,8 +167,6 @@ bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { - std::scoped_lock lock(mRefreshRateSelectorLock); - return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) { return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid); }; @@ -187,7 +174,7 @@ impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const { return [this](uid_t uid) { - const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps(); + const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps; const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs(); const auto frameRate = getFrameRateOverride(uid); @@ -281,7 +268,7 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { const bool supportsFrameRateOverrideByContent = - holdRefreshRateSelector()->supportsFrameRateOverrideByContent(); + leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); std::vector<FrameRateOverride> overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); @@ -295,7 +282,7 @@ void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDis thread->onFrameRateOverridesChanged(displayId, std::move(overrides)); } -void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { +void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { { std::lock_guard<std::mutex> lock(mPolicyLock); // Cache the last reported modes for primary display. @@ -310,7 +297,7 @@ void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayMode void Scheduler::dispatchCachedReportedMode() { // Check optional fields first. - if (!mPolicy.mode) { + if (!mPolicy.modeOpt) { ALOGW("No mode ID found, not dispatching cached mode."); return; } @@ -322,29 +309,28 @@ void Scheduler::dispatchCachedReportedMode() { // If the mode is not the current mode, this means that a // mode change is in progress. In that case we shouldn't dispatch an event // as it will be dispatched when the current mode changes. - if (std::scoped_lock lock(mRefreshRateSelectorLock); - mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) { + if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) { return; } // If there is no change from cached mode, there is no need to dispatch an event - if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) { + if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) { return; } - mPolicy.cachedModeChangedParams->mode = mPolicy.mode; + mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt; onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle, mPolicy.cachedModeChangedParams->mode); } -void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { +void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { android::EventThread* thread; { std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle); thread = mConnections[handle].thread.get(); } - thread->onModeChanged(mode); + thread->onModeChanged(mode.modePtr.get()); } size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) { @@ -409,6 +395,24 @@ void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) { setVsyncPeriod(refreshRate.getPeriodNsecs()); } +void Scheduler::setRenderRate(Fps renderFrameRate) { + const auto mode = leaderSelectorPtr()->getActiveMode(); + + using fps_approx_ops::operator!=; + LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps, + "Mismatch in render frame rates. Selector: %s, Scheduler: %s", + to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str()); + + ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), + to_string(mode.modePtr->getFps()).c_str()); + + const auto divisor = RefreshRateSelector::getFrameRateDivisor(mode.modePtr->getFps(), mode.fps); + LOG_ALWAYS_FATAL_IF(divisor == 0, "%s <> %s -- not divisors", to_string(mode.fps).c_str(), + to_string(mode.fps).c_str()); + + mVsyncSchedule->getTracker().setDivisor(static_cast<unsigned>(divisor)); +} + void Scheduler::resync() { static constexpr nsecs_t kIgnoreDelay = ms2ns(750); @@ -416,10 +420,7 @@ void Scheduler::resync() { const nsecs_t last = mLastResyncTime.exchange(now); if (now - last > kIgnoreDelay) { - const auto refreshRate = [&] { - std::scoped_lock lock(mRefreshRateSelectorLock); - return mRefreshRateSelector->getActiveModePtr()->getFps(); - }(); + const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps(); resyncToHardwareVsync(false, refreshRate); } } @@ -478,12 +479,9 @@ void Scheduler::deregisterLayer(Layer* layer) { void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) { - { - std::scoped_lock lock(mRefreshRateSelectorLock); - if (!mRefreshRateSelector->canSwitch()) return; + if (leaderSelectorPtr()->canSwitch()) { + mLayerHistory.record(layer, presentTime, systemTime(), updateType); } - - mLayerHistory.record(layer, presentTime, systemTime(), updateType); } void Scheduler::setModeChangePending(bool pending) { @@ -496,7 +494,7 @@ void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) { } void Scheduler::chooseRefreshRateForContent() { - const auto selectorPtr = holdRefreshRateSelector(); + const auto selectorPtr = leaderSelectorPtr(); if (!selectorPtr->canSwitch()) return; ATRACE_CALL(); @@ -506,16 +504,13 @@ void Scheduler::chooseRefreshRateForContent() { } void Scheduler::resetIdleTimer() { - std::scoped_lock lock(mRefreshRateSelectorLock); - mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false); + leaderSelectorPtr()->resetIdleTimer(); } void Scheduler::onTouchHint() { if (mTouchTimer) { mTouchTimer->reset(); - - std::scoped_lock lock(mRefreshRateSelectorLock); - mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true); + leaderSelectorPtr()->resetKernelIdleTimer(); } } @@ -535,30 +530,12 @@ void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) { mLayerHistory.clear(); } -void Scheduler::bindIdleTimer(RefreshRateSelector& selector) { - selector.setIdleTimerCallbacks( - {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, - .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, - .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, - .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); - - selector.startIdleTimer(); -} - -void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) { - selector.stopIdleTimer(); - selector.clearIdleTimerCallbacks(); -} - void Scheduler::kernelIdleTimerCallback(TimerState state) { ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state)); // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate // magic number - const Fps refreshRate = [&] { - std::scoped_lock lock(mRefreshRateSelectorLock); - return mRefreshRateSelector->getActiveModePtr()->getFps(); - }(); + const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps(); constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz; using namespace fps_approx_ops; @@ -614,7 +591,11 @@ void Scheduler::dump(utils::Dumper& dumper) const { } { utils::Dumper::Section section(dumper, "Policy"sv); - + { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + dumper.dump("leaderDisplayId"sv, mLeaderDisplayId); + } dumper.dump("layerHistory"sv, mLayerHistory.dump()); dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval)); dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval)); @@ -638,17 +619,44 @@ void Scheduler::dumpVsync(std::string& out) const { } bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { - // we always update mFrameRateOverridesByContent here - // supportsFrameRateOverridesByContent will be checked - // when getting FrameRateOverrides from mFrameRateOverrideMappings - if (!consideredSignals.idle) { - const auto frameRateOverrides = - holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements, - displayRefreshRate, - consideredSignals); - return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); + if (consideredSignals.idle) return false; + + const auto frameRateOverrides = + leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements, + displayRefreshRate, consideredSignals); + + // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying + // the FrameRateOverrideMappings rather than here. + return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); +} + +void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) { + // TODO(b/241286431): Choose the leader display. + mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first); + ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str()); + + if (const auto leaderPtr = leaderSelectorPtrLocked()) { + leaderPtr->setIdleTimerCallbacks( + {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, + .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, + .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, + .onExpired = + [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); + + leaderPtr->startIdleTimer(); } - return false; +} + +void Scheduler::demoteLeaderDisplay() { + // No need to lock for reads on kMainThreadContext. + if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) { + leaderPtr->stopIdleTimer(); + leaderPtr->clearIdleTimerCallbacks(); + } + + // Clear state that depends on the leader's RefreshRateSelector. + std::scoped_lock lock(mPolicyLock); + mPolicy = {}; } template <typename S, typename T> @@ -660,36 +668,41 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals bool frameRateOverridesChanged; { - std::lock_guard<std::mutex> lock(mPolicyLock); + std::scoped_lock lock(mPolicyLock); auto& currentState = mPolicy.*statePtr; if (currentState == newState) return {}; currentState = std::forward<T>(newState); - auto modeChoices = chooseDisplayModes(); - - // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest to go - // through. Fix this by tracking per-display Scheduler::Policy and timers. - DisplayModePtr modePtr; - std::tie(modePtr, consideredSignals) = - modeChoices.get(*mLeaderDisplayId) - .transform([](const DisplayModeChoice& choice) { - return std::make_pair(choice.modePtr, choice.consideredSignals); - }) - .value(); + DisplayModeChoiceMap modeChoices; + ftl::Optional<FrameRateMode> modeOpt; + { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + + modeChoices = chooseDisplayModes(); + + // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest + // to go through. Fix this by tracking per-display Scheduler::Policy and timers. + std::tie(modeOpt, consideredSignals) = + modeChoices.get(*mLeaderDisplayId) + .transform([](const DisplayModeChoice& choice) { + return std::make_pair(choice.mode, choice.consideredSignals); + }) + .value(); + } modeRequests.reserve(modeChoices.size()); for (auto& [id, choice] : modeChoices) { modeRequests.emplace_back( - display::DisplayModeRequest{.modePtr = - ftl::as_non_null(std::move(choice.modePtr)), + display::DisplayModeRequest{.mode = std::move(choice.mode), .emitEvent = !choice.consideredSignals.idle}); } - frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modePtr->getFps()); + frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps); - if (mPolicy.mode != modePtr) { - mPolicy.mode = modePtr; + if (mPolicy.modeOpt != modeOpt) { + mPolicy.modeOpt = modeOpt; refreshRateChanged = true; } else { // We don't need to change the display mode, but we might need to send an event @@ -711,7 +724,7 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { ATRACE_CALL(); - using RankedRefreshRates = RefreshRateSelector::RankedRefreshRates; + using RankedRefreshRates = RefreshRateSelector::RankedFrameRates; display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking; // Tallies the score of a refresh rate across `displayCount` displays. @@ -729,11 +742,11 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { const auto globalSignals = makeGlobalSignals(); for (const auto& [id, selectorPtr] : mRefreshRateSelectors) { - auto rankedRefreshRates = - selectorPtr->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals); + auto rankedFrameRates = + selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals); - for (const auto& [modePtr, score] : rankedRefreshRates.ranking) { - const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score); + for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) { + const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score); if (!inserted) { auto& tally = it->second; @@ -742,7 +755,7 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { } } - perDisplayRanking.push_back(std::move(rankedRefreshRates)); + perDisplayRanking.push_back(std::move(rankedFrameRates)); } auto maxScoreIt = refreshRateTallies.cbegin(); @@ -775,16 +788,16 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { for (auto& [ranking, signals] : perDisplayRanking) { if (!chosenFps) { - auto& [modePtr, _] = ranking.front(); - modeChoices.try_emplace(modePtr->getPhysicalDisplayId(), - DisplayModeChoice{std::move(modePtr), signals}); + const auto& [frameRateMode, _] = ranking.front(); + modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(), + DisplayModeChoice{frameRateMode, signals}); continue; } - for (auto& [modePtr, _] : ranking) { - if (modePtr->getFps() == *chosenFps) { - modeChoices.try_emplace(modePtr->getPhysicalDisplayId(), - DisplayModeChoice{std::move(modePtr), signals}); + for (auto& [frameRateMode, _] : ranking) { + if (frameRateMode.fps == *chosenFps) { + modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(), + DisplayModeChoice{frameRateMode, signals}); break; } } @@ -802,18 +815,18 @@ GlobalSignals Scheduler::makeGlobalSignals() const { .powerOnImminent = powerOnImminent}; } -DisplayModePtr Scheduler::getPreferredDisplayMode() { +ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() { std::lock_guard<std::mutex> lock(mPolicyLock); // Make sure the stored mode is up to date. - if (mPolicy.mode) { + if (mPolicy.modeOpt) { const auto ranking = - holdRefreshRateSelector() - ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals()) + leaderSelectorPtr() + ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals()) .ranking; - mPolicy.mode = ranking.front().modePtr; + mPolicy.modeOpt = ranking.front().frameRateMode; } - return mPolicy.mode; + return mPolicy.modeOpt; } void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 04f3b69b98..cf2ffb8cdd 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -22,7 +22,6 @@ #include <future> #include <memory> #include <mutex> -#include <optional> #include <unordered_map> #include <utility> @@ -33,6 +32,8 @@ #include <ui/GraphicTypes.h> #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" +#include <ftl/fake_guard.h> +#include <ftl/optional.h> #include <scheduler/Features.h> #include <scheduler/Time.h> #include <ui/DisplayId.h> @@ -108,12 +109,15 @@ public: void startTimers(); + // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}. + void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); + using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>; - void setRefreshRateSelector(RefreshRateSelectorPtr) REQUIRES(kMainThreadContext) - EXCLUDES(mRefreshRateSelectorLock); - void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr); - void unregisterDisplay(PhysicalDisplayId); + void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); + void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); void run(); @@ -145,8 +149,8 @@ public: sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock); - void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr); + void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock); + void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); @@ -157,6 +161,9 @@ public: void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); + // Sets the render rate for the scheduler to run at. + void setRenderRate(Fps); + void enableHardwareVsync(); void disableHardwareVsync(bool makeUnavailable); @@ -165,7 +172,7 @@ public: // Otherwise, if hardware vsync is not already enabled then this method will // no-op. void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate); - void resync() EXCLUDES(mRefreshRateSelectorLock); + void resync() EXCLUDES(mDisplayLock); void forceNextResync() { mLastResyncTime = 0; } // Passes a vsync sample to VsyncController. periodFlushed will be true if @@ -176,14 +183,14 @@ public: // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); - void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) - EXCLUDES(mRefreshRateSelectorLock); + void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType) + EXCLUDES(mDisplayLock); void setModeChangePending(bool pending); void setDefaultFrameRateCompatibility(Layer*); void deregisterLayer(Layer*); // Detects content using layer history, and selects a matching refresh rate. - void chooseRefreshRateForContent() EXCLUDES(mRefreshRateSelectorLock); + void chooseRefreshRateForContent() EXCLUDES(mDisplayLock); void resetIdleTimer(); @@ -203,7 +210,7 @@ public: void dumpVsync(std::string&) const; // Get the appropriate refresh for current conditions. - DisplayModePtr getPreferredDisplayMode(); + ftl::Optional<FrameRateMode> getPreferredDisplayMode(); // Notifies the scheduler about a refresh rate timeline change. void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline); @@ -228,11 +235,10 @@ public: void setGameModeRefreshRateForUid(FrameRateOverride); // Retrieves the overridden refresh rate for a given uid. - std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateSelectorLock); + std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock); - nsecs_t getVsyncPeriodFromRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) { - std::scoped_lock lock(mRefreshRateSelectorLock); - return mRefreshRateSelector->getActiveModePtr()->getFps().getPeriodNsecs(); + nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) { + return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs(); } // Returns the framerate of the layer with the given sequence ID @@ -255,21 +261,23 @@ private: sp<EventThreadConnection> createConnectionInternal( EventThread*, EventRegistrationFlags eventRegistration = {}); - void bindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext, mRefreshRateSelectorLock); - - // Blocks until the timer thread exits. `mRefreshRateSelectorLock` must not be locked by the - // caller on the main thread to avoid deadlock, since the timer thread locks it before exit. - static void unbindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext) - EXCLUDES(mRefreshRateSelectorLock); - // Update feature state machine to given state when corresponding timer resets or expires. - void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock); + void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock); void idleTimerCallback(TimerState); void touchTimerCallback(TimerState); void displayPowerTimerCallback(TimerState); void setVsyncPeriod(nsecs_t period); + // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new + // `mLeaderDisplayId` is never `std::nullopt`. + void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt) + REQUIRES(kMainThreadContext, mDisplayLock); + + // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the + // caller on the main thread to avoid deadlock, since the timer thread locks it before exit. + void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock); + struct Policy; // Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode @@ -278,41 +286,38 @@ private: GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock); struct DisplayModeChoice { - DisplayModeChoice(DisplayModePtr modePtr, GlobalSignals consideredSignals) - : modePtr(std::move(modePtr)), consideredSignals(consideredSignals) {} + DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals) + : mode(std::move(mode)), consideredSignals(consideredSignals) {} - DisplayModePtr modePtr; + FrameRateMode mode; GlobalSignals consideredSignals; bool operator==(const DisplayModeChoice& other) const { - return modePtr == other.modePtr && consideredSignals == other.consideredSignals; + return mode == other.mode && consideredSignals == other.consideredSignals; } // For tests. friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) { - return stream << '{' << to_string(*choice.modePtr) << " considering " + return stream << '{' << to_string(*choice.mode.modePtr) << " considering " << choice.consideredSignals.toString().c_str() << '}'; } }; using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>; - DisplayModeChoiceMap chooseDisplayModes() const REQUIRES(mPolicyLock); + + // See mDisplayLock for thread safety. + DisplayModeChoiceMap chooseDisplayModes() const + REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext); GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock); bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock); - void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateSelectorLock); + void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock); - android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const - EXCLUDES(mRefreshRateSelectorLock); + android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const; android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const; - RefreshRateSelectorPtr holdRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) { - std::scoped_lock lock(mRefreshRateSelectorLock); - return mRefreshRateSelector; - } - // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { sp<EventThreadConnection> connection; @@ -342,10 +347,34 @@ private: ISchedulerCallback& mSchedulerCallback; + // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; - display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors; - std::optional<PhysicalDisplayId> mLeaderDisplayId; + // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so + // must lock for writes but not reads. See also mPolicyLock for locking order. + mutable std::mutex mDisplayLock; + + display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors + GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext); + + ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock) + GUARDED_BY(kMainThreadContext); + + RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) { + std::scoped_lock lock(mDisplayLock); + return leaderSelectorPtrLocked(); + } + + RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) { + ftl::FakeGuard guard(kMainThreadContext); + const RefreshRateSelectorPtr noLeader; + return mLeaderDisplayId + .and_then([this](PhysicalDisplayId leaderId) + REQUIRES(mDisplayLock, kMainThreadContext) { + return mRefreshRateSelectors.get(leaderId); + }) + .value_or(std::cref(noLeader)); + } struct Policy { // Policy for choosing the display mode. @@ -356,21 +385,17 @@ private: hal::PowerMode displayPowerMode = hal::PowerMode::ON; // Chosen display mode. - DisplayModePtr mode; + ftl::Optional<FrameRateMode> modeOpt; struct ModeChangedParams { ConnectionHandle handle; - DisplayModePtr mode; + FrameRateMode mode; }; // Parameters for latest dispatch of mode change event. std::optional<ModeChangedParams> cachedModeChangedParams; } mPolicy GUARDED_BY(mPolicyLock); - // TODO(b/255635821): Remove this by instead looking up the `mLeaderDisplayId` selector. - mutable std::mutex mRefreshRateSelectorLock; - RefreshRateSelectorPtr mRefreshRateSelector GUARDED_BY(mRefreshRateSelectorLock); - std::mutex mVsyncTimelineLock; std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline GUARDED_BY(mVsyncTimelineLock); diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 0ad42364a2..ed4d25e49b 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -253,7 +253,13 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { std::lock_guard lock(mMutex); - return nextAnticipatedVSyncTimeFromLocked(timePoint); + + // TODO(b/246164114): This implementation is not efficient at all. Refactor. + nsecs_t nextVsync = nextAnticipatedVSyncTimeFromLocked(timePoint); + while (!isVSyncInPhaseLocked(nextVsync, mDivisor)) { + nextVsync = nextAnticipatedVSyncTimeFromLocked(nextVsync + 1); + } + return nextVsync; } /* @@ -265,6 +271,13 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { * isVSyncInPhase(50.0, 30) = true */ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { + std::lock_guard lock(mMutex); + const auto divisor = + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); + return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor)); +} + +bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const { struct VsyncError { nsecs_t vsyncTimestamp; float error; @@ -272,9 +285,6 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { bool operator<(const VsyncError& other) const { return error < other.error; } }; - std::lock_guard lock(mMutex); - const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); if (divisor <= 1 || timePoint == 0) { return true; } @@ -312,6 +322,12 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2; } +void VSyncPredictor::setDivisor(unsigned divisor) { + ALOGV("%s: %d", __func__, divisor); + std::lock_guard lock(mMutex); + mDivisor = divisor; +} + VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { std::lock_guard lock(mMutex); const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 3181102663..4a3ba67419 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -67,6 +67,8 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); + void setDivisor(unsigned divisor) final EXCLUDES(mMutex); + void dump(std::string& result) const final EXCLUDES(mMutex); private: @@ -89,6 +91,8 @@ private: nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); + bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex); + nsecs_t mIdealPeriod GUARDED_BY(mMutex); std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); @@ -100,6 +104,8 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); + + unsigned mDivisor GUARDED_BY(mMutex) = 1; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 76315d2b5b..8d1629faae 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -79,6 +79,17 @@ public: */ virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; + /* + * Sets a divisor on the rate (which is a multiplier of the period). + * The tracker will continue to track the vsync timeline and expect it + * to match the current period, however, nextAnticipatedVSyncTimeFrom will + * return vsyncs according to the divisor set. Setting a divisor is useful + * when a display is running at 120Hz but the render frame rate is 60Hz. + * + * \param [in] divisor The rate divisor the tracker should operate at. + */ + virtual void setDivisor(unsigned divisor) = 0; + virtual void dump(std::string& result) const = 0; protected: diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index 31b1d6901c..5522ff80c8 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h @@ -66,6 +66,7 @@ struct FpsRange { Fps max = Fps::fromValue(std::numeric_limits<float>::max()); bool includes(Fps) const; + bool includes(FpsRange) const; }; struct FpsRanges { @@ -75,6 +76,8 @@ struct FpsRanges { // the range of frame rates that refers to the render rate, which is // the rate that frames are swapped. FpsRange render; + + bool valid() const; }; static_assert(std::is_trivially_copyable_v<Fps>); @@ -159,6 +162,16 @@ inline bool FpsRange::includes(Fps fps) const { return min <= fps && fps <= max; } +inline bool FpsRange::includes(FpsRange range) const { + using namespace fps_approx_ops; + return min <= range.min && max >= range.max; +} + +inline bool FpsRanges::valid() const { + using fps_approx_ops::operator>=; + return physical.max >= render.max; +} + struct FpsApproxEqual { bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); } }; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h new file mode 100644 index 0000000000..db38ebe658 --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h @@ -0,0 +1,42 @@ +/* + * Copyright 2022 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 <ftl/non_null.h> +#include <scheduler/Fps.h> + +// TODO(b/241285191): Pull this to <ui/DisplayMode.h> +#include "DisplayHardware/DisplayMode.h" + +namespace android::scheduler { + +struct FrameRateMode { + Fps fps; // The render frame rate, which is a divisor of modePtr->getFps(). + ftl::NonNull<DisplayModePtr> modePtr; + + bool operator==(const FrameRateMode& other) const { + return isApproxEqual(fps, other.fps) && modePtr == other.modePtr; + } + + bool operator!=(const FrameRateMode& other) const { return !(*this == other); } +}; + +inline std::string to_string(const FrameRateMode& mode) { + return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")"; +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 8f93ba40ee..37b3218138 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -24,10 +24,16 @@ namespace android { -std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs&& args) { +std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) { std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated< - ScreenCaptureOutput, compositionengine::CompositionEngine, - ScreenCaptureOutputArgs&&>(args.compositionEngine, std::move(args)); + ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&, + std::unordered_set<compositionengine::LayerFE*>, + const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine, + args.renderArea, + std::move( + args.filterForScreenshot), + args.colorProfile, + args.regionSampling); output->editState().isSecure = args.renderArea.isSecure(); output->setCompositionEnabled(true); output->setLayerFilter({args.layerStack}); @@ -50,11 +56,14 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp return output; } -ScreenCaptureOutput::ScreenCaptureOutput(ScreenCaptureOutputArgs&& args) - : mRenderArea(args.renderArea), - mFilterForScreenshot(std::move(args.filterForScreenshot)), - mColorProfile(args.colorProfile), - mRegionSampling(args.regionSampling) {} +ScreenCaptureOutput::ScreenCaptureOutput( + const RenderArea& renderArea, + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot, + const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling) + : mRenderArea(renderArea), + mFilterForScreenshot(std::move(filterForScreenshot)), + mColorProfile(colorProfile), + mRegionSampling(regionSampling) {} void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) { auto& outputState = editState(); diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h index 61b5ddb1bb..5dffc1d530 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.h +++ b/services/surfaceflinger/ScreenCaptureOutput.h @@ -43,7 +43,10 @@ struct ScreenCaptureOutputArgs { // SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay. class ScreenCaptureOutput : public compositionengine::impl::Output { public: - ScreenCaptureOutput(ScreenCaptureOutputArgs&&); + ScreenCaptureOutput(const RenderArea& renderArea, + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot, + const compositionengine::Output::ColorProfile& colorProfile, + bool regionSampling); void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; @@ -64,6 +67,6 @@ private: const bool mRegionSampling; }; -std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs&&); +std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs); } // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index f0d6aebede..365ffb7732 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -107,6 +107,7 @@ #include <optional> #include <type_traits> #include <unordered_map> +#include <vector> #include <ui/DisplayIdentification.h> #include "BackgroundExecutor.h" @@ -179,6 +180,7 @@ using CompositionStrategyPredictionState = android::compositionengine::impl:: using base::StringAppendF; using display::PhysicalDisplay; using display::PhysicalDisplays; +using frontend::TransactionHandler; using gui::DisplayInfo; using gui::GameMode; using gui::IDisplayEventConnection; @@ -373,7 +375,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI int debugDdms = atoi(value); ALOGI_IF(debugDdms, "DDMS debugging not supported"); - property_get("debug.sf.enable_gl_backpressure", value, "0"); + property_get("debug.sf.enable_gl_backpressure", value, "1"); mPropagateBackpressureClientComposition = atoi(value); ALOGI_IF(mPropagateBackpressureClientComposition, "Enabling backpressure propagation for Client Composition"); @@ -735,6 +737,10 @@ chooseRenderEngineTypeViaSysProp() { return renderengine::RenderEngine::RenderEngineType::SKIA_GL; } else if (strcmp(prop, "skiaglthreaded") == 0) { return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED; + } else if (strcmp(prop, "skiavk") == 0) { + return renderengine::RenderEngine::RenderEngineType::SKIA_VK; + } else if (strcmp(prop, "skiavkthreaded") == 0) { + return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED; } else { ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop); return {}; @@ -840,8 +846,6 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { } } - onActiveDisplaySizeChanged(display); - // Inform native graphics APIs whether the present timestamp is supported: const bool presentFenceReliable = @@ -1035,7 +1039,8 @@ status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, const PhysicalDisplayId displayId = snapshot.displayId(); - info->activeDisplayModeId = display->refreshRateSelector().getActiveModePtr()->getId().value(); + info->activeDisplayModeId = + display->refreshRateSelector().getActiveMode().modePtr->getId().value(); info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = display->getHdrCapabilities(); @@ -1072,26 +1077,39 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* ou void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) { ATRACE_CALL(); - auto display = getDisplayDeviceLocked(request.modePtr->getPhysicalDisplayId()); + auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId()); if (!display) { ALOGW("%s: display is no longer valid", __func__); return; } - const Fps refreshRate = request.modePtr->getFps(); + const Fps renderFps = request.mode.fps; + const Fps displayFps = request.mode.modePtr->getFps(); - if (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) { - scheduleComposite(FrameHint::kNone); + switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) { + case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch: + scheduleComposite(FrameHint::kNone); - // Start receiving vsync samples now, so that we can detect a period - // switch. - mScheduler->resyncToHardwareVsync(true, refreshRate); - // As we called to set period, we will call to onRefreshRateChangeCompleted once - // VsyncController model is locked. - modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); + // Start receiving vsync samples now, so that we can detect a period + // switch. + mScheduler->resyncToHardwareVsync(true, displayFps); + // As we called to set period, we will call to onRefreshRateChangeCompleted once + // VsyncController model is locked. + modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); - updatePhaseConfiguration(refreshRate); - mScheduler->setModeChangePending(true); + updatePhaseConfiguration(renderFps); + mScheduler->setModeChangePending(true); + break; + case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch: + mScheduler->setRenderRate(renderFps); + updatePhaseConfiguration(renderFps); + mRefreshRateStats->setRefreshRate(renderFps); + + // TODO(b/259740021): send event to display manager about + // the render rate change + break; + case DisplayDevice::DesiredActiveModeAction::None: + break; } } @@ -1153,18 +1171,19 @@ void SurfaceFlinger::updateInternalStateWithChangedMode() { } const auto upcomingModeInfo = display->getUpcomingActiveMode(); - if (!upcomingModeInfo.mode) { + if (!upcomingModeInfo.modeOpt) { // There is no pending mode change. This can happen if the active // display changed and the mode change happened on a different display. return; } - if (display->getActiveMode().getResolution() != upcomingModeInfo.mode->getResolution()) { + if (display->getActiveMode().modePtr->getResolution() != + upcomingModeInfo.modeOpt->modePtr->getResolution()) { auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken()); // We need to generate new sequenceId in order to recreate the display (and this // way the framebuffer). state.sequenceId = DisplayDeviceState{}.sequenceId; - state.physical->activeMode = upcomingModeInfo.mode; + state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get(); processDisplayChangesLocked(); // processDisplayChangesLocked will update all necessary components so we're done here. @@ -1175,15 +1194,17 @@ void SurfaceFlinger::updateInternalStateWithChangedMode() { .transform(&PhysicalDisplay::snapshotRef) .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { FTL_FAKE_GUARD(kMainThreadContext, - display->setActiveMode(upcomingModeInfo.mode->getId(), snapshot)); + display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(), + upcomingModeInfo.modeOpt->modePtr->getFps(), + upcomingModeInfo.modeOpt->fps)); })); - const Fps refreshRate = upcomingModeInfo.mode->getFps(); + const Fps refreshRate = upcomingModeInfo.modeOpt->fps; mRefreshRateStats->setRefreshRate(refreshRate); updatePhaseConfiguration(refreshRate); if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) { - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode); + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt); } } @@ -1195,10 +1216,12 @@ void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& displa } void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) { - const auto refreshRate = display->getDesiredActiveMode()->mode->getFps(); + const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps(); + const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps; clearDesiredActiveModeState(display); - mScheduler->resyncToHardwareVsync(true, refreshRate); - updatePhaseConfiguration(refreshRate); + mScheduler->resyncToHardwareVsync(true, displayFps); + mScheduler->setRenderRate(renderFps); + updatePhaseConfiguration(renderFps); } void SurfaceFlinger::setActiveModeInHwcIfNeeded() { @@ -1229,13 +1252,10 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { continue; } - const auto desiredModeId = desiredActiveMode->mode->getId(); - const auto refreshRateOpt = - snapshot.displayModes() - .get(desiredModeId) - .transform([](const DisplayModePtr& mode) { return mode->getFps(); }); + const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId(); + const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId); - if (!refreshRateOpt) { + if (!displayModePtrOpt) { ALOGW("Desired display mode is no longer supported. Mode ID = %d", desiredModeId.value()); clearDesiredActiveModeState(display); @@ -1243,9 +1263,10 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { } ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(), - to_string(*refreshRateOpt).c_str(), to_string(display->getId()).c_str()); + to_string(displayModePtrOpt->get()->getFps()).c_str(), + to_string(display->getId()).c_str()); - if (display->getActiveMode().getId() == desiredModeId) { + if (display->getActiveMode() == desiredActiveMode->modeOpt) { // we are already in the requested mode, there is nothing left to do desiredActiveModeChangeDone(display); continue; @@ -1254,7 +1275,8 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { // Desired active mode was set, it is different than the mode currently in use, however // allowed modes might have changed by the time we process the refresh. // Make sure the desired mode is still allowed - const auto displayModeAllowed = display->refreshRateSelector().isModeAllowed(desiredModeId); + const auto displayModeAllowed = + display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt); if (!displayModeAllowed) { clearDesiredActiveModeState(display); continue; @@ -1295,8 +1317,7 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately); const auto desiredActiveMode = display->getDesiredActiveMode(); - if (desiredActiveMode && - display->getActiveMode().getId() == desiredActiveMode->mode->getId()) { + if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) { desiredActiveModeChangeDone(display); } } @@ -1388,7 +1409,26 @@ status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const { return NO_ERROR; } -status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* /*outProperties*/) const { +status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const { + const auto& aidlProperties = getHwComposer().getOverlaySupport(); + // convert aidl OverlayProperties to gui::OverlayProperties + outProperties->combinations.reserve(aidlProperties.combinations.size()); + for (const auto& combination : aidlProperties.combinations) { + std::vector<int32_t> pixelFormats; + pixelFormats.reserve(combination.pixelFormats.size()); + std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(), + std::back_inserter(pixelFormats), + [](const auto& val) { return static_cast<int32_t>(val); }); + std::vector<int32_t> dataspaces; + dataspaces.reserve(combination.dataspaces.size()); + std::transform(combination.dataspaces.cbegin(), combination.dataspaces.cend(), + std::back_inserter(dataspaces), + [](const auto& val) { return static_cast<int32_t>(val); }); + gui::OverlayProperties::SupportedBufferCombinations outCombination; + outCombination.pixelFormats = std::move(pixelFormats); + outCombination.dataspaces = std::move(dataspaces); + outProperties->combinations.emplace_back(outCombination); + } return NO_ERROR; } @@ -1800,7 +1840,7 @@ void SurfaceFlinger::scheduleCommit(FrameHint hint) { if (hint == FrameHint::kActive) { mScheduler->resetIdleTimer(); } - mPowerAdvisor->notifyDisplayUpdateImminent(); + mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset(); mScheduler->scheduleFrame(); } @@ -2046,7 +2086,7 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe activeDisplay->getPowerMode() == hal::PowerMode::ON; if (mPowerHintSessionEnabled) { const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get(); - const Period vsyncPeriod = Period::fromNs(display->getActiveMode().getVsyncPeriod()); + const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs()); mPowerAdvisor->setCommitStart(frameTime); mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime); @@ -2185,14 +2225,13 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty; - std::vector<sp<Layer>> layers; + std::vector<Layer*> layers; mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) { - auto strongLayer = sp<Layer>::fromExisting(layer); if (auto layerFE = layer->getCompositionEngineLayerFE()) { layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame); refreshArgs.layers.push_back(layerFE); - layers.push_back(std::move(strongLayer)); + layers.push_back(layer); } }); refreshArgs.blursAreExpensive = mBlursAreExpensive; @@ -2224,8 +2263,8 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) { std::vector<LayerSnapshotGuard> layerSnapshotGuards; - for (auto& layer : layers) { - layerSnapshotGuards.emplace_back(layer.get()); + for (Layer* layer : layers) { + layerSnapshotGuards.emplace_back(layer); } mCompositionEngine->present(refreshArgs); } @@ -2788,7 +2827,11 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( } if (sysprop::frame_rate_override_for_native_rates(true)) { - return Config::FrameRateOverride::EnabledForNativeRefreshRates; + return Config::FrameRateOverride::AppOverrideNativeRefreshRates; + } + + if (!base::GetBoolProperty("debug.sf.frame_rate_override_global"s, false)) { + return Config::FrameRateOverride::AppOverride; } return Config::FrameRateOverride::Enabled; @@ -2872,7 +2915,9 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( .transform(&PhysicalDisplay::snapshotRef) .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { FTL_FAKE_GUARD(kMainThreadContext, - display->setActiveMode(physical->activeMode->getId(), snapshot)); + display->setActiveMode(physical->activeMode->getId(), + physical->activeMode->getFps(), + physical->activeMode->getFps())); })); } @@ -2954,18 +2999,16 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, displaySurface, producer); if (mScheduler && !display->isVirtual()) { - auto selectorPtr = display->holdRefreshRateSelector(); - - // Display modes are reloaded on hotplug reconnect. - if (display->isPrimary()) { + const auto displayId = display->getPhysicalId(); + { // TODO(b/241285876): Annotate `processDisplayAdded` instead. ftl::FakeGuard guard(kMainThreadContext); - mScheduler->setRefreshRateSelector(selectorPtr); + + // For hotplug reconnect, renew the registration since display modes have been reloaded. + mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector()); } - const auto displayId = display->getPhysicalId(); - mScheduler->registerDisplay(displayId, std::move(selectorPtr)); - dispatchDisplayHotplugEvent(display->getPhysicalId(), true); + dispatchDisplayHotplugEvent(displayId, true); } mDisplays.try_emplace(displayToken, std::move(display)); @@ -3070,7 +3113,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) { mVsyncConfiguration->reset(); - const Fps refreshRate = activeDisplay->getActiveMode().getFps(); + const Fps refreshRate = activeDisplay->getActiveMode().fps; updatePhaseConfiguration(refreshRate); mRefreshRateStats->setRefreshRate(refreshRate); } @@ -3121,6 +3164,10 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded; if (displayTransactionNeeded) { processDisplayChangesLocked(); + mFrontEndDisplayInfos.clear(); + for (const auto& [_, display] : mDisplays) { + mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo()); + } } mForceTransactionDisplayChange = displayTransactionNeeded; @@ -3290,29 +3337,6 @@ void SurfaceFlinger::persistDisplayBrightness(bool needsComposite) { void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, std::vector<DisplayInfo>& outDisplayInfos) { - display::DisplayMap<ui::LayerStack, DisplayDevice::InputInfo> displayInputInfos; - - for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { - const auto layerStack = display->getLayerStack(); - const auto info = display->getInputInfo(); - - const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info); - if (emplaced) { - continue; - } - - // If the layer stack is mirrored on multiple displays, the first display that is configured - // to receive input takes precedence. - auto& otherInfo = it->second; - if (otherInfo.receivesInput) { - ALOGW_IF(display->receivesInput(), - "Multiple displays claim to accept input for the same layer stack: %u", - layerStack.id); - } else { - otherInfo = info; - } - } - static size_t sNumWindowInfos = 0; outWindowInfos.reserve(sNumWindowInfos); sNumWindowInfos = 0; @@ -3320,8 +3344,8 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (!layer->needsInputInfo()) return; - const auto opt = displayInputInfos.get(layer->getLayerStack()) - .transform([](const DisplayDevice::InputInfo& info) { + const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack()) + .transform([](const frontend::DisplayInfo& info) { return Layer::InputDisplayArgs{&info.transform, info.isSecure}; }); @@ -3330,8 +3354,8 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, sNumWindowInfos = outWindowInfos.size(); - outDisplayInfos.reserve(displayInputInfos.size()); - for (const auto& [_, info] : displayInputInfos) { + outDisplayInfos.reserve(mFrontEndDisplayInfos.size()); + for (const auto& [_, info] : mFrontEndDisplayInfos) { outDisplayInfos.push_back(info.info); } } @@ -3346,12 +3370,7 @@ void SurfaceFlinger::updateCursorAsync() { std::vector<LayerSnapshotGuard> layerSnapshotGuards; mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) { - auto strongLayer = sp<Layer>::fromExisting(layer); - const LayerSnapshot* snapshot = layer->getLayerSnapshot(); - if (!snapshot) { - LOG_ALWAYS_FATAL("Layer snapshot unexpectedly null"); - } - if (snapshot->compositionType == + if (layer->getLayerSnapshot()->compositionType == aidl::android::hardware::graphics::composer3::Composition::CURSOR) { layer->updateSnapshot(false /* updateGeometry */); layerSnapshotGuards.emplace_back(layer); @@ -3376,12 +3395,12 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); for (auto& request : modeRequests) { - const auto& modePtr = request.modePtr; + const auto& modePtr = request.mode.modePtr; const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId()); if (!display) continue; - if (display->refreshRateSelector().isModeAllowed(modePtr->getId())) { + if (display->refreshRateSelector().isModeAllowed(request.mode)) { setDesiredActiveMode(std::move(request)); } else { ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(), @@ -3402,8 +3421,8 @@ void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { LOG_ALWAYS_FATAL_IF(mScheduler); - const auto activeModePtr = display->refreshRateSelector().getActiveModePtr(); - const Fps activeRefreshRate = activeModePtr->getFps(); + const auto activeMode = display->refreshRateSelector().getActiveMode(); + const Fps activeRefreshRate = activeMode.fps; mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF); @@ -3424,9 +3443,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) { features |= Feature::kPresentFences; } - - auto selectorPtr = display->holdRefreshRateSelector(); - if (selectorPtr->kernelIdleTimerController()) { + if (display->refreshRateSelector().kernelIdleTimerController()) { features |= Feature::kKernelIdleTimer; } @@ -3434,8 +3451,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { static_cast<ISchedulerCallback&>(*this), features); mScheduler->createVsyncSchedule(features); - mScheduler->setRefreshRateSelector(selectorPtr); - mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr)); + mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); setVsyncEnabled(false); mScheduler->startTimers(); @@ -3458,14 +3474,6 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { sp<RegionSamplingThread>::make(*this, RegionSamplingThread::EnvironmentTimingTunables()); mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this); - // Dispatch a mode change request for the primary display on scheduler - // initialization, so that the EventThreads always contain a reference to a - // prior configuration. - // - // This is a bit hacky, but this avoids a back-pointer into the main SF - // classes from EventThread, and there should be no run-time binder cost - // anyway since there are no connected apps at this point. - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr); } void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) { @@ -3900,7 +3908,7 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s } status_t SurfaceFlinger::setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states, + const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, @@ -3932,7 +3940,29 @@ status_t SurfaceFlinger::setTransactionState( IPCThreadState* ipc = IPCThreadState::self(); const int originPid = ipc->getCallingPid(); const int originUid = ipc->getCallingUid(); - TransactionState state{frameTimelineInfo, states, + + std::vector<ResolvedComposerState> resolvedStates; + resolvedStates.reserve(states.size()); + for (auto& state : states) { + resolvedStates.emplace_back(std::move(state)); + auto& resolvedState = resolvedStates.back(); + if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() && + resolvedState.state.surface) { + sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface); + std::string layerName = (layer) ? + layer->getDebugName() : std::to_string(resolvedState.state.layerId); + resolvedState.externalTexture = + getExternalTextureFromBufferData(*resolvedState.state.bufferData, + layerName.c_str(), transactionId); + mBufferCountTracker.increment(resolvedState.state.surface->localBinder()); + if (layer) { + resolvedState.hwcBufferSlot = + layer->getHwcCacheSlot(resolvedState.state.bufferData->cachedBuffer); + } + } + } + + TransactionState state{frameTimelineInfo, resolvedStates, displays, flags, applyToken, inputWindowCommands, desiredPresentTime, isAutoTimestamp, @@ -3941,11 +3971,6 @@ status_t SurfaceFlinger::setTransactionState( listenerCallbacks, originPid, originUid, transactionId}; - // Check for incoming buffer updates and increment the pending buffer count. - state.traverseStatesWithBuffers([&](const layer_state_t& state) { - mBufferCountTracker.increment(state.surface->localBinder()); - }); - if (mTransactionTracing) { mTransactionTracing->addQueuedTransaction(state); } @@ -3964,7 +3989,7 @@ status_t SurfaceFlinger::setTransactionState( } bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo, - Vector<ComposerState>& states, + std::vector<ResolvedComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, bool isAutoTimestamp, @@ -3986,13 +4011,12 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin } uint32_t clientStateFlags = 0; - for (int i = 0; i < states.size(); i++) { - ComposerState& state = states.editItemAt(i); + for (auto& resolvedState : states) { clientStateFlags |= - setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp, - postTime, permissions, transactionId); - if ((flags & eAnimation) && state.state.surface) { - if (const auto layer = LayerHandle::getLayer(state.state.surface)) { + setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime, + isAutoTimestamp, postTime, permissions, transactionId); + if ((flags & eAnimation) && resolvedState.state.surface) { + if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) { using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; mScheduler->recordLayerHistory(layer.get(), isAutoTimestamp ? 0 : desiredPresentTime, @@ -4104,7 +4128,7 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermis } uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo, - ComposerState& composerState, + ResolvedComposerState& composerState, int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions, uint64_t transactionId) { @@ -4399,11 +4423,9 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } if (what & layer_state_t::eBufferChanged) { - std::shared_ptr<renderengine::ExternalTexture> buffer = - getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName(), - transactionId); - if (layer->setBuffer(buffer, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp, - dequeueBufferTimestamp, frameTimelineInfo)) { + if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime, + desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp, + frameTimelineInfo, composerState.hwcBufferSlot)) { flags |= eTraversalNeeded; } } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { @@ -4611,7 +4633,7 @@ void SurfaceFlinger::onInitializeDisplays() { LOG_ALWAYS_FATAL_IF(token == nullptr); // reset screen orientation and use primary layer stack - Vector<ComposerState> state; + std::vector<ResolvedComposerState> state; Vector<DisplayState> displays; DisplayState d; d.what = DisplayState::eDisplayProjectionChanged | @@ -4634,8 +4656,6 @@ void SurfaceFlinger::onInitializeDisplays() { {}, mPid, getuid(), transactionId); setPowerModeInternal(display, hal::PowerMode::ON); - - mActiveDisplayTransformHint = display->getTransformHint(); } void SurfaceFlinger::initializeDisplays() { @@ -4654,8 +4674,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: const auto displayId = display->getPhysicalId(); ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str()); - std::optional<hal::PowerMode> currentMode = display->getPowerMode(); - if (currentMode.has_value() && mode == *currentMode) { + const auto currentModeOpt = display->getPowerMode(); + if (currentModeOpt == mode) { return; } @@ -4672,8 +4692,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: display->setPowerMode(mode); - const auto refreshRate = display->refreshRateSelector().getActiveMode().getFps(); - if (*currentMode == hal::PowerMode::OFF) { + const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps(); + if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) { // Turn on the display if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) { onActiveDisplayChangedLocked(display); @@ -4703,7 +4723,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) { ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno)); } - if (isActiveDisplay && *currentMode != hal::PowerMode::DOZE_SUSPEND) { + if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) { mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } @@ -4717,7 +4737,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing getHwComposer().setPowerMode(displayId, mode); - if (isActiveDisplay && *currentMode == hal::PowerMode::DOZE_SUSPEND) { + if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) { ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); mVisibleRegionsDirty = true; scheduleRepaint(); @@ -5242,7 +5262,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp if (const auto display = getDefaultDisplayDeviceLocked()) { std::string fps, xDpi, yDpi; - if (const auto activeModePtr = display->refreshRateSelector().getActiveModePtr()) { + if (const auto activeModePtr = + display->refreshRateSelector().getActiveMode().modePtr.get()) { fps = to_string(activeModePtr->getFps()); const auto dpi = activeModePtr->getDpi(); @@ -5914,7 +5935,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { if (!updateOverlay) return; // Update the overlay on the main thread to avoid race conditions with - // RefreshRateSelector::getActiveMode. + // RefreshRateSelector::getActiveMode static_cast<void>(mScheduler->schedule([=] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { @@ -5925,7 +5946,8 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { const auto desiredActiveMode = display->getDesiredActiveMode(); const std::optional<DisplayModeId> desiredModeId = desiredActiveMode - ? std::make_optional(desiredActiveMode->mode->getId()) + ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId()) + : std::nullopt; const bool timerExpired = mKernelIdleTimerEnabled && expired; @@ -6397,8 +6419,10 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); if (!renderArea) { ALOGW("Skipping screen capture because of invalid render area."); - captureResults.fenceResult = base::unexpected(NO_MEMORY); - captureListener->onScreenCaptureCompleted(captureResults); + if (captureListener) { + captureResults.fenceResult = base::unexpected(NO_MEMORY); + captureListener->onScreenCaptureCompleted(captureResults); + } return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); } @@ -6484,7 +6508,6 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( layers.reserve(layerCount); std::unordered_set<compositionengine::LayerFE*> filterForScreenshot; traverseLayers([&](Layer* layer) { - auto strongLayer = sp<Layer>::fromExisting(layer); captureResults.capturedHdrLayers |= isHdrLayer(layer); // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are @@ -6618,11 +6641,11 @@ void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const } } -std::optional<ftl::NonNull<DisplayModePtr>> SurfaceFlinger::getPreferredDisplayMode( +ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode( PhysicalDisplayId displayId, DisplayModeId defaultModeId) const { if (const auto schedulerMode = mScheduler->getPreferredDisplayMode(); - schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) { - return ftl::as_non_null(schedulerMode); + schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) { + return schedulerMode; } return mPhysicalDisplays.get(displayId) @@ -6630,7 +6653,9 @@ std::optional<ftl::NonNull<DisplayModePtr>> SurfaceFlinger::getPreferredDisplayM .and_then([&](const display::DisplaySnapshot& snapshot) { return snapshot.displayModes().get(defaultModeId); }) - .transform(&ftl::as_non_null<const DisplayModePtr&>); + .transform([](const DisplayModePtr& modePtr) { + return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)}; + }); } status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( @@ -6654,19 +6679,22 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( case SetPolicyResult::Unchanged: return NO_ERROR; case SetPolicyResult::Changed: - break; + return applyRefreshRateSelectorPolicy(displayId, selector); } +} +status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( + PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) { const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy(); ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might // be depending in this callback. - if (const auto activeModePtr = selector.getActiveModePtr(); displayId == mActiveDisplayId) { - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr); + if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) { + mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); toggleKernelIdleTimer(); } else { - mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr); + mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); } auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode); @@ -6676,12 +6704,12 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( } auto preferredMode = std::move(*preferredModeOpt); - const auto preferredModeId = preferredMode->getId(); + const auto preferredModeId = preferredMode.modePtr->getId(); ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(), - to_string(preferredMode->getFps()).c_str()); + to_string(preferredMode.fps).c_str()); - if (!selector.isModeAllowed(preferredModeId)) { + if (!selector.isModeAllowed(preferredMode)) { ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value()); return INVALID_OPERATION; } @@ -6893,7 +6921,7 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui refreshRate = *frameRateOverride; } else if (!getHwComposer().isHeadless()) { if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) { - refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps(); + refreshRate = display->refreshRateSelector().getActiveMode().fps; } } @@ -6980,15 +7008,19 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activ } mActiveDisplayId = activeDisplay->getPhysicalId(); activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); + updateInternalDisplayVsyncLocked(activeDisplay); mScheduler->setModeChangePending(false); - mScheduler->setRefreshRateSelector(activeDisplay->holdRefreshRateSelector()); + mScheduler->setLeaderDisplay(mActiveDisplayId); + onActiveDisplaySizeChanged(activeDisplay); mActiveDisplayTransformHint = activeDisplay->getTransformHint(); - // Update the kernel timer for the current active display, since the policy - // for this display might have changed when it was not the active display. - toggleKernelIdleTimer(); + // The policy of the new active/leader display may have changed while it was inactive. In that + // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either + // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode, + // and the kernel idle timer of the newly active display must be toggled. + applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector()); } status_t SurfaceFlinger::addWindowInfosListener( @@ -7007,9 +7039,16 @@ std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextur BufferData& bufferData, const char* layerName, uint64_t transactionId) { if (bufferData.buffer && exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), bufferData.buffer->getHeight())) { - ALOGE("Attempted to create an ExternalTexture for layer %s that exceeds render target " - "size limit.", - layerName); + std::string errorMessage = + base::StringPrintf("Attempted to create an ExternalTexture with size (%u, %u) for " + "layer %s that exceeds render target size limit of %u.", + bufferData.buffer->getWidth(), bufferData.buffer->getHeight(), + layerName, static_cast<uint32_t>(mMaxRenderTargetSize)); + ALOGD("%s", errorMessage.c_str()); + if (bufferData.releaseBufferListener) { + bufferData.releaseBufferListener->onTransactionQueueStalled( + String8(errorMessage.c_str())); + } return nullptr; } @@ -7022,9 +7061,12 @@ std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextur } if (result.error() == ClientCache::AddError::CacheFull) { - mTransactionHandler - .onTransactionQueueStalled(transactionId, bufferData.releaseBufferListener, - "Buffer processing hung due to full buffer cache"); + ALOGE("Attempted to create an ExternalTexture for layer %s but CacheFull", layerName); + + if (bufferData.releaseBufferListener) { + bufferData.releaseBufferListener->onTransactionQueueStalled( + String8("Buffer processing hung due to full buffer cache")); + } } return nullptr; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 1918913419..e3217a3228 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -66,6 +66,7 @@ #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FlagManager.h" +#include "FrontEnd/DisplayInfo.h" #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/TransactionHandler.h" #include "LayerVector.h" @@ -120,6 +121,7 @@ class FrameTracer; class ScreenCapturer; class WindowInfosListenerInvoker; +using frontend::TransactionHandler; using gui::CaptureArgs; using gui::DisplayCaptureArgs; using gui::IRegionSamplingListener; @@ -464,8 +466,7 @@ private: typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)> void modulateVsync(Handler handler, Args... args) { if (const auto config = (*mVsyncModulator.*handler)(args...)) { - const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateSelector(); - setVsyncConfig(*config, vsyncPeriod); + setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod()); } } @@ -490,9 +491,8 @@ private: sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const; status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, - const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, + Vector<ComposerState>& state, const Vector<DisplayState>& displays, + uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, @@ -660,13 +660,18 @@ private: // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that // display. Falls back to the display's defaultModeId otherwise. - std::optional<ftl::NonNull<DisplayModePtr>> getPreferredDisplayMode( + ftl::Optional<scheduler::FrameRateMode> getPreferredDisplayMode( PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock); status_t setDesiredDisplayModeSpecsInternal( const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&) EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); + // TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter. + status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId, + const scheduler::RefreshRateSelector&) + REQUIRES(mStateLock, kMainThreadContext); + void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock, kMainThreadContext); @@ -693,7 +698,8 @@ private: /* * Transactions */ - bool applyTransactionState(const FrameTimelineInfo& info, Vector<ComposerState>& state, + bool applyTransactionState(const FrameTimelineInfo& info, + std::vector<ResolvedComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, bool isAutoTimestamp, @@ -714,7 +720,7 @@ private: const TransactionHandler::TransactionFlushState& flushState) REQUIRES(kMainThreadContext); - uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&, + uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&, int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions, uint64_t transactionId) REQUIRES(mStateLock); @@ -928,7 +934,8 @@ private: const sp<compositionengine::DisplaySurface>& displaySurface, const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock); void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext); - void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock); + void processDisplayRemoved(const wp<IBinder>& displayToken) + REQUIRES(mStateLock, kMainThreadContext); void processDisplayChanged(const wp<IBinder>& displayToken, const DisplayDeviceState& currentState, const DisplayDeviceState& drawingState) @@ -1366,6 +1373,7 @@ private: } mPowerHintSessionMode; TransactionHandler mTransactionHandler; + display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos; }; class SurfaceComposerAIDL : public gui::BnSurfaceComposer { diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index e5a9dd47c3..e860d88cd1 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -68,6 +68,8 @@ SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(GameMode gameMode) { return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE; case GameMode::Battery: return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY; + case GameMode::Custom: + return SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM; default: return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED; } diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto index e45757ddfd..d4d444e85e 100644 --- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto +++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto @@ -173,6 +173,7 @@ message SurfaceflingerStatsLayerInfo { GAME_MODE_STANDARD = 2; GAME_MODE_PERFORMANCE = 3; GAME_MODE_BATTERY = 4; + GAME_MODE_CUSTOM = 5; } // Game mode that the layer was running at. Used to track user engagement diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index 3418c82f9e..2f464873ea 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -310,10 +310,10 @@ TransactionState TransactionProtoParser::fromProto(const proto::TransactionState int32_t layerCount = proto.layer_changes_size(); t.states.reserve(static_cast<size_t>(layerCount)); for (int i = 0; i < layerCount; i++) { - ComposerState s; + ResolvedComposerState s; s.state.what = 0; fromProto(proto.layer_changes(i), s.state); - t.states.add(s); + t.states.emplace_back(s); } int32_t displayCount = proto.display_changes_size(); diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index 25fdd26ef1..f1a6c0e2fa 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -240,13 +240,7 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, for (int j = 0; j < entry.transactions_size(); j++) { // apply transactions TransactionState transaction = parser.fromProto(entry.transactions(j)); - mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, - transaction.displays, transaction.flags, - transaction.applyToken, transaction.inputWindowCommands, - transaction.desiredPresentTime, - transaction.isAutoTimestamp, {}, - transaction.hasListenerCallbacks, - transaction.listenerCallbacks, transaction.id); + mFlinger.setTransactionStateInternal(transaction); } const auto frameTime = TimePoint::fromNs(entry.elapsed_realtime_nanos()); diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h index 3cbfe811ea..7bde2c1e23 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/TransactionState.h @@ -20,17 +20,27 @@ #include <memory> #include <mutex> #include <vector> +#include "renderengine/ExternalTexture.h" #include <gui/LayerState.h> #include <system/window.h> namespace android { +// Extends the client side composer state by resolving buffer cache ids. +class ResolvedComposerState : public ComposerState { +public: + ResolvedComposerState() = default; + ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); } + std::shared_ptr<renderengine::ExternalTexture> externalTexture; + int hwcBufferSlot = 0; +}; + struct TransactionState { TransactionState() = default; TransactionState(const FrameTimelineInfo& frameTimelineInfo, - const Vector<ComposerState>& composerStates, + std::vector<ResolvedComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp, @@ -38,7 +48,7 @@ struct TransactionState { bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks, int originPid, int originUid, uint64_t transactionId) : frameTimelineInfo(frameTimelineInfo), - states(composerStates), + states(std::move(composerStates)), displays(displayStates), flags(transactionFlags), applyToken(applyToken), @@ -57,18 +67,20 @@ struct TransactionState { // Invokes `void(const layer_state_t&)` visitor for matching layers. template <typename Visitor> void traverseStatesWithBuffers(Visitor&& visitor) const { - for (const auto& [state] : states) { - if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) { - visitor(state); + for (const auto& state : states) { + if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && + state.state.surface) { + visitor(state.state); } } } template <typename Visitor> void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) const { - for (const auto& [state] : states) { - if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) { - if (!visitor(state)) return; + for (const auto& state : states) { + if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && + state.state.surface) { + if (!visitor(state.state)) return; } } } @@ -79,8 +91,8 @@ struct TransactionState { bool isFrameActive() const { if (!displays.empty()) return true; - for (const auto& [state] : states) { - if (state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) { + for (const auto& state : states) { + if (state.state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) { return true; } } @@ -89,7 +101,7 @@ struct TransactionState { } FrameTimelineInfo frameTimelineInfo; - Vector<ComposerState> states; + std::vector<ResolvedComposerState> states; Vector<DisplayState> displays; uint32_t flags; sp<IBinder> applyToken; diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 9ba9b90b82..dbfce78283 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -230,7 +230,9 @@ public: ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - setRefreshRateSelector(std::move(selectorPtr)); + + const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); + registerDisplay(displayId, std::move(selectorPtr)); } ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { @@ -242,7 +244,7 @@ public: auto &mutableLayerHistory() { return mLayerHistory; } - auto refreshRateSelector() { return holdRefreshRateSelector(); } + auto refreshRateSelector() { return leaderSelectorPtr(); } void replaceTouchTimer(int64_t millis) { if (mTouchTimer) { @@ -270,7 +272,7 @@ public: mPolicy.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { + void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) { return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } @@ -655,8 +657,7 @@ public: } mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); - const auto fps = - FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateSelector->getActiveMode().getFps()); + const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps(); mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -730,12 +731,14 @@ public: return mFlinger->mTransactionHandler.mPendingTransactionQueues; } - auto setTransactionState( - const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states, - const Vector<DisplayState> &displays, uint32_t flags, const sp<IBinder> &applyToken, - const InputWindowCommands &inputWindowCommands, int64_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t &uncacheBuffer, bool hasListenerCallbacks, - std::vector<ListenerCallbacks> &listenerCallbacks, uint64_t transactionId) { + auto setTransactionState(const FrameTimelineInfo &frameTimelineInfo, + Vector<ComposerState> &states, const Vector<DisplayState> &displays, + uint32_t flags, const sp<IBinder> &applyToken, + const InputWindowCommands &inputWindowCommands, + int64_t desiredPresentTime, bool isAutoTimestamp, + const client_cache_t &uncacheBuffer, bool hasListenerCallbacks, + std::vector<ListenerCallbacks> &listenerCallbacks, + uint64_t transactionId) { return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands, desiredPresentTime, isAutoTimestamp, uncacheBuffer, hasListenerCallbacks, diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp index acfc1d4dc8..c5b3fa67ae 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp @@ -160,7 +160,7 @@ void LayerFuzzer::invokeBufferStateLayer() { layer->setBuffer(texture, {} /*bufferData*/, mFdp.ConsumeIntegral<nsecs_t>() /*postTime*/, mFdp.ConsumeIntegral<nsecs_t>() /*desiredTime*/, mFdp.ConsumeBool() /*isAutoTimestamp*/, - {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/); + {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/, 0 /* hwcslot */); LayerRenderArea layerArea(*(flinger.flinger()), layer, getFuzzedRect(), {mFdp.ConsumeIntegral<int32_t>(), diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index bd11a37a06..7959e52fba 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -353,7 +353,7 @@ void SchedulerFuzzer::fuzzRefreshRateSelector() { const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}}; - refreshRateSelector.getRankedRefreshRates(layers, globalSignals); + refreshRateSelector.getRankedFrameRates(layers, globalSignals); layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength); layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>(); @@ -381,7 +381,8 @@ void SchedulerFuzzer::fuzzRefreshRateSelector() { mFdp.ConsumeFloatingPoint<float>())}}); refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{}); - refreshRateSelector.setActiveModeId(modeId); + refreshRateSelector.setActiveMode(modeId, + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); } RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue( diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 88e32e1bd0..2bc5b4676f 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -116,6 +116,8 @@ public: return true; } + void setDivisor(unsigned) override {} + nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { return timePoint; diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index d88da4da4a..01145772d5 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -85,6 +85,7 @@ cc_test { "FpsTest.cpp", "FramebufferSurfaceTest.cpp", "FrameRateOverrideMappingsTest.cpp", + "FrameRateSelectionPriorityTest.cpp", "FrameTimelineTest.cpp", "GameModeTest.cpp", "HWComposerTest.cpp", @@ -92,6 +93,7 @@ cc_test { "LayerHistoryTest.cpp", "LayerInfoTest.cpp", "LayerMetadataTest.cpp", + "LayerLifecycleManagerTest.cpp", "LayerTest.cpp", "LayerTestUtils.cpp", "MessageQueueTest.cpp", @@ -112,7 +114,6 @@ cc_test { "SchedulerTest.cpp", "SetFrameRateTest.cpp", "RefreshRateSelectorTest.cpp", - "RefreshRateSelectionTest.cpp", "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", "TimeStatsTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp index 982b9ff9ea..60ad7a3a03 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #include "DisplayTransactionTestHelpers.h" +#include "mock/MockFrameRateMode.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -29,6 +30,7 @@ using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjec class InitiateModeChangeTest : public DisplayTransactionTest { public: + using Action = DisplayDevice::DesiredActiveModeAction; using Event = scheduler::DisplayModeEvent; void SetUp() override { @@ -56,31 +58,42 @@ protected: static constexpr DisplayModeId kModeId90{1}; static constexpr DisplayModeId kModeId120{2}; - static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz); - static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz); - static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz); + static inline const ftl::NonNull<DisplayModePtr> kMode60 = + ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode90 = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode120 = + ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz)); }; TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) { - EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None})); + EXPECT_EQ(Action::None, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None})); EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); } TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); - // Setting another mode should be cached but return false - EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None})); + // Setting another mode should be cached but return None + EXPECT_EQ(Action::None, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); } TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); mDisplay->clearDesiredActiveModeState(); @@ -88,9 +101,11 @@ TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) { } TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); hal::VsyncPeriodChangeConstraints constraints{ @@ -101,18 +116,27 @@ TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS { EXPECT_EQ(OK, mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, &timeline)); - EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); mDisplay->clearDesiredActiveModeState(); ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); } +TEST_F(InitiateModeChangeTest, initiateRenderRateChange) { + EXPECT_EQ(Action::InitiateRenderRateSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None})); + EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode()); +} + TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged) NO_THREAD_SAFETY_ANALYSIS { - EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None})); + EXPECT_EQ(Action::InitiateDisplayModeSwitch, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); hal::VsyncPeriodChangeConstraints constraints{ @@ -123,21 +147,23 @@ NO_THREAD_SAFETY_ANALYSIS { EXPECT_EQ(OK, mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, &timeline)); - EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); - EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None})); + EXPECT_EQ(Action::None, + mDisplay->setDesiredActiveMode( + {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); - EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); EXPECT_EQ(OK, mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints, &timeline)); - EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event); mDisplay->clearDesiredActiveModeState(); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp index ac63a0edbd..ac63a0edbd 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 342c646847..8f89a8ca7a 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -146,6 +146,8 @@ TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) { {kMetadata2Name, kMetadata2Mandatory}, }), Return(hardware::graphics::composer::V2_4::Error::NONE))); + EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE)); + EXPECT_CALL(*mHal, registerCallback(_)); mHwc.setCallback(mCallback); @@ -162,6 +164,7 @@ TEST_F(HWComposerSetCallbackTest, handlesUnsupportedCallToGetLayerGenericMetadat EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<aidl::Capability>{})); EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)) .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED)); + EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED)); EXPECT_CALL(*mHal, registerCallback(_)); mHwc.setCallback(mCallback); diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp new file mode 100644 index 0000000000..89440a689c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -0,0 +1,453 @@ +/* + * Copyright 2022 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "FrontEnd/LayerHandle.h" +#include "FrontEnd/LayerLifecycleManager.h" +#include "Layer.h" +#include "gui/SurfaceComposerClient.h" + +using namespace android::surfaceflinger; + +namespace android::surfaceflinger::frontend { + +namespace { +LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) { + LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); + args.addToRoot = canBeRoot; + args.parentHandle = parent; + args.mirrorLayerHandle = mirror; + return args; +} +} // namespace + +// To run test: +/** + mp :libsurfaceflinger_unittest && adb sync; adb shell \ + /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \ + --gtest_filter="LayerLifecycleManagerTest.*" --gtest_repeat=100 \ + --gtest_shuffle \ + --gtest_brief=1 +*/ +class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener { +public: + void onLayerAdded(const RequestedLayerState& layer) override { + mActualLayersAdded.emplace(layer.id); + }; + void onLayerDestroyed(const RequestedLayerState& layer) override { + mActualLayersDestroyed.emplace(layer.id); + }; + + void expectLayersAdded(const std::unordered_set<uint32_t>& expectedLayersAdded) { + EXPECT_EQ(expectedLayersAdded, mActualLayersAdded); + mActualLayersAdded.clear(); + } + void expectLayersDestroyed(const std::unordered_set<uint32_t>& expectedLayersDestroyed) { + EXPECT_EQ(expectedLayersDestroyed, mActualLayersDestroyed); + mActualLayersDestroyed.clear(); + } + + std::unordered_set<uint32_t> mActualLayersAdded; + std::unordered_set<uint32_t> mActualLayersDestroyed; +}; + +class LayerLifecycleManagerTest : public testing::Test { +protected: + std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) { + return std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)); + } + + std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) { + mHandles[parentId] = sp<LayerHandle>::make(parentId); + return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false, + /*parent=*/mHandles[parentId], + /*mirror=*/nullptr)); + } + + TransactionState reparentLayer(uint32_t id, uint32_t newParentId) { + TransactionState transaction; + transaction.states.push_back({}); + + if (newParentId == UNASSIGNED_LAYER_ID) { + transaction.states.front().state.parentSurfaceControlForChild = nullptr; + } else { + transaction.states.front().state.parentSurfaceControlForChild = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), + sp<LayerHandle>::make(newParentId), + static_cast<int32_t>(newParentId), "Test"); + } + transaction.states.front().state.what = layer_state_t::eReparent; + transaction.states.front().state.surface = sp<LayerHandle>::make(id); + return transaction; + } + + TransactionState setLayer(uint32_t id, int32_t z) { + TransactionState transaction; + transaction.states.push_back({}); + transaction.states.front().state.z = z; + transaction.states.front().state.what = layer_state_t::eLayerChanged; + transaction.states.front().state.surface = sp<LayerHandle>::make(id); + return transaction; + } + + TransactionState makeRelative(uint32_t id, uint32_t relativeParentId) { + TransactionState transaction; + transaction.states.push_back({}); + + if (relativeParentId == UNASSIGNED_LAYER_ID) { + transaction.states.front().state.relativeLayerSurfaceControl = nullptr; + } else { + transaction.states.front().state.relativeLayerSurfaceControl = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), + sp<LayerHandle>::make(relativeParentId), + static_cast<int32_t>(relativeParentId), "Test"); + } + transaction.states.front().state.what = layer_state_t::eRelativeLayerChanged; + transaction.states.front().state.surface = sp<LayerHandle>::make(id); + return transaction; + } + + RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager, + uint32_t layerId) { + return lifecycleManager.getLayerFromId(layerId); + } + + std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; +}; + +TEST_F(LayerLifecycleManagerTest, addLayers) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(rootLayer(3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({1, 2, 3}); + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + listener->expectLayersAdded({1, 2, 3}); + listener->expectLayersDestroyed({1, 2, 3}); +} + +TEST_F(LayerLifecycleManagerTest, updateLayerStates) { + LayerLifecycleManager lifecycleManager; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.z = 2; + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + lifecycleManager.applyTransactions(transactions); + transactions.clear(); + + auto& managedLayers = lifecycleManager.getLayers(); + ASSERT_EQ(managedLayers.size(), 1u); + + EXPECT_EQ(managedLayers.front()->z, 2); + EXPECT_TRUE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z)); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + ASSERT_EQ(managedLayers.size(), 1u); + EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z)); + + // apply transactions that do not affect the hierarchy + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.backgroundBlurRadius = 22; + transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged; + transactions.back().states.front().state.surface = handle; + lifecycleManager.applyTransactions(transactions); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + EXPECT_EQ(managedLayers.front()->backgroundBlurRadius, 22u); +} + +TEST_F(LayerLifecycleManagerTest, layerWithoutHandleIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({1}); + lifecycleManager.commitChanges(); + + SCOPED_TRACE("layerWithoutHandleIsDestroyed"); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({1}); +} + +TEST_F(LayerLifecycleManagerTest, rootLayerWithoutHandleIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.onHandlesDestroyed({1}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({1}); +} + +TEST_F(LayerLifecycleManagerTest, offscreenLayerIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); + + lifecycleManager.onHandlesDestroyed({3}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({3}); +} + +TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithHandleIsNotDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.onHandlesDestroyed({3}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({3}); +} + +TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithoutHandleIsDestroyed) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.onHandlesDestroyed({3, 4}); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({3, 4}); +} + +TEST_F(LayerLifecycleManagerTest, reparentingDoesNotAffectRelativeZ) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({makeRelative(4, 1)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + lifecycleManager.applyTransactions({reparentLayer(4, 2)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); +} + +TEST_F(LayerLifecycleManagerTest, reparentingToNullRemovesRelativeZ) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({makeRelative(4, 1)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)}); + EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); +} + +TEST_F(LayerLifecycleManagerTest, setZRemovesRelativeZ) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + layers.emplace_back(rootLayer(2)); + layers.emplace_back(childLayer(3, /*parent*/ 2)); + layers.emplace_back(childLayer(4, /*parent*/ 3)); + + lifecycleManager.addLayers(std::move(layers)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2, 3, 4}); + listener->expectLayersDestroyed({}); + + lifecycleManager.applyTransactions({makeRelative(4, 1)}); + EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + lifecycleManager.applyTransactions({setLayer(4, 1)}); + EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); + + lifecycleManager.commitChanges(); + listener->expectLayersAdded({}); + listener->expectLayersDestroyed({}); +} + +TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0.5; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + lifecycleManager.applyTransactions(transactions); + + auto& managedLayers = lifecycleManager.getLayers(); + ASSERT_EQ(managedLayers.size(), 2u); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({}); + EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf); +} + +TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0.5; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + transactions.back().states.front().state.surface = handle; + + lifecycleManager.applyTransactions(transactions); + + ASSERT_EQ(lifecycleManager.getLayers().size(), 1u); + ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 1u); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({2}); +} + +TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) { + LayerLifecycleManager lifecycleManager; + auto listener = std::make_shared<ExpectLayerLifecycleListener>(); + lifecycleManager.addLifecycleListener(listener); + + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(rootLayer(1)); + lifecycleManager.addLayers(std::move(layers)); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.bgColorAlpha = 0.5; + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + sp<LayerHandle> handle = sp<LayerHandle>::make(1u); + transactions.back().states.front().state.surface = handle; + transactions.emplace_back(); + lifecycleManager.applyTransactions(transactions); + lifecycleManager.onHandlesDestroyed({1}); + + ASSERT_EQ(lifecycleManager.getLayers().size(), 0u); + ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 2u); + + EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + lifecycleManager.commitChanges(); + listener->expectLayersAdded({1, 2}); + listener->expectLayersDestroyed({1, 2}); +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index cedb7eb6ee..fdf2ffe3ce 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -26,10 +26,14 @@ #include <log/log.h> #include <ui/Size.h> +#include <scheduler/FrameRateMode.h> #include "DisplayHardware/HWC2.h" #include "FpsOps.h" #include "Scheduler/RefreshRateSelector.h" #include "mock/DisplayHardware/MockDisplayMode.h" +#include "mock/MockFrameRateMode.h" + +#include "libsurfaceflinger_unittest_main.h" using namespace std::chrono_literals; @@ -45,71 +49,77 @@ using SetPolicyResult = RefreshRateSelector::SetPolicyResult; using mock::createDisplayMode; struct TestableRefreshRateSelector : RefreshRateSelector { + using RefreshRateSelector::FrameRateRanking; using RefreshRateSelector::RefreshRateOrder; - using RefreshRateSelector::RefreshRateRanking; using RefreshRateSelector::RefreshRateSelector; - void setActiveModeId(DisplayModeId modeId) { + void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) { ftl::FakeGuard guard(kMainThreadContext); - return RefreshRateSelector::setActiveModeId(modeId); + return RefreshRateSelector::setActiveMode(modeId, renderFrameRate); } const DisplayMode& getActiveMode() const { - ftl::FakeGuard guard(kMainThreadContext); - return RefreshRateSelector::getActiveMode(); + std::lock_guard lock(mLock); + return *RefreshRateSelector::getActiveModeLocked().modePtr; } - DisplayModePtr getMinSupportedRefreshRate() const { + ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const { std::lock_guard lock(mLock); - return mMinRefreshRateModeIt->second; + return ftl::as_non_null(mMinRefreshRateModeIt->second); } - DisplayModePtr getMaxSupportedRefreshRate() const { + ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const { std::lock_guard lock(mLock); - return mMaxRefreshRateModeIt->second; + return ftl::as_non_null(mMaxRefreshRateModeIt->second); } - DisplayModePtr getMinRefreshRateByPolicy() const { + ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const { std::lock_guard lock(mLock); - return getMinRefreshRateByPolicyLocked(); + return ftl::as_non_null(getMinRefreshRateByPolicyLocked()); } - DisplayModePtr getMaxRefreshRateByPolicy() const { + ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const { std::lock_guard lock(mLock); - return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup()); + return ftl::as_non_null( + getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup())); } - RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, - RefreshRateOrder refreshRateOrder) const { + FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, + RefreshRateOrder refreshRateOrder) const { std::lock_guard lock(mLock); - return RefreshRateSelector::rankRefreshRates(anchorGroupOpt, refreshRateOrder); + return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder); } const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; } - using RefreshRateSelector::GetRankedRefreshRatesCache; - auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; } + using RefreshRateSelector::GetRankedFrameRatesCache; + auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; } - auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const { - const auto result = RefreshRateSelector::getRankedRefreshRates(layers, signals); + auto getRankedFrameRates(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const { + const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals); EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(), - ScoredRefreshRate::DescendingScore{})); + ScoredFrameRate::DescendingScore{})); return result; } auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers, GlobalSignals signals) const { - const auto [ranking, consideredSignals] = getRankedRefreshRates(layers, signals); + const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals); return std::make_pair(ranking, consideredSignals); } - DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {}, - GlobalSignals signals = {}) const { - return getRankedRefreshRates(layers, signals).ranking.front().modePtr; + ftl::NonNull<DisplayModePtr> getBestFrameRateMode( + const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const { + return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr; + } + + ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {}, + GlobalSignals signals = {}) const { + return getRankedFrameRates(layers, signals).ranking.front(); } SetPolicyResult setPolicy(const PolicyVariant& policy) { @@ -120,9 +130,11 @@ struct TestableRefreshRateSelector : RefreshRateSelector { SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) { return setPolicy(policy); } + + const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; } }; -class RefreshRateSelectorTest : public testing::Test { +class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> { protected: using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder; @@ -140,28 +152,48 @@ protected: static constexpr DisplayModeId kModeId24Frac{8}; static constexpr DisplayModeId kModeId30Frac{9}; static constexpr DisplayModeId kModeId60Frac{10}; - - static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz); - static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz); - static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz); - static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1); - static inline const DisplayModePtr kMode90_4K = - createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}); - static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz); - static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1); - static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz); - static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1); - static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz); - static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1); - static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz); - static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz); - static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1); - static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz); - static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz); - static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz); + static constexpr DisplayModeId kModeId35{11}; + + static inline const ftl::NonNull<DisplayModePtr> kMode60 = + ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode60Frac = + ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode90 = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode90_4K = + ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160})); + static inline const ftl::NonNull<DisplayModePtr> kMode72 = + ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 = + ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode120 = + ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 = + ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode30 = + ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 = + ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode30Frac = + ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode25 = + ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 = + ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1)); + static inline const ftl::NonNull<DisplayModePtr> kMode35 = + ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode50 = + ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode24 = + ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kMode24Frac = + ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz)); // Test configurations. static inline const DisplayModes kModes_60 = makeModes(kMode60); + static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90); static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90); static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1); static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K); @@ -185,6 +217,13 @@ protected: static inline const DisplayModes kModes_24_25_30_50_60_Frac = makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60, kMode60Frac); + + static TestableRefreshRateSelector createSelector(DisplayModes modes, + DisplayModeId activeModeId, + Config config = {}) { + config.enableFrameRateOverride = GetParam(); + return TestableRefreshRateSelector(modes, activeModeId, config); + } }; RefreshRateSelectorTest::RefreshRateSelectorTest() { @@ -201,13 +240,23 @@ RefreshRateSelectorTest::~RefreshRateSelectorTest() { namespace { -TEST_F(RefreshRateSelectorTest, oneMode_canSwitch) { - RefreshRateSelector selector(kModes_60, kModeId60); - EXPECT_FALSE(selector.canSwitch()); +INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest, + testing::Values(Config::FrameRateOverride::Disabled, + Config::FrameRateOverride::AppOverrideNativeRefreshRates, + Config::FrameRateOverride::AppOverride, + Config::FrameRateOverride::Enabled)); + +TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) { + auto selector = createSelector(kModes_60, kModeId60); + if (GetParam() == Config::FrameRateOverride::Enabled) { + EXPECT_TRUE(selector.canSwitch()); + } else { + EXPECT_FALSE(selector.canSwitch()); + } } -TEST_F(RefreshRateSelectorTest, invalidPolicy) { - TestableRefreshRateSelector selector(kModes_60, kModeId60); +TEST_P(RefreshRateSelectorTest, invalidPolicy) { + auto selector = createSelector(kModes_60, kModeId60); EXPECT_EQ(SetPolicyResult::Invalid, selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}})); @@ -215,8 +264,8 @@ TEST_F(RefreshRateSelectorTest, invalidPolicy) { selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}})); } -TEST_F(RefreshRateSelectorTest, unchangedPolicy) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, unchangedPolicy) { + auto selector = createSelector(kModes_60_90, kModeId60); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); @@ -236,8 +285,8 @@ TEST_F(RefreshRateSelectorTest, unchangedPolicy) { selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}})); } -TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) { + auto selector = createSelector(kModes_60_90, kModeId60); const auto minRate = selector.getMinSupportedRefreshRate(); const auto performanceRate = selector.getMaxSupportedRefreshRate(); @@ -252,8 +301,8 @@ TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) { EXPECT_EQ(performanceRateByPolicy, performanceRate); } -TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); const auto minRate = selector.getMinRefreshRateByPolicy(); const auto performanceRate = selector.getMaxSupportedRefreshRate(); @@ -266,7 +315,7 @@ TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroup EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); const auto minRate90 = selector.getMinRefreshRateByPolicy(); const auto performanceRate90 = selector.getMaxRefreshRateByPolicy(); @@ -276,8 +325,8 @@ TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroup EXPECT_EQ(kMode90_G1, performanceRate90); } -TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) { - TestableRefreshRateSelector selector(kModes_60_90_4K, kModeId60); +TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) { + auto selector = createSelector(kModes_60_90_4K, kModeId60); const auto minRate = selector.getMinRefreshRateByPolicy(); const auto performanceRate = selector.getMaxSupportedRefreshRate(); @@ -290,7 +339,7 @@ TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResol EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); const auto minRate90 = selector.getMinRefreshRateByPolicy(); const auto performanceRate90 = selector.getMaxRefreshRateByPolicy(); @@ -300,8 +349,8 @@ TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResol EXPECT_EQ(kMode90_4K, performanceRate90); } -TEST_F(RefreshRateSelectorTest, twoModes_policyChange) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, twoModes_policyChange) { + auto selector = createSelector(kModes_60_90, kModeId60); const auto minRate = selector.getMinRefreshRateByPolicy(); const auto performanceRate = selector.getMaxRefreshRateByPolicy(); @@ -319,14 +368,14 @@ TEST_F(RefreshRateSelectorTest, twoModes_policyChange) { EXPECT_EQ(kMode60, performanceRate60); } -TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) { + auto selector = createSelector(kModes_60_90, kModeId60); { const auto& mode = selector.getActiveMode(); EXPECT_EQ(mode.getId(), kModeId60); } - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); { const auto& mode = selector.getActiveMode(); EXPECT_EQ(mode.getId(), kModeId90); @@ -340,31 +389,31 @@ TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) { } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_noLayers) { +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) { { - TestableRefreshRateSelector selector(kModes_60_72_90, kModeId72); + auto selector = createSelector(kModes_60_72_90, kModeId72); // If there are no layers we select the default frame rate, which is the max of the primary // range. - EXPECT_EQ(kMode90, selector.getBestRefreshRate()); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode()); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); - EXPECT_EQ(kMode60, selector.getBestRefreshRate()); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode()); } { // We select max even when this will cause a non-seamless switch. - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); + auto selector = createSelector(kModes_60_90_G1, kModeId60); constexpr bool kAllowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy( {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching})); - EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate()); + EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode()); } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId72); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId72); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].vote = LayerVoteType::ExplicitExact; @@ -372,188 +421,187 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhe EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}})); - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_90) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.name = ""; EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}})); lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_multipleThreshold_60_90) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60, - {.frameRateMultipleThreshold = 90}); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) { + auto selector = createSelector(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_72_90) { - TestableRefreshRateSelector selector(kModes_60_72_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) { + auto selector = createSelector(kModes_60_72_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -563,23 +611,23 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) { lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 48_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 48_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -591,7 +639,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -599,7 +647,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -607,7 +655,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -615,7 +663,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -623,7 +671,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -631,7 +679,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; @@ -639,7 +687,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -647,7 +695,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -655,12 +703,13 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60, - {.frameRateMultipleThreshold = 120}); +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) { + auto selector = + createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -673,7 +722,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -681,7 +730,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -689,7 +738,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -697,7 +746,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -705,7 +754,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -713,7 +762,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; @@ -721,7 +770,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -729,7 +778,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -737,14 +786,14 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.name = "24Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -752,7 +801,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 120_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "120Hz ExplicitDefault"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -760,7 +809,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 120_Hz; lr2.vote = LayerVoteType::ExplicitExact; lr2.name = "120Hz ExplicitExact"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 10_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -768,7 +817,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr2.desiredRefreshRate = 120_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "120Hz ExplicitExact"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); lr1.desiredRefreshRate = 30_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -779,86 +828,86 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_m lr3.vote = LayerVoteType::Heuristic; lr3.desiredRefreshRate = 120_Hz; lr3.name = "120Hz Heuristic"; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60) { - TestableRefreshRateSelector selector(kModes_30_60, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) { + auto selector = createSelector(kModes_30_60, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90) { - TestableRefreshRateSelector selector(kModes_30_60_72_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) { + auto selector = createSelector(kModes_30_60_72_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); lr.desiredRefreshRate = 24_Hz; lr.vote = LayerVoteType::ExplicitExactOrMultiple; lr.name = "24Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) { - TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) { + auto selector = createSelector(kModes_30_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -866,43 +915,43 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) { lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Max; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Max; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 15_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 30_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -910,15 +959,14 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) { lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = selector.getBestRefreshRate(layers); + const auto mode = selector.getBestFrameRateMode(layers); EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " << to_string(mode->getFps()); } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) { - TestableRefreshRateSelector selector(kModes_60_120, kModeId60, - {.frameRateMultipleThreshold = 120}); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) { + auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -926,14 +974,14 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_ lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = selector.getBestRefreshRate(layers); + const auto mode = selector.getBestFrameRateMode(layers); EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " << to_string(mode->getFps()); } } -TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -943,23 +991,23 @@ TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) { lr1.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::ExplicitDefault; lr1.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -967,14 +1015,14 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) { lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = selector.getBestRefreshRate(layers, {}); + const auto mode = selector.getBestFrameRateMode(layers, {}); EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses " << to_string(mode->getFps()); } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -986,7 +1034,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; @@ -994,14 +1042,14 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::ExplicitDefault; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 30_Hz; @@ -1009,18 +1057,18 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 30_Hz; lr1.name = "30Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -1031,28 +1079,28 @@ TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true})); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); // The other layer starts to provide buffers lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -1061,49 +1109,70 @@ TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) { +TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); - + auto selector = createSelector(kModes_30_60_90, kModeId60); const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(), RefreshRateOrder::Descending); - const std::array expectedRefreshRates = {kMode90, kMode60, kMode30}; + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}}; + } + }(); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } } -TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) { +TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); + auto selector = createSelector(kModes_30_60_90, kModeId60); const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(), RefreshRateOrder::Ascending); - const std::array expectedRefreshRates = {kMode30, kMode60, kMode90}; + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + } + }(); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } } -TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) { +TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72); + auto selector = createSelector(kModes_30_60_90, kModeId72); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}})); @@ -1111,20 +1180,31 @@ TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) { const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending); - const std::array expectedRefreshRates = {kMode30, kMode60, kMode90}; + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}}; + } + }(); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } } -TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { +TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72); + auto selector = createSelector(kModes_30_60_90, kModeId72); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}})); @@ -1132,29 +1212,52 @@ TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Descending); - const std::array expectedRefreshRates = {kMode90, kMode60, kMode30}; + const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}}; + } + }(); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } } -TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { + auto selector = createSelector(kModes_60_90, kModeId60); - auto [refreshRates, signals] = selector.getRankedRefreshRates({}, {}); + auto [refreshRates, signals] = selector.getRankedFrameRates({}, {}); EXPECT_FALSE(signals.powerOnImminent); - std::array expectedRefreshRates = {kMode90, kMode60}; + auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, {60_Hz, kMode60}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, + {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}}; + } + }(); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } std::tie(refreshRates, signals) = @@ -1164,9 +1267,11 @@ TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) { ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1182,29 +1287,43 @@ TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) { ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } std::tie(refreshRates, signals) = selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false}); EXPECT_FALSE(signals.powerOnImminent); - expectedRefreshRates = {kMode60, kMode90}; + expectedRefreshRates = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{60_Hz, kMode60}, {90_Hz, kMode90}}; + case Config::FrameRateOverride::Enabled: + return {{60_Hz, kMode60}, {90_Hz, kMode90}, {45_Hz, kMode90}, + {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}}; + } + }(); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); for (size_t i = 0; i < expectedRefreshRates.size(); ++i) { - EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr) - << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue() - << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode) + << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " (" + << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " (" + << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } } -TEST_F(RefreshRateSelectorTest, touchConsidered) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, touchConsidered) { + auto selector = createSelector(kModes_60_90, kModeId60); - auto [_, signals] = selector.getRankedRefreshRates({}, {}); + auto [_, signals] = selector.getRankedFrameRates({}, {}); EXPECT_FALSE(signals.touch); std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true}); @@ -1251,8 +1370,8 @@ TEST_F(RefreshRateSelectorTest, touchConsidered) { EXPECT_FALSE(signals.touch); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) { - TestableRefreshRateSelector selector(kModes_60_90_72_120, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) { + auto selector = createSelector(kModes_60_90_72_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1284,57 +1403,57 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) { ss << "ExplicitDefault " << desired; lr.name = ss.str(); - EXPECT_EQ(expected, selector.getBestRefreshRate(layers)->getFps()); + EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps()); } } -TEST_F(RefreshRateSelectorTest, - getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) { +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) { std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; // Test that 23.976 will choose 24 if 23.976 is not supported { - TestableRefreshRateSelector selector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); + auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60, + kMode60Frac), + kModeId60); lr.vote = LayerVoteType::ExplicitExactOrMultiple; lr.desiredRefreshRate = 23.976_Hz; lr.name = "ExplicitExactOrMultiple 23.976 Hz"; - EXPECT_EQ(kModeId24, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId()); } // Test that 24 will choose 23.976 if 24 is not supported { - TestableRefreshRateSelector selector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); + auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, + kMode60, kMode60Frac), + kModeId60); lr.desiredRefreshRate = 24_Hz; lr.name = "ExplicitExactOrMultiple 24 Hz"; - EXPECT_EQ(kModeId24Frac, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId()); } // Test that 29.97 will prefer 59.94 over 60 and 30 { - TestableRefreshRateSelector selector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, - kMode60, kMode60Frac), - kModeId60); + auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60, + kMode60Frac), + kModeId60); lr.desiredRefreshRate = 29.97_Hz; lr.name = "ExplicitExactOrMultiple 29.97 Hz"; - EXPECT_EQ(kModeId60Frac, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId()); } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) { +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) { std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; // Test that voting for supported refresh rate will select this refresh rate { - TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60); + auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60); for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) { lr.vote = LayerVoteType::ExplicitExact; @@ -1343,14 +1462,14 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact_WithFractionalR ss << "ExplicitExact " << desired; lr.name = ss.str(); - EXPECT_EQ(lr.desiredRefreshRate, selector.getBestRefreshRate(layers)->getFps()); + EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps()); } } } -TEST_F(RefreshRateSelectorTest, - getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId90); +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) { + auto selector = createSelector(kModes_60_90, kModeId90); constexpr FpsRange k90 = {90_Hz, 90_Hz}; constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; @@ -1365,16 +1484,16 @@ TEST_F(RefreshRateSelectorTest, lr.name = "60Hz ExplicitDefault"; lr.focused = true; - const auto [mode, signals] = - selector.getRankedRefreshRates(layers, {.touch = true, .idle = true}); + const auto [rankedFrameRate, signals] = + selector.getRankedFrameRates(layers, {.touch = true, .idle = true}); - EXPECT_EQ(mode.begin()->modePtr, kMode60); + EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60); EXPECT_FALSE(signals.touch); } -TEST_F(RefreshRateSelectorTest, - getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) { + auto selector = createSelector(kModes_60_90, kModeId60); constexpr FpsRange k60 = {60_Hz, 60_Hz}; constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; @@ -1389,11 +1508,11 @@ TEST_F(RefreshRateSelectorTest, lr.desiredRefreshRate = 90_Hz; lr.name = "90Hz ExplicitDefault"; lr.focused = true; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.idle = true})); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true})); } -TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); +TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, @@ -1426,15 +1545,31 @@ TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) { lr5.name = "30Hz"; lr5.focused = true; - std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30}; - auto actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; + auto expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{120_Hz, kMode120}, + {90_Hz, kMode90}, + {72_Hz, kMode72}, + {60_Hz, kMode60}, + {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}}; + } + }(); + auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { - EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr) - << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps " - << actualRanking[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } lr1.vote = LayerVoteType::Max; @@ -1452,15 +1587,31 @@ TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) { lr5.desiredRefreshRate = 120_Hz; lr5.name = "120Hz"; - expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30}; - actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; + expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{120_Hz, kMode120}, + {90_Hz, kMode90}, + {72_Hz, kMode72}, + {60_Hz, kMode60}, + {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}}; + } + }(); + actualRanking = selector.getRankedFrameRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { - EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr) - << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps " - << actualRanking[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } lr1.vote = LayerVoteType::Heuristic; @@ -1476,15 +1627,31 @@ TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) { lr5.desiredRefreshRate = 72_Hz; lr5.name = "72Hz"; - expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72}; - actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; + expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, kMode30}, + {60_Hz, kMode60}, + {90_Hz, kMode90}, + {120_Hz, kMode120}, + {72_Hz, kMode72}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}, {120_Hz, kMode120}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}}; + } + }(); + actualRanking = selector.getRankedFrameRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { - EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr) - << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps " - << actualRanking[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } lr1.desiredRefreshRate = 120_Hz; @@ -1503,21 +1670,37 @@ TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) { lr5.desiredRefreshRate = 120_Hz; lr5.name = "120Hz-2"; - expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30}; - actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; + expectedRanking = []() -> std::vector<FrameRateMode> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{90_Hz, kMode90}, + {60_Hz, kMode60}, + {120_Hz, kMode120}, + {72_Hz, kMode72}, + {30_Hz, kMode30}}; + case Config::FrameRateOverride::Enabled: + return {{90_Hz, kMode90}, {60_Hz, kMode60}, {120_Hz, kMode120}, {72_Hz, kMode72}, + {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}}; + } + }(); + actualRanking = selector.getRankedFrameRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); for (size_t i = 0; i < expectedRanking.size(); ++i) { - EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr) - << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps " - << actualRanking[i].modePtr->getFps().getIntValue(); + EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode) + << "Expected " << expectedRanking[i].fps.getIntValue() << " (" + << expectedRanking[i].modePtr->getFps().getIntValue() << ")" + << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " (" + << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")"; } } -TEST_F(RefreshRateSelectorTest, - getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId90); +TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) { + auto selector = createSelector(kModes_60_90, kModeId90); constexpr FpsRange k90 = {90_Hz, 90_Hz}; constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; @@ -1525,8 +1708,8 @@ TEST_F(RefreshRateSelectorTest, EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}})); - const auto [ranking, signals] = selector.getRankedRefreshRates({}, {}); - EXPECT_EQ(ranking.front().modePtr, kMode90); + const auto [ranking, signals] = selector.getRankedFrameRates({}, {}); + EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90); EXPECT_FALSE(signals.touch); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1536,50 +1719,50 @@ TEST_F(RefreshRateSelectorTest, lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitExactOrMultiple"; lr.focused = false; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.focused = true; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::ExplicitDefault; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitDefault"; lr.focused = false; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.focused = true; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Heuristic; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; lr.focused = false; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.focused = true; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Max; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Max"; lr.focused = false; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.focused = true; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.vote = LayerVoteType::Min; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Min"; lr.focused = false; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); lr.focused = true; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers)); } -TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); // The default policy doesn't allow group switching. Verify that no // group switches are performed. @@ -1591,11 +1774,11 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) { layer.name = "90Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; @@ -1609,11 +1792,11 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) { layer.seamlessness = Seamlessness::SeamedAndSeamless; layer.name = "90Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; @@ -1628,18 +1811,18 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) { layer.seamlessness = Seamlessness::OnlySeamless; layer.name = "90Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); // Verify that we won't do a seamless switch if we request the same mode as the default std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1649,18 +1832,18 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps layer.seamlessness = Seamlessness::OnlySeamless; layer.name = "60Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); // Verify that if the active mode is in another group and there are no layers with // Seamlessness::SeamedAndSeamless, we should switch back to the default group. @@ -1673,18 +1856,18 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) { layer.name = "60Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with // Seamlessness::OnlySeamless can't change the mode group. @@ -1702,18 +1885,18 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed layers[1].name = "90Hz ExplicitDefault"; layers[1].focused = false; - EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with // Seamlessness::Default can't change the mode group back to the group of the default @@ -1735,18 +1918,18 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeam layers[1].vote = LayerVoteType::ExplicitDefault; layers[1].name = "90Hz ExplicitDefault"; - EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); +TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { + auto selector = createSelector(kModes_60_90_G1, kModeId60); RefreshRateSelector::DisplayManagerPolicy policy; policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); // Layer with Seamlessness::Default can change the mode group if there's an // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens @@ -1765,11 +1948,11 @@ TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndS layers[1].vote = LayerVoteType::ExplicitDefault; layers[1].name = "90Hz ExplicitDefault"; - EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) { - TestableRefreshRateSelector selector(kModes_30_60, kModeId60); +TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) { + auto selector = createSelector(kModes_30_60, kModeId60); // Allow group switching. RefreshRateSelector::DisplayManagerPolicy policy; @@ -1785,14 +1968,14 @@ TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) { layer.name = "60Hz ExplicitExactOrMultiple"; layer.focused = true; - EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); - selector.setActiveModeId(kModeId120); - EXPECT_EQ(kModeId120, selector.getBestRefreshRate(layers)->getId()); + selector.setActiveMode(kModeId120, 120_Hz); + EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) { - TestableRefreshRateSelector selector(kModes_25_30_50_60, kModeId60); +TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) { + auto selector = createSelector(kModes_25_30_50_60, kModeId60); // Allow group switching. RefreshRateSelector::DisplayManagerPolicy policy; @@ -1813,18 +1996,18 @@ TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) { .weight = 1.f, .focused = true}}; - EXPECT_EQ(kModeId50, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId()); auto& seamedLayer = layers[0]; seamedLayer.desiredRefreshRate = 30_Hz; seamedLayer.name = "30Hz ExplicitDefault"; - selector.setActiveModeId(kModeId30); + selector.setActiveMode(kModeId30, 30_Hz); - EXPECT_EQ(kModeId25, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) { - TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId90); +TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) { + auto selector = createSelector(kModes_60_90_G1, kModeId90); // Allow group switching. RefreshRateSelector::DisplayManagerPolicy policy; @@ -1835,11 +2018,11 @@ TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) { std::vector<LayerRequirement> layers = { {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}}; - EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId()); } -TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { - TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { + auto selector = createSelector(kModes_30_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; @@ -1849,13 +2032,14 @@ TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { bool focused = true; }; - // Returns the mode selected by getBestRefreshRate for a single layer with the given arguments. + // Returns the mode selected by getBestFrameRateMode for a single layer with the given + // arguments. const auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId { layers[0].vote = voteType; layers[0].desiredRefreshRate = fps; layers[0].focused = args.focused; - return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId(); + return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId(); }; constexpr FpsRange k30_60 = {30_Hz, 60_Hz}; @@ -1864,7 +2048,7 @@ TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}})); - EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId()); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz)); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); @@ -1897,22 +2081,23 @@ TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); } -TEST_F(RefreshRateSelectorTest, idle) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, idle) { + auto selector = createSelector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; - const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId { + const auto getIdleDisplayModeId = [&](LayerVoteType voteType, + bool touchActive) -> DisplayModeId { layers[0].vote = voteType; layers[0].desiredRefreshRate = 90_Hz; const auto [ranking, signals] = - selector.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true}); + selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true}); // Refresh rate will be chosen by either touch state or idle state. EXPECT_EQ(!touchActive, signals.idle); - return ranking.front().modePtr->getId(); + return ranking.front().frameRateMode.modePtr->getId(); }; EXPECT_EQ(SetPolicyResult::Changed, @@ -1921,38 +2106,38 @@ TEST_F(RefreshRateSelectorTest, idle) { // Idle should be lower priority than touch boost. { constexpr bool kTouchActive = true; - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive)); - EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive)); EXPECT_EQ(kModeId90, - getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); } // With no layers, idle should still be lower priority than touch boost. - EXPECT_EQ(kModeId90, selector.getBestRefreshRate({}, {.touch = true, .idle = true})->getId()); + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId()); // Idle should be higher precedence than other layer frame rate considerations. - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); { constexpr bool kTouchActive = false; - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive)); - EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive)); EXPECT_EQ(kModeId60, - getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); } // Idle should be applied rather than the active mode when there are no layers. - EXPECT_EQ(kModeId60, selector.getBestRefreshRate({}, {.idle = true})->getId()); + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId()); } -TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) { + auto selector = createSelector(kModes_60_90, kModeId60); for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) { const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps)); @@ -1969,12 +2154,12 @@ TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) { } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) { - TestableRefreshRateSelector selector(kModes_60_90, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) { + auto selector = createSelector(kModes_60_90, kModeId60); struct Expectation { Fps fps; - DisplayModePtr mode; + ftl::NonNull<DisplayModePtr> mode; }; const std::initializer_list<Expectation> knownFrameRatesExpectations = { @@ -1997,101 +2182,86 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) { for (const auto& [fps, mode] : knownFrameRatesExpectations) { layer.desiredRefreshRate = fps; - EXPECT_EQ(mode, selector.getBestRefreshRate(layers)); + EXPECT_EQ(mode, selector.getBestFrameRateMode(layers)); } } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; auto& explicitExactOrMultipleLayer = layers[1]; - explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; - explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; - explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; - explicitExactLayer.vote = LayerVoteType::ExplicitExact; explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers, {.touch = true})); - - explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; - explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); - - explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); -} - -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60, - {.enableFrameRateOverride = - Config::FrameRateOverride::Enabled}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - auto& explicitExactLayer = layers[0]; - auto& explicitExactOrMultipleLayer = layers[1]; - explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz; - explicitExactLayer.vote = LayerVoteType::ExplicitExact; - explicitExactLayer.name = "ExplicitExact"; - explicitExactLayer.desiredRefreshRate = 30_Hz; - - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true})); + if (GetParam() == Config::FrameRateOverride::Disabled) { + EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz, + selector.getBestScoredFrameRate(layers, {.touch = true}) + .frameRateMode); + + } else { + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, + selector.getBestScoredFrameRate(layers, {.touch = true}) + .frameRateMode); + } explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + + if (GetParam() == Config::FrameRateOverride::Disabled) { + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } else { + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ReadsCache) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); using GlobalSignals = RefreshRateSelector::GlobalSignals; const auto args = std::make_pair(std::vector<LayerRequirement>{}, GlobalSignals{.touch = true, .idle = true}); - const RefreshRateSelector::RankedRefreshRates result = {{RefreshRateSelector::ScoredRefreshRate{ - kMode90}}, - {.touch = true}}; + const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{ + {90_Hz, kMode90}}}, + GlobalSignals{.touch = true}}; selector.mutableGetRankedRefreshRatesCache() = {args, result}; - EXPECT_EQ(result, selector.getRankedRefreshRates(args.first, args.second)); + EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second)); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache()); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true}; - const auto result = selector.getRankedRefreshRates(layers, globalSignals); + const auto result = selector.getRankedFrameRates(layers, globalSignals); const auto& cache = selector.mutableGetRankedRefreshRatesCache(); ASSERT_TRUE(cache); @@ -2100,10 +2270,8 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) { EXPECT_EQ(cache->result, result); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) { - TestableRefreshRateSelector selector(kModes_60_120, kModeId60, - {.enableFrameRateOverride = - Config::FrameRateOverride::Enabled}); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) { + auto selector = createSelector(kModes_60_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -2117,19 +2285,21 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) { explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + if (GetParam() == Config::FrameRateOverride::Disabled) { + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true})); + } else { + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true})); + } explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true})); } -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) { - TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60, - {.enableFrameRateOverride = - Config::FrameRateOverride::Enabled}); +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) { + auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}}; auto& explicitDefaultLayer = layers[0]; @@ -2143,12 +2313,16 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactA explicitDefaultLayer.name = "ExplicitDefault"; explicitDefaultLayer.desiredRefreshRate = 59.94_Hz; - EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers)); } // b/190578904 -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) { - constexpr int kMinRefreshRate = 10; +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) { + if (g_noSlowTests) { + GTEST_SKIP(); + } + + const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue(); constexpr int kMaxRefreshRate = 240; DisplayModes displayModes; @@ -2159,14 +2333,13 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) { Fps::fromValue(static_cast<float>(fps)))); } - const TestableRefreshRateSelector selector(std::move(displayModes), - DisplayModeId(kMinRefreshRate)); + const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate)); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { layers[0].desiredRefreshRate = fps; layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), selector.getBestRefreshRate(layers)->getFps().getIntValue()) + EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue()) << "Failed for " << ftl::enum_string(vote); }; @@ -2180,7 +2353,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) { } // b/190578904 -TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) { +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) { constexpr DisplayModeId kActiveModeId{0}; DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz), createDisplayMode(DisplayModeId(1), 53_Hz), @@ -2188,7 +2361,7 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) { createDisplayMode(DisplayModeId(3), 60_Hz)); const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; - const TestableRefreshRateSelector selector(std::move(displayModes), kActiveModeId); + const auto selector = createSelector(std::move(displayModes), kActiveModeId); const std::vector<LayerRequirement> layers = { { @@ -2205,19 +2378,19 @@ TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) { }, }; - EXPECT_EQ(53_Hz, selector.getBestRefreshRate(layers, globalSignals)->getFps()); + EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps()); } -TEST_F(RefreshRateSelectorTest, modeComparison) { +TEST_P(RefreshRateSelectorTest, modeComparison) { EXPECT_LT(kMode60->getFps(), kMode90->getFps()); EXPECT_GE(kMode60->getFps(), kMode60->getFps()); EXPECT_GE(kMode90->getFps(), kMode90->getFps()); } -TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) { +TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) { using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; - TestableRefreshRateSelector selector(kModes_60_90, kModeId90); + auto selector = createSelector(kModes_60_90, kModeId90); EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); @@ -2234,10 +2407,10 @@ TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) { EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); } -TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) { +TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) { using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; - TestableRefreshRateSelector selector(kModes_60_120, kModeId120); + auto selector = createSelector(kModes_60_120, kModeId120); EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}})); @@ -2256,30 +2429,30 @@ TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) { EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); } -TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId30); +TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId30); const auto frameRate = 30_Hz; Fps displayRefreshRate = selector.getActiveMode().getFps(); EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - selector.setActiveModeId(kModeId60); + selector.setActiveMode(kModeId60, 60_Hz); displayRefreshRate = selector.getActiveMode().getFps(); EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - selector.setActiveModeId(kModeId72); + selector.setActiveMode(kModeId72, 72_Hz); displayRefreshRate = selector.getActiveMode().getFps(); EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); displayRefreshRate = selector.getActiveMode().getFps(); EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - selector.setActiveModeId(kModeId120); + selector.setActiveMode(kModeId120, 120_Hz); displayRefreshRate = selector.getActiveMode().getFps(); EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - selector.setActiveModeId(kModeId90); + selector.setActiveMode(kModeId90, 90_Hz); displayRefreshRate = selector.getActiveMode().getFps(); EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz)); @@ -2289,7 +2462,7 @@ TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) { EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz)); } -TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) { +TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) { EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz)); EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz)); @@ -2315,22 +2488,72 @@ TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) { EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz)); } -TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) { - RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120); +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty()); } -TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) { - RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + + layers[0].vote = LayerVoteType::NoVote; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::Min; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::Max; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::Heuristic; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) { + if (GetParam() != Config::FrameRateOverride::Disabled) { + return; + } + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; layers[0].ownerUid = 1234; layers[0].desiredRefreshRate = 60_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); + + layers[0].vote = LayerVoteType::ExplicitExact; + EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty()); +} + +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + + layers[0].vote = LayerVoteType::ExplicitDefault; auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); @@ -2342,26 +2565,23 @@ TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) { ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - layers[0].vote = LayerVoteType::NoVote; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Min; + layers[0].vote = LayerVoteType::ExplicitExact; frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); +} - layers[0].vote = LayerVoteType::Max; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } - layers[0].vote = LayerVoteType::Heuristic; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); -} + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); -TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { - RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, {.ownerUid = 5678, .weight = 1.f}}; @@ -2392,9 +2612,16 @@ TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { EXPECT_TRUE(frameRateOverrides.empty()); } -TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) { - RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}}; layers[0].name = "Test layer"; @@ -2432,88 +2659,87 @@ TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) { EXPECT_TRUE(frameRateOverrides.empty()); } -TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_Enabled) { - RefreshRateSelector selector(kModes_60_120, kModeId120, - {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) { + if (GetParam() == Config::FrameRateOverride::Disabled) { + return; + } + + ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates || + GetParam() == Config::FrameRateOverride::AppOverride || + GetParam() == Config::FrameRateOverride::Enabled); + + auto selector = createSelector(kModes_60_120, kModeId120); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; layers[0].ownerUid = 1234; layers[0].desiredRefreshRate = 30_Hz; - layers[0].vote = LayerVoteType::ExplicitDefault; + const auto expetedFps = + GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); + EXPECT_EQ(expetedFps, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); + EXPECT_EQ(expetedFps, frameRateOverrides.at(1234)); - layers[0].vote = LayerVoteType::NoVote; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Min; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Max; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); - - layers[0].vote = LayerVoteType::Heuristic; + layers[0].vote = LayerVoteType::ExplicitExact; frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); -} - -TEST_F(RefreshRateSelectorTest, - getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_EnabledForNativeRefreshRates) { - RefreshRateSelector selector(kModes_60_120, kModeId120, - {.enableFrameRateOverride = - Config::FrameRateOverride::EnabledForNativeRefreshRates}); - - std::vector<LayerRequirement> layers = {{.weight = 1.f}}; - layers[0].name = "Test layer"; - layers[0].ownerUid = 1234; - layers[0].desiredRefreshRate = 30_Hz; - layers[0].vote = LayerVoteType::ExplicitDefault; - - auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + EXPECT_EQ(expetedFps, frameRateOverrides.at(1234)); +} - layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_EQ(1u, frameRateOverrides.size()); - ASSERT_EQ(1u, frameRateOverrides.count(1234)); - EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); +TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) { + auto selector = createSelector(kModes_60_120, kModeId120); - layers[0].vote = LayerVoteType::NoVote; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); + // The render frame rate cannot be greater than the physical refresh rate + { + const FpsRange physical = {60_Hz, 60_Hz}; + const FpsRange render = {60_Hz, 120_Hz}; + EXPECT_EQ(SetPolicyResult::Invalid, + selector.setDisplayManagerPolicy( + {kModeId60, {physical, render}, {physical, render}})); + } +} - layers[0].vote = LayerVoteType::Min; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); +TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) { + auto selector = createSelector(kModes_60_120, kModeId120); - layers[0].vote = LayerVoteType::Max; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); + { + const FpsRange physical = {0_Hz, 120_Hz}; + const FpsRange render = {0_Hz, 60_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId60, {physical, render}, {physical, render}})); + const auto expectedMaxMode = + GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60; + EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy()); + EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy()); + } - layers[0].vote = LayerVoteType::Heuristic; - frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); - EXPECT_TRUE(frameRateOverrides.empty()); + { + const FpsRange physical = {0_Hz, 120_Hz}; + const FpsRange render = {120_Hz, 120_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId60, {physical, render}, {physical, render}})); + EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy()); + EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy()); + } } -TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) { - TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = - Config::FrameRateOverride::Enabled}); +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; { @@ -2566,5 +2792,156 @@ TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) { EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); } +TEST_P(RefreshRateSelectorTest, renderFrameRates) { + auto selector = createSelector(kModes_30_60_72_90_120, kModeId120); + + // [renderRate, refreshRate] + const auto expected = []() -> std::vector<std::pair<Fps, Fps>> { + switch (GetParam()) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::AppOverrideNativeRefreshRates: + case Config::FrameRateOverride::AppOverride: + return {{30_Hz, 30_Hz}, + {60_Hz, 60_Hz}, + {72_Hz, 72_Hz}, + {90_Hz, 90_Hz}, + {120_Hz, 120_Hz}}; + case Config::FrameRateOverride::Enabled: + return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz}, + {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}}; + } + }(); + + const auto& primaryRefreshRates = selector.getPrimaryFrameRates(); + ASSERT_EQ(expected.size(), primaryRefreshRates.size()); + + for (size_t i = 0; i < expected.size(); i++) { + const auto [expectedRenderRate, expectedRefreshRate] = expected[i]; + EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps); + EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps()); + } +} + +TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + auto selector = createSelector(kModes_60_120, kModeId60); + + constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz}; + constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz}; + + constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz, + /*render*/ k0_120Hz}; + + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode); + { + constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz, + /*render*/ k0_120Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60, + /*primaryRanges*/ + kPrimary, + /*appRequestRanges*/ + kAppRequest})); + } + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode); + + { + constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz, + /*render*/ k0_60Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60, + /*primaryRanges*/ + kPrimary, + /*appRequestRanges*/ + kAppRequest})); + } + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode); + + { + constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz, + /*render*/ k0_60Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60, + /*primaryRanges*/ + kPrimary, + /*appRequestRanges*/ + kAppRequest})); + } + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode); +} + +TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) { + auto selector = createSelector(kModes_60_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + auto& layer = layers[0]; + + const auto expectedRenderRate = + GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz; + + layer.name = "30Hz ExplicitDefault"; + layer.desiredRefreshRate = 30_Hz; + layer.vote = LayerVoteType::ExplicitDefault; + EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate, + selector.getBestScoredFrameRate(layers).frameRateMode); + + layer.name = "30Hz Heuristic"; + layer.desiredRefreshRate = 30_Hz; + layer.vote = LayerVoteType::Heuristic; + EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate, + selector.getBestScoredFrameRate(layers).frameRateMode); + + layer.name = "30Hz ExplicitExactOrMultiple"; + layer.desiredRefreshRate = 30_Hz; + layer.vote = LayerVoteType::ExplicitExactOrMultiple; + EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate, + selector.getBestScoredFrameRate(layers).frameRateMode); +} + +TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) { + auto selector = createSelector(kModes_35_60_90, kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + + const auto getIdleDisplayModeId = [&](LayerVoteType voteType, + bool touchActive) -> DisplayModeId { + layers[0].vote = voteType; + layers[0].desiredRefreshRate = 90_Hz; + + const auto [ranking, signals] = + selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true}); + + // Refresh rate will be chosen by either touch state or idle state. + EXPECT_EQ(!touchActive, signals.idle); + return ranking.front().frameRateMode.modePtr->getId(); + }; + + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}})); + + // With no layers, idle should still be lower priority than touch boost. + EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId()); + + // Idle should be higher precedence than other layer frame rate considerations. + selector.setActiveMode(kModeId90, 90_Hz); + { + constexpr bool kTouchActive = false; + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive)); + EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive)); + EXPECT_EQ(kModeId35, + getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); + } + + // Idle should be applied rather than the active mode when there are no layers. + EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId()); +} + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index ea4666ed4b..3ee53c94c2 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -58,22 +58,22 @@ protected: SchedulerTest(); static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u); - static inline const DisplayModePtr kDisplay1Mode60 = - createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz); - static inline const DisplayModePtr kDisplay1Mode120 = - createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz); + static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 = + ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 = + ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz)); static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120); static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u); - static inline const DisplayModePtr kDisplay2Mode60 = - createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz); - static inline const DisplayModePtr kDisplay2Mode120 = - createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz); + static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 = + ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz)); + static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 = + ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz)); static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120); static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u); - static inline const DisplayModePtr kDisplay3Mode60 = - createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz); + static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 = + ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz)); static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60); std::shared_ptr<RefreshRateSelector> mSelector = @@ -190,8 +190,10 @@ TEST_F(SchedulerTest, updateDisplayModes) { sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); ASSERT_EQ(1u, mScheduler->layerHistorySize()); - mScheduler->setRefreshRateSelector( - std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId())); + // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes. + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); @@ -215,7 +217,9 @@ TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { // If the handle is incorrect, the function should return before // onModeChange is called. ConnectionHandle invalidHandle = {.id = 123}; - EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode)); + EXPECT_NO_FATAL_FAILURE( + mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, + {90_Hz, ftl::as_non_null(mode)})); EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); } @@ -230,15 +234,13 @@ TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { } MATCHER(Is120Hz, "") { - return isApproxEqual(arg.front().modePtr->getFps(), 120_Hz); + return isApproxEqual(arg.front().mode.fps, 120_Hz); } TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { - const auto selectorPtr = - std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId()); - - mScheduler->registerDisplay(kDisplayId1, selectorPtr); - mScheduler->setRefreshRateSelector(selectorPtr); + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true)); @@ -277,7 +279,7 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { auto choice = modeChoices.get(kDisplayId1); ASSERT_TRUE(choice); - EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode60, globalSignals)); + EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals)); globalSignals = {.idle = false}; mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); @@ -287,7 +289,7 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { choice = modeChoices.get(kDisplayId1); ASSERT_TRUE(choice); - EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals)); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); globalSignals = {.touch = true}; mScheduler->replaceTouchTimer(10); @@ -298,7 +300,7 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { choice = modeChoices.get(kDisplayId1); ASSERT_TRUE(choice); - EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals)); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); mScheduler->unregisterDisplay(kDisplayId1); EXPECT_FALSE(mScheduler->hasRefreshRateSelectors()); @@ -319,8 +321,11 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { const GlobalSignals globalSignals = {.idle = true}; expectedChoices = ftl::init::map<const PhysicalDisplayId&, - DisplayModeChoice>(kDisplayId1, kDisplay1Mode60, - globalSignals)(kDisplayId2, kDisplay2Mode60, + DisplayModeChoice>(kDisplayId1, + FrameRateMode{60_Hz, kDisplay1Mode60}, + globalSignals)(kDisplayId2, + FrameRateMode{60_Hz, + kDisplay2Mode60}, globalSignals); std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f}, @@ -335,8 +340,11 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { const GlobalSignals globalSignals = {.idle = false}; expectedChoices = ftl::init::map<const PhysicalDisplayId&, - DisplayModeChoice>(kDisplayId1, kDisplay1Mode120, - globalSignals)(kDisplayId2, kDisplay2Mode120, + DisplayModeChoice>(kDisplayId1, + FrameRateMode{120_Hz, kDisplay1Mode120}, + globalSignals)(kDisplayId2, + FrameRateMode{120_Hz, + kDisplay2Mode120}, globalSignals); mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); @@ -351,8 +359,11 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { expectedChoices = ftl::init::map<const PhysicalDisplayId&, - DisplayModeChoice>(kDisplayId1, kDisplay1Mode120, - globalSignals)(kDisplayId2, kDisplay2Mode120, + DisplayModeChoice>(kDisplayId1, + FrameRateMode{120_Hz, kDisplay1Mode120}, + globalSignals)(kDisplayId2, + FrameRateMode{120_Hz, + kDisplay2Mode120}, globalSignals); const auto actualChoices = mScheduler->chooseDisplayModes(); @@ -369,13 +380,15 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { mScheduler->replaceTouchTimer(10); mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); - expectedChoices = - ftl::init::map<const PhysicalDisplayId&, - DisplayModeChoice>(kDisplayId1, kDisplay1Mode60, - globalSignals)(kDisplayId2, kDisplay2Mode60, - globalSignals)(kDisplayId3, - kDisplay3Mode60, - globalSignals); + expectedChoices = ftl::init::map< + const PhysicalDisplayId&, + DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60}, + globalSignals)(kDisplayId2, + FrameRateMode{60_Hz, kDisplay2Mode60}, + globalSignals)(kDisplayId3, + FrameRateMode{60_Hz, + kDisplay3Mode60}, + globalSignals); const auto actualChoices = mScheduler->chooseDisplayModes(); EXPECT_EQ(expectedChoices, actualChoices); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 05d0ebf773..b81693a0c2 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -116,7 +116,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe ftl::FakeGuard guard(kMainThreadContext); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); mFlinger.onActiveDisplayChanged(mDisplay); @@ -125,8 +125,8 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; @@ -139,7 +139,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe Mock::VerifyAndClearExpectations(mComposer); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that the next commit will complete the mode change and send // a onModeChanged event to the framework. @@ -149,7 +149,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe Mock::VerifyAndClearExpectations(mAppEventThread); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90); } TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) { @@ -164,8 +164,8 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefres 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. @@ -180,7 +180,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefres mFlinger.commit(); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90); } TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { @@ -190,7 +190,7 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { // is still being processed the later call will be respected. ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); mFlinger.onActiveDisplayChanged(mDisplay); @@ -211,7 +211,7 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { 180)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120); EXPECT_CALL(*mComposer, setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, @@ -221,19 +221,19 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.commit(); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120); mFlinger.commit(); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120); } TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) { ftl::FakeGuard guard(kMainThreadContext); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); mFlinger.onActiveDisplayChanged(mDisplay); @@ -242,8 +242,8 @@ TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefresh 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60); + ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. @@ -278,7 +278,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefresh mDisplay = mFlinger.getDisplay(displayToken); ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K); + ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp index 0384568707..c0796df6cb 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -288,7 +288,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { if constexpr (Case::Display::CONNECTION_TYPE::value) { ftl::FakeGuard guard(kMainThreadContext); - EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId()); + EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId()); } } diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index ba214d534f..54c10c51e8 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -43,7 +43,9 @@ public: ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - setRefreshRateSelector(std::move(selectorPtr)); + + const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); + registerDisplay(displayId, std::move(selectorPtr)); ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { // Execute task to prevent broken promise exception on destruction. @@ -67,12 +69,27 @@ public: auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } - auto refreshRateSelector() { return holdRefreshRateSelector(); } - bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); } + auto refreshRateSelector() { return leaderSelectorPtr(); } + + const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS { + return mRefreshRateSelectors; + } + + bool hasRefreshRateSelectors() const { return !refreshRateSelectors().empty(); } - void setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) { + void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { ftl::FakeGuard guard(kMainThreadContext); - return Scheduler::setRefreshRateSelector(std::move(selectorPtr)); + Scheduler::registerDisplay(displayId, std::move(selectorPtr)); + } + + void unregisterDisplay(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + Scheduler::unregisterDisplay(displayId); + } + + void setLeaderDisplay(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + Scheduler::setLeaderDisplay(displayId); } auto& mutableLayerHistory() { return mLayerHistory; } @@ -115,14 +132,13 @@ public: using Scheduler::DisplayModeChoice; using Scheduler::DisplayModeChoiceMap; - DisplayModeChoiceMap chooseDisplayModes() { - std::lock_guard<std::mutex> lock(mPolicyLock); + DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS { return Scheduler::chooseDisplayModes(); } void dispatchCachedReportedMode() { std::lock_guard<std::mutex> lock(mPolicyLock); - return Scheduler::dispatchCachedReportedMode(); + Scheduler::dispatchCachedReportedMode(); } void clearCachedReportedMode() { @@ -130,8 +146,8 @@ public: mPolicy.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) { - return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); + void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { + Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } private: diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index df53f1560a..e29dd67c21 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -221,7 +221,7 @@ public: selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); } - const auto fps = FTL_FAKE_GUARD(kMainThreadContext, selectorPtr->getActiveMode().getFps()); + const auto fps = selectorPtr->getActiveMode().fps; mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -429,18 +429,24 @@ public: return mFlinger->mTransactionHandler.mPendingTransactionCount.load(); } - auto setTransactionState( - const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) { + auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo, + Vector<ComposerState>& states, const Vector<DisplayState>& displays, + uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, + int64_t desiredPresentTime, bool isAutoTimestamp, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + std::vector<ListenerCallbacks>& listenerCallbacks, + uint64_t transactionId) { return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands, desiredPresentTime, isAutoTimestamp, uncacheBuffer, hasListenerCallbacks, listenerCallbacks, transactionId); } + auto setTransactionStateInternal(TransactionState& transaction) { + return mFlinger->mTransactionHandler.queueTransaction(std::move(transaction)); + } + auto flushTransactionQueues() { return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues(kVsyncId)); } @@ -837,34 +843,38 @@ public: sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs); mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display); - if (mFlinger.scheduler()) { - mFlinger.scheduler()->registerDisplay(display->getPhysicalId(), - display->holdRefreshRateSelector()); - } DisplayDeviceState state; state.isSecure = mCreationArgs.isSecure; if (mConnectionType) { LOG_ALWAYS_FATAL_IF(!displayId); - const auto physicalId = PhysicalDisplayId::tryCast(*displayId); - LOG_ALWAYS_FATAL_IF(!physicalId); + const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId); + LOG_ALWAYS_FATAL_IF(!physicalIdOpt); + const auto physicalId = *physicalIdOpt; + LOG_ALWAYS_FATAL_IF(!mHwcDisplayId); const auto activeMode = modes.get(activeModeId); LOG_ALWAYS_FATAL_IF(!activeMode); + const auto fps = activeMode->get()->getFps(); - state.physical = {.id = *physicalId, + state.physical = {.id = physicalId, .hwcDisplayId = *mHwcDisplayId, .activeMode = activeMode->get()}; - const auto it = mFlinger.mutablePhysicalDisplays() - .emplace_or_replace(*physicalId, mDisplayToken, *physicalId, - *mConnectionType, std::move(modes), - ui::ColorModes(), std::nullopt) - .first; + mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken, + physicalId, *mConnectionType, + std::move(modes), + ui::ColorModes(), + std::nullopt); + + if (mFlinger.scheduler()) { + mFlinger.scheduler()->registerDisplay(physicalId, + display->holdRefreshRateSelector()); + } - display->setActiveMode(activeModeId, it->second.snapshot()); + display->setActiveMode(activeModeId, fps, fps); } mFlinger.mutableCurrentState().displays.add(mDisplayToken, state); diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 6ffc0396d7..1dd4f254fb 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -1320,6 +1320,7 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery); + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom); std::string pulledData; EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); @@ -1327,9 +1328,9 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { SurfaceflingerStatsLayerInfoWrapper atomList; ASSERT_TRUE(atomList.ParseFromString(pulledData)); // The first time record is never uploaded to stats. - ASSERT_EQ(atomList.atom_size(), 3); + ASSERT_EQ(atomList.atom_size(), 4); // Layers are ordered based on the hash in LayerStatsKey. For this test, the order happens to - // be: 0 - Battery 1 - Performance 2 - Standard + // be: 0 - Battery 1 - Custom 2 - Performance 3 - Standard const SurfaceflingerStatsLayerInfo& atom0 = atomList.atom(0); EXPECT_EQ(atom0.layer_name(), genLayerName(LAYER_ID_0)); @@ -1364,7 +1365,7 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { EXPECT_EQ(atom1.uid(), UID_0); EXPECT_EQ(atom1.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0); EXPECT_EQ(atom1.render_rate_bucket(), RENDER_RATE_BUCKET_0); - EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE); + EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM); const SurfaceflingerStatsLayerInfo& atom2 = atomList.atom(2); @@ -1377,12 +1378,30 @@ TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) { EXPECT_THAT(atom2.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1}))); EXPECT_THAT(atom2.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1}))); EXPECT_THAT(atom2.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1}))); - EXPECT_EQ(atom2.late_acquire_frames(), LATE_ACQUIRE_FRAMES); - EXPECT_EQ(atom2.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES); + EXPECT_EQ(atom2.late_acquire_frames(), 0); + EXPECT_EQ(atom2.bad_desired_present_frames(), 0); EXPECT_EQ(atom2.uid(), UID_0); EXPECT_EQ(atom2.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0); EXPECT_EQ(atom2.render_rate_bucket(), RENDER_RATE_BUCKET_0); - EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD); + EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE); + + const SurfaceflingerStatsLayerInfo& atom3 = atomList.atom(3); + + EXPECT_EQ(atom3.layer_name(), genLayerName(LAYER_ID_0)); + EXPECT_EQ(atom3.total_frames(), 1); + EXPECT_EQ(atom3.dropped_frames(), 0); + EXPECT_THAT(atom3.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1}))); + EXPECT_THAT(atom3.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1}))); + EXPECT_THAT(atom3.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1}))); + EXPECT_THAT(atom3.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1}))); + EXPECT_THAT(atom3.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1}))); + EXPECT_THAT(atom3.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1}))); + EXPECT_EQ(atom3.late_acquire_frames(), LATE_ACQUIRE_FRAMES); + EXPECT_EQ(atom3.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES); + EXPECT_EQ(atom3.uid(), UID_0); + EXPECT_EQ(atom3.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0); + EXPECT_EQ(atom3.render_rate_bucket(), RENDER_RATE_BUCKET_0); + EXPECT_EQ(atom3.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD); } TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) { diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 9888f002fb..d84698f279 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -32,6 +32,7 @@ #include "FrontEnd/TransactionHandler.h" #include "TestableSurfaceFlinger.h" +#include "TransactionState.h" #include "mock/MockEventThread.h" #include "mock/MockVsyncController.h" @@ -41,6 +42,8 @@ using testing::_; using testing::Return; using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using frontend::TransactionHandler; + constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5); class TransactionApplicationTest : public testing::Test { public: @@ -359,13 +362,23 @@ public: EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty()); EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size()); - for (const auto& transaction : transactions) { - mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states, - transaction.displays, transaction.flags, - transaction.applyToken, transaction.inputWindowCommands, - transaction.desiredPresentTime, - transaction.isAutoTimestamp, transaction.uncacheBuffer, - mHasListenerCallbacks, mCallbacks, transaction.id); + for (auto transaction : transactions) { + std::vector<ResolvedComposerState> resolvedStates; + resolvedStates.reserve(transaction.states.size()); + for (auto& state : transaction.states) { + resolvedStates.emplace_back(std::move(state)); + } + + TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates, + transaction.displays, transaction.flags, + transaction.applyToken, + transaction.inputWindowCommands, + transaction.desiredPresentTime, + transaction.isAutoTimestamp, + transaction.uncacheBuffer, systemTime(), 0, + mHasListenerCallbacks, mCallbacks, getpid(), + static_cast<int>(getuid()), transaction.id); + mFlinger.setTransactionStateInternal(transactionState); } mFlinger.flushTransactionQueues(); EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty()); diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 1173d1c876..09d002f6e8 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -126,7 +126,7 @@ public: HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false, - dequeueTime, FrameTimelineInfo{}); + dequeueTime, FrameTimelineInfo{}, 0); commitTransaction(layer.get()); nsecs_t latchTime = 25; diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp index 14e1aac793..b6427c0ffb 100644 --- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp @@ -46,14 +46,14 @@ TEST(TransactionProtoParserTest, parse) { size_t layerCount = 2; t1.states.reserve(layerCount); for (uint32_t i = 0; i < layerCount; i++) { - ComposerState s; + ResolvedComposerState s; if (i == 1) { layer.parentSurfaceControlForChild = sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42, "#42"); } s.state = layer; - t1.states.add(s); + t1.states.emplace_back(s); } size_t displayCount = 2; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index ae03db43a7..7dfbcc00d4 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -131,7 +131,7 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -166,7 +166,7 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -183,7 +183,7 @@ public: 2ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -229,7 +229,7 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -264,7 +264,7 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -307,7 +307,7 @@ public: FrameTimelineInfo ftInfo3; ftInfo3.vsyncId = 3; ftInfo3.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3, 0); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -352,7 +352,7 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -367,7 +367,7 @@ public: 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, 0ULL /*usage*/); - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); acquireFence2->signalForTest(12); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -404,7 +404,7 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; @@ -424,7 +424,7 @@ public: FrameTimelineInfo ftInfoInv; ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID; ftInfoInv.inputEventId = 0; - layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv); + layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv, 0); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -445,7 +445,7 @@ public: FrameTimelineInfo ftInfo2; ftInfo2.vsyncId = 2; ftInfo2.inputEventId = 0; - layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2); + layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2, 0); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -494,7 +494,7 @@ public: FrameTimelineInfo ftInfo; ftInfo.vsyncId = 1; ftInfo.inputEventId = 0; - layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo); + layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0); FrameTimelineInfo ftInfo2; ftInfo2.vsyncId = 2; ftInfo2.inputEventId = 0; diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index 2dbcfbdb18..482c3a8e50 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -112,16 +112,16 @@ protected: { TransactionState transaction; transaction.id = 50; - ComposerState layerState; + ResolvedComposerState layerState; layerState.state.surface = fakeLayerHandle; layerState.state.what = layer_state_t::eLayerChanged; layerState.state.z = 42; - transaction.states.add(layerState); - ComposerState childState; + transaction.states.emplace_back(layerState); + ResolvedComposerState childState; childState.state.surface = fakeChildLayerHandle; childState.state.what = layer_state_t::eLayerChanged; childState.state.z = 43; - transaction.states.add(childState); + transaction.states.emplace_back(childState); mTracing.addQueuedTransaction(transaction); std::vector<TransactionState> transactions; @@ -138,12 +138,12 @@ protected: { TransactionState transaction; transaction.id = 51; - ComposerState layerState; + ResolvedComposerState layerState; layerState.state.surface = fakeLayerHandle; layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged; layerState.state.z = 41; layerState.state.x = 22; - transaction.states.add(layerState); + transaction.states.emplace_back(layerState); mTracing.addQueuedTransaction(transaction); std::vector<TransactionState> transactions; @@ -247,16 +247,16 @@ protected: { TransactionState transaction; transaction.id = 50; - ComposerState layerState; + ResolvedComposerState layerState; layerState.state.surface = fakeLayerHandle; layerState.state.what = layer_state_t::eLayerChanged; layerState.state.z = 42; - transaction.states.add(layerState); - ComposerState mirrorState; + transaction.states.emplace_back(layerState); + ResolvedComposerState mirrorState; mirrorState.state.surface = fakeMirrorLayerHandle; mirrorState.state.what = layer_state_t::eLayerChanged; mirrorState.state.z = 43; - transaction.states.add(mirrorState); + transaction.states.emplace_back(mirrorState); mTracing.addQueuedTransaction(transaction); std::vector<TransactionState> transactions; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 2da266bd33..47c2deef51 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -54,6 +54,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } + void setDivisor(unsigned) final {} void dump(std::string&) const final {} private: @@ -91,6 +92,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } + void setDivisor(unsigned) final {} void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index f66075362e..2b86e94244 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -55,6 +55,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); + MOCK_METHOD(void, setDivisor, (unsigned), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 74d2b7d9b6..3095e8aa9a 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -532,6 +532,26 @@ TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) { EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError)); } +TEST_F(VSyncPredictorTest, setDivisorIsRespected) { + auto last = mNow; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod; + last = mNow; + tracker.addVsyncTimestamp(mNow); + } + + tracker.setDivisor(3); + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod)); +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index a35ff96815..8bd689a61d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -50,6 +50,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); + MOCK_METHOD(void, setDivisor, (unsigned), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h index c2c3d77364..5654691884 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h @@ -36,7 +36,7 @@ public: MockAidlPowerHalWrapper(); ~MockAidlPowerHalWrapper() override; MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override)); - MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(bool, notifyDisplayUpdateImminentAndCpuReset, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); MOCK_METHOD(void, restartPowerHintSession, (), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index fb1b3946a4..7fc625c4b6 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -32,7 +32,7 @@ public: MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected), (override)); MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override)); - MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override)); MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h index a8bc994274..ef9cd9bc43 100644 --- a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.gui; +#pragma once -/** @hide */ -parcelable SupportedBufferCombinations { - int[] pixelFormats; - int[] dataspaces; -} +#include <scheduler/FrameRateMode.h> + +// Use a C style macro to keep the line numbers printed in gtest +#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \ + EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode)) diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 5b0c1f38be..6893154259 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -34,6 +34,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); + MOCK_METHOD(void, setDivisor, (unsigned), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp deleted file mode 100644 index 80e9a3c3b1..0000000000 --- a/services/vr/hardware_composer/Android.bp +++ /dev/null @@ -1,134 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -cc_library_shared { - name: "libvr_hwc-hal", - - system_ext_specific: true, - - srcs: [ - "impl/vr_hwc.cpp", - "impl/vr_composer_client.cpp", - ], - - static_libs: [ - "libbroadcastring", - "libdisplay", - ], - - shared_libs: [ - "android.frameworks.vr.composer@2.0", - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.2", - "android.hardware.graphics.composer@2.3", - "android.hardware.graphics.composer@2.1-resources", - "android.hardware.graphics.mapper@2.0", - "android.hardware.graphics.mapper@3.0", - "android.hardware.graphics.mapper@4.0", - "libbase", - "libbufferhubqueue", - "libbinder", - "libcutils", - "libfmq", - "libhardware", - "libhidlbase", - "liblog", - "libsync", - "libui", - "libutils", - "libpdx_default_transport", - ], - - header_libs: [ - "android.hardware.graphics.composer@2.1-command-buffer", - "android.hardware.graphics.composer@2.3-hal", - ], - - export_header_lib_headers: [ - "android.hardware.graphics.composer@2.3-hal", - ], - - export_static_lib_headers: [ - "libdisplay", - ], - - export_shared_lib_headers: [ - "android.frameworks.vr.composer@2.0", - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.2", - "android.hardware.graphics.composer@2.3", - ], - - export_include_dirs: ["."], - - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-Wall", - "-Werror", - "-Wno-error=unused-private-field", - // Warnings in vr_hwc.cpp to be fixed after sync of goog/master. - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - -} - -cc_library_static { - name: "libvr_hwc-impl", - srcs: [ - "vr_composer.cpp", - ], - static_libs: [ - "libvr_hwc-binder", - ], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libui", - "libutils", - "libvr_hwc-hal", - ], - export_shared_lib_headers: [ - "libvr_hwc-hal", - ], - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-Wall", - "-Werror", - ], -} - -cc_test { - name: "vr_hwc_test", - gtest: true, - srcs: ["tests/vr_composer_test.cpp"], - static_libs: [ - "libgtest", - "libvr_hwc-impl", - // NOTE: This needs to be included after the *-impl lib otherwise the - // symbols in the *-binder library get optimized out. - "libvr_hwc-binder", - ], - cflags: [ - "-Wall", - "-Werror", - // warnings in vr_composer_test.cpp to be fixed after merge of goog/master - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libui", - "libutils", - ], -} diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp deleted file mode 100644 index fa71ed7633..0000000000 --- a/services/vr/hardware_composer/aidl/Android.bp +++ /dev/null @@ -1,36 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - -cc_library_static { - name: "libvr_hwc-binder", - srcs: [ - "android/dvr/IVrComposer.aidl", - "android/dvr/IVrComposerCallback.aidl", - "android/dvr/parcelable_composer_frame.cpp", - "android/dvr/parcelable_composer_layer.cpp", - "android/dvr/parcelable_unique_fd.cpp", - ], - aidl: { - local_include_dirs: ["."], - export_aidl_headers: true, - }, - export_include_dirs: ["."], - - cflags: [ - "-Wall", - "-Werror", - ], - - shared_libs: [ - "libbinder", - "libui", - "libutils", - "libvr_hwc-hal", - ], -} diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl deleted file mode 100644 index be1ec5b2a3..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl +++ /dev/null @@ -1,25 +0,0 @@ -package android.dvr; - -import android.dvr.IVrComposerCallback; - -/** - * Service interface exposed by VR HWC exposed to system apps which allows one - * system app to connect to get SurfaceFlinger's outputs (all displays). This - * is active when SurfaceFlinger is in VR mode, where all 2D output is - * redirected to VR HWC. - * - * @hide */ -interface IVrComposer -{ - const String SERVICE_NAME = "vr_hwc"; - - /** - * Registers a callback used to receive frame notifications. - */ - void registerObserver(in IVrComposerCallback callback); - - /** - * Clears a previously registered frame notification callback. - */ - void clearObserver(); -} diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl deleted file mode 100644 index aa70de1645..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl +++ /dev/null @@ -1,22 +0,0 @@ -package android.dvr; - -import android.dvr.ParcelableComposerFrame; -import android.dvr.ParcelableUniqueFd; - -/** - * A system app will implement and register this callback with VRComposer - * to receive the layers SurfaceFlinger presented when in VR mode. - * - * @hide */ -interface IVrComposerCallback { - /** - * Called by the VR HWC service when a new frame is ready to be presented. - * - * @param frame The new frame VR HWC wants to present. - * @return A fence FD used to signal when the previous frame is no longer - * used by the client. This may be an invalid fence (-1) if the client is not - * using the previous frame, in which case the previous frame may be re-used - * at any point in time. - */ - ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame); -} diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl deleted file mode 100644 index 84abc19c23..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.dvr; - -parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h"; diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl deleted file mode 100644 index a200345fbd..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.dvr; - -parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h"; diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl deleted file mode 100644 index eee9d138ba..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.dvr; - -parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h"; diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp deleted file mode 100644 index db7d5dc225..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "aidl/android/dvr/parcelable_composer_frame.h" - -#include <binder/Parcel.h> - -#include "aidl/android/dvr/parcelable_composer_layer.h" - -namespace android { -namespace dvr { - -ParcelableComposerFrame::ParcelableComposerFrame() {} - -ParcelableComposerFrame::ParcelableComposerFrame( - const ComposerView::Frame& frame) - : frame_(frame) {} - -ParcelableComposerFrame::~ParcelableComposerFrame() {} - -status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const { - status_t ret = parcel->writeUint64(frame_.display_id); - if (ret != OK) return ret; - - ret = parcel->writeInt32(frame_.display_width); - if (ret != OK) return ret; - - ret = parcel->writeInt32(frame_.display_height); - if (ret != OK) return ret; - - ret = parcel->writeBool(frame_.removed); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config)); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode)); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode)); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled)); - if (ret != OK) return ret; - - ret = parcel->writeInt32(frame_.color_transform_hint); - if (ret != OK) return ret; - - for(size_t i = 0; i < 16; i++) { - ret = parcel->writeFloat(frame_.color_transform[i]); - if (ret != OK) return ret; - } - - std::vector<ParcelableComposerLayer> layers; - for (size_t i = 0; i < frame_.layers.size(); ++i) - layers.push_back(ParcelableComposerLayer(frame_.layers[i])); - - ret = parcel->writeParcelableVector(layers); - - return ret; -} - -status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) { - status_t ret = parcel->readUint64(&frame_.display_id); - if (ret != OK) return ret; - - ret = parcel->readInt32(&frame_.display_width); - if (ret != OK) return ret; - - ret = parcel->readInt32(&frame_.display_height); - if (ret != OK) return ret; - - ret = parcel->readBool(&frame_.removed); - if (ret != OK) return ret; - - uint32_t value; - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.active_config = static_cast<Config>(value); - - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.color_mode = static_cast<ColorMode>(value); - - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.power_mode = static_cast<IComposerClient::PowerMode>(value); - - ret = parcel->readUint32(&value); - if (ret != OK) return ret; - frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value); - - ret = parcel->readInt32(&frame_.color_transform_hint); - if (ret != OK) return ret; - - for(size_t i = 0; i < 16; i++) { - ret = parcel->readFloat(&frame_.color_transform[i]); - if (ret != OK) return ret; - } - - std::vector<ParcelableComposerLayer> layers; - ret = parcel->readParcelableVector(&layers); - if (ret != OK) return ret; - - frame_.layers.clear(); - for (size_t i = 0; i < layers.size(); ++i) - frame_.layers.push_back(layers[i].layer()); - - return ret; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h deleted file mode 100644 index a82df7f2e7..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H -#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H - -#include <binder/Parcelable.h> -#include <impl/vr_hwc.h> - -namespace android { -namespace dvr { - -class ParcelableComposerFrame : public Parcelable { - public: - ParcelableComposerFrame(); - explicit ParcelableComposerFrame(const ComposerView::Frame& frame); - ~ParcelableComposerFrame() override; - - ComposerView::Frame frame() const { return frame_; } - - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - private: - ComposerView::Frame frame_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp deleted file mode 100644 index c3621ebf0f..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include "aidl/android/dvr/parcelable_composer_layer.h" - -#include <binder/Parcel.h> -#include <ui/Fence.h> -#include <ui/GraphicBuffer.h> -#include <ui/GraphicBufferMapper.h> - -namespace android { -namespace dvr { - -ParcelableComposerLayer::ParcelableComposerLayer() {} - -ParcelableComposerLayer::ParcelableComposerLayer( - const ComposerView::ComposerLayer& layer) : layer_(layer) {} - -ParcelableComposerLayer::~ParcelableComposerLayer() {} - -status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const { - status_t ret = parcel->writeUint64(layer_.id); - if (ret != OK) return ret; - - ret = parcel->write(*layer_.buffer); - if (ret != OK) return ret; - - ret = parcel->writeBool(layer_.fence->isValid()); - if (ret != OK) return ret; - - if (layer_.fence->isValid()) { - ret = parcel->writeFileDescriptor(layer_.fence->dup(), true); - if (ret != OK) return ret; - } - - ret = parcel->writeInt32(layer_.display_frame.left); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.display_frame.top); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.display_frame.right); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.display_frame.bottom); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.left); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.top); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.right); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.crop.bottom); - if (ret != OK) return ret; - - ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode)); - if (ret != OK) return ret; - - ret = parcel->writeFloat(layer_.alpha); - if (ret != OK) return ret; - - ret = parcel->writeUint32(layer_.type); - if (ret != OK) return ret; - - ret = parcel->writeUint32(layer_.app_id); - if (ret != OK) return ret; - - ret = parcel->writeUint32(layer_.z_order); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.cursor_x); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.cursor_y); - if (ret != OK) return ret; - - uint32_t color = layer_.color.r | - (static_cast<uint32_t>(layer_.color.g) << 8) | - (static_cast<uint32_t>(layer_.color.b) << 16) | - (static_cast<uint32_t>(layer_.color.a) << 24); - ret = parcel->writeUint32(color); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.dataspace); - if (ret != OK) return ret; - - ret = parcel->writeInt32(layer_.transform); - if (ret != OK) return ret; - - ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size())); - if (ret != OK) return ret; - - for (auto& rect: layer_.visible_regions) { - ret = parcel->writeInt32(rect.left); - ret = parcel->writeInt32(rect.top); - ret = parcel->writeInt32(rect.right); - ret = parcel->writeInt32(rect.bottom); - if (ret != OK) return ret; - } - - ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size())); - if (ret != OK) return ret; - - for (auto& rect: layer_.damaged_regions) { - ret = parcel->writeInt32(rect.left); - ret = parcel->writeInt32(rect.top); - ret = parcel->writeInt32(rect.right); - ret = parcel->writeInt32(rect.bottom); - if (ret != OK) return ret; - } - - return OK; -} - -status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) { - status_t ret = parcel->readUint64(&layer_.id); - if (ret != OK) return ret; - - layer_.buffer = new GraphicBuffer(); - ret = parcel->read(*layer_.buffer); - if (ret != OK) { - layer_.buffer.clear(); - return ret; - } - - bool has_fence = 0; - ret = parcel->readBool(&has_fence); - if (ret != OK) return ret; - - if (has_fence) - layer_.fence = new Fence(dup(parcel->readFileDescriptor())); - else - layer_.fence = new Fence(); - - ret = parcel->readInt32(&layer_.display_frame.left); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.display_frame.top); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.display_frame.right); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.display_frame.bottom); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.left); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.top); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.right); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.crop.bottom); - if (ret != OK) return ret; - - ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode)); - if (ret != OK) return ret; - - ret = parcel->readFloat(&layer_.alpha); - if (ret != OK) return ret; - - ret = parcel->readUint32(&layer_.type); - if (ret != OK) return ret; - - ret = parcel->readUint32(&layer_.app_id); - if (ret != OK) return ret; - - ret = parcel->readUint32(&layer_.z_order); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.cursor_x); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.cursor_y); - if (ret != OK) return ret; - - uint32_t color; - ret = parcel->readUint32(&color); - if (ret != OK) return ret; - layer_.color.r = color & 0xFF; - layer_.color.g = (color >> 8) & 0xFF; - layer_.color.b = (color >> 16) & 0xFF; - layer_.color.a = (color >> 24) & 0xFF; - - ret = parcel->readInt32(&layer_.dataspace); - if (ret != OK) return ret; - - ret = parcel->readInt32(&layer_.transform); - if (ret != OK) return ret; - - uint32_t size; - ret = parcel->readUint32(&size); - if (ret != OK) return ret; - - for(size_t i = 0; i < size; i++) { - hwc_rect_t rect; - ret = parcel->readInt32(&rect.left); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.top); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.right); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.bottom); - if (ret != OK) return ret; - - layer_.visible_regions.push_back(rect); - } - - ret = parcel->readUint32(&size); - if (ret != OK) return ret; - - for(size_t i = 0; i < size; i++) { - hwc_rect_t rect; - ret = parcel->readInt32(&rect.left); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.top); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.right); - if (ret != OK) return ret; - - ret = parcel->readInt32(&rect.bottom); - if (ret != OK) return ret; - - layer_.damaged_regions.push_back(rect); - } - - return OK; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h deleted file mode 100644 index 6d2ac097e5..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H -#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H - -#include <binder/Parcelable.h> -#include <impl/vr_hwc.h> - -#include <memory> - -namespace android { -namespace dvr { - -class ParcelableComposerLayer : public Parcelable { - public: - ParcelableComposerLayer(); - explicit ParcelableComposerLayer(const ComposerView::ComposerLayer& layer); - ~ParcelableComposerLayer() override; - - ComposerView::ComposerLayer layer() const { return layer_; } - - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - private: - ComposerView::ComposerLayer layer_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp deleted file mode 100644 index 9486f3c919..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "android/dvr/parcelable_unique_fd.h" - -#include <binder/Parcel.h> - -namespace android { -namespace dvr { - -ParcelableUniqueFd::ParcelableUniqueFd() {} - -ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence) - : fence_(dup(fence.get())) {} - -ParcelableUniqueFd::~ParcelableUniqueFd() {} - -status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const { - status_t ret = parcel->writeBool(fence_.get() >= 0); - if (ret != OK) return ret; - - if (fence_.get() >= 0) - ret = parcel->writeUniqueFileDescriptor(fence_); - - return ret; -} - -status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) { - bool has_fence = 0; - status_t ret = parcel->readBool(&has_fence); - if (ret != OK) return ret; - - if (has_fence) - ret = parcel->readUniqueFileDescriptor(&fence_); - - return ret; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h deleted file mode 100644 index c4216f6212..0000000000 --- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H -#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H - -#include <android-base/unique_fd.h> -#include <binder/Parcelable.h> - -namespace android { -namespace dvr { - -// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the -// case where the FD is invalid (-1), unlike FileDescriptor which expects a -// valid FD. -class ParcelableUniqueFd : public Parcelable { - public: - ParcelableUniqueFd(); - explicit ParcelableUniqueFd(const base::unique_fd& fence); - ~ParcelableUniqueFd() override; - - void set_fence(const base::unique_fd& fence) { - fence_.reset(dup(fence.get())); - } - base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); } - - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - private: - base::unique_fd fence_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp deleted file mode 100644 index dd1603d4a9..0000000000 --- a/services/vr/hardware_composer/impl/vr_composer_client.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2016 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 <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#include <hardware/gralloc.h> -#include <hardware/gralloc1.h> -#include <log/log.h> - -#include <memory> - -#include "impl/vr_hwc.h" -#include "impl/vr_composer_client.h" - -namespace android { -namespace dvr { - -using android::frameworks::vr::composer::V2_0::IVrComposerClient; - -VrComposerClient::VrComposerClient(dvr::VrHwc& hal) - : ComposerClient(&hal), mVrHal(hal) { - if (!init()) { - LOG_ALWAYS_FATAL("failed to initialize VrComposerClient"); - } -} - -VrComposerClient::~VrComposerClient() {} - -std::unique_ptr<ComposerCommandEngine> -VrComposerClient::createCommandEngine() { - return std::make_unique<VrCommandEngine>(*this); -} - -VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client) - : ComposerCommandEngine(client.mHal, client.mResources.get()), - mVrHal(client.mVrHal) {} - -VrComposerClient::VrCommandEngine::~VrCommandEngine() {} - -bool VrComposerClient::VrCommandEngine::executeCommand( - hardware::graphics::composer::V2_1::IComposerClient::Command command, - uint16_t length) { - IVrComposerClient::VrCommand vrCommand = - static_cast<IVrComposerClient::VrCommand>(command); - switch (vrCommand) { - case IVrComposerClient::VrCommand::SET_LAYER_INFO: - return executeSetLayerInfo(length); - case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA: - return executeSetClientTargetMetadata(length); - case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA: - return executeSetLayerBufferMetadata(length); - default: - return ComposerCommandEngine::executeCommand(command, length); - } -} - -bool VrComposerClient::VrCommandEngine::executeSetLayerInfo(uint16_t length) { - if (length != 2) { - return false; - } - - auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read()); - if (err != Error::NONE) { - mWriter->setError(getCommandLoc(), err); - } - - return true; -} - -bool VrComposerClient::VrCommandEngine::executeSetClientTargetMetadata( - uint16_t length) { - if (length != 7) - return false; - - auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata()); - if (err != Error::NONE) - mWriter->setError(getCommandLoc(), err); - - return true; -} - -bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata( - uint16_t length) { - if (length != 7) - return false; - - auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer, - readBufferMetadata()); - if (err != Error::NONE) - mWriter->setError(getCommandLoc(), err); - - return true; -} - -IVrComposerClient::BufferMetadata -VrComposerClient::VrCommandEngine::readBufferMetadata() { - IVrComposerClient::BufferMetadata metadata = { - .width = read(), - .height = read(), - .stride = read(), - .layerCount = read(), - .format = - static_cast<android::hardware::graphics::common::V1_2::PixelFormat>( - readSigned()), - .usage = read64(), - }; - return metadata; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h deleted file mode 100644 index 1b2b5f4f56..0000000000 --- a/services/vr/hardware_composer/impl/vr_composer_client.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2017 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. - */ - -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H -#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H - -#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#include <composer-command-buffer/2.3/ComposerCommandBuffer.h> -#include <composer-hal/2.1/ComposerClient.h> -#include <composer-hal/2.1/ComposerCommandEngine.h> -#include <composer-hal/2.2/ComposerClient.h> -#include <composer-hal/2.3/ComposerClient.h> - -namespace android { -namespace dvr { - -class VrHwc; - -using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine; -using hardware::graphics::composer::V2_3::hal::ComposerHal; -using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl; - -using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>; - -class VrComposerClient : public ComposerClient { - public: - explicit VrComposerClient(android::dvr::VrHwc& hal); - virtual ~VrComposerClient(); - - private: - class VrCommandEngine : public ComposerCommandEngine { - public: - explicit VrCommandEngine(VrComposerClient& client); - ~VrCommandEngine() override; - - bool executeCommand( - hardware::graphics::composer::V2_1::IComposerClient::Command command, - uint16_t length) override; - - private: - bool executeSetLayerInfo(uint16_t length); - bool executeSetClientTargetMetadata(uint16_t length); - bool executeSetLayerBufferMetadata(uint16_t length); - - IVrComposerClient::BufferMetadata readBufferMetadata(); - - android::dvr::VrHwc& mVrHal; - - VrCommandEngine(const VrCommandEngine&) = delete; - void operator=(const VrCommandEngine&) = delete; - }; - - VrComposerClient(const VrComposerClient&) = delete; - void operator=(const VrComposerClient&) = delete; - - std::unique_ptr<ComposerCommandEngine> createCommandEngine() override; - dvr::VrHwc& mVrHal; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp deleted file mode 100644 index e530b16b1b..0000000000 --- a/services/vr/hardware_composer/impl/vr_hwc.cpp +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * Copyright 2016 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 "impl/vr_hwc.h" - -#include "android-base/stringprintf.h" -#include <binder/IServiceManager.h> -#include <cutils/properties.h> -#include <private/dvr/display_client.h> -#include <ui/Fence.h> -#include <utils/Trace.h> - -#include <mutex> - -#include "vr_composer_client.h" - -using namespace android::hardware::graphics::common::V1_0; -using namespace android::hardware::graphics::composer::V2_3; - -using android::base::StringPrintf; -using android::hardware::hidl_handle; -using android::hardware::hidl_string; -using android::hardware::hidl_vec; -using android::hardware::Return; -using android::hardware::Void; - -namespace types = android::hardware::graphics::common; - -namespace android { -namespace dvr { -namespace { - -const Display kDefaultDisplayId = 1; -const Config kDefaultConfigId = 1; - -sp<GraphicBuffer> CreateGraphicBuffer( - const native_handle_t* handle, - const IVrComposerClient::BufferMetadata& metadata) { - sp<GraphicBuffer> buffer = new GraphicBuffer( - handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height, - static_cast<int32_t>(metadata.format), metadata.layerCount, - metadata.usage, metadata.stride); - if (buffer->initCheck() != OK) { - ALOGE("Failed to create graphic buffer"); - return nullptr; - } - - return buffer; -} - -void GetPrimaryDisplaySize(int32_t* width, int32_t* height) { - *width = 1080; - *height = 1920; - - int error = 0; - auto display_client = display::DisplayClient::Create(&error); - if (!display_client) { - ALOGE("Could not connect to display service : %s(%d)", strerror(error), - error); - return; - } - - auto status = display_client->GetDisplayMetrics(); - if (!status) { - ALOGE("Could not get display metrics from display service : %s(%d)", - status.GetErrorMessage().c_str(), status.error()); - return; - } - - *width = status.get().display_width; - *height = status.get().display_height; -} - -} // namespace - -HwcDisplay::HwcDisplay(int32_t width, int32_t height) - : width_(width), height_(height) {} - -HwcDisplay::~HwcDisplay() {} - -bool HwcDisplay::SetClientTarget(const native_handle_t* handle, - base::unique_fd fence) { - if (handle) - buffer_ = CreateGraphicBuffer(handle, buffer_metadata_); - - fence_ = new Fence(fence.release()); - return true; -} - -void HwcDisplay::SetClientTargetMetadata( - const IVrComposerClient::BufferMetadata& metadata) { - buffer_metadata_ = metadata; -} - -HwcLayer* HwcDisplay::CreateLayer() { - uint64_t layer_id = layer_ids_++; - layers_.push_back(HwcLayer(layer_id)); - return &layers_.back(); -} - -HwcLayer* HwcDisplay::GetLayer(Layer id) { - for (size_t i = 0; i < layers_.size(); ++i) - if (layers_[i].info.id == id) - return &layers_[i]; - - return nullptr; -} - -bool HwcDisplay::DestroyLayer(Layer id) { - for (auto it = layers_.begin(); it != layers_.end(); ++it) { - if (it->info.id == id) { - layers_.erase(it); - return true; - } - } - - return false; -} - -void HwcDisplay::GetChangedCompositionTypes( - std::vector<Layer>* layer_ids, - std::vector<IComposerClient::Composition>* types) { - std::sort(layers_.begin(), layers_.end(), - [](const auto& lhs, const auto& rhs) { - return lhs.info.z_order < rhs.info.z_order; - }); - - const size_t no_layer = std::numeric_limits<size_t>::max(); - size_t first_client_layer = no_layer, last_client_layer = no_layer; - for (size_t i = 0; i < layers_.size(); ++i) { - switch (layers_[i].composition_type) { - case IComposerClient::Composition::SOLID_COLOR: - case IComposerClient::Composition::CURSOR: - case IComposerClient::Composition::SIDEBAND: - if (first_client_layer == no_layer) - first_client_layer = i; - - last_client_layer = i; - break; - default: - break; - } - } - - for (size_t i = 0; i < layers_.size(); ++i) { - if (i >= first_client_layer && i <= last_client_layer) { - if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) { - layer_ids->push_back(layers_[i].info.id); - types->push_back(IComposerClient::Composition::CLIENT); - layers_[i].composition_type = IComposerClient::Composition::CLIENT; - } - - continue; - } - - if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) { - layer_ids->push_back(layers_[i].info.id); - types->push_back(IComposerClient::Composition::DEVICE); - layers_[i].composition_type = IComposerClient::Composition::DEVICE; - } - } -} - -Error HwcDisplay::GetFrame( - std::vector<ComposerView::ComposerLayer>* out_frames) { - bool queued_client_target = false; - std::vector<ComposerView::ComposerLayer> frame; - for (const auto& layer : layers_) { - if (layer.composition_type == IComposerClient::Composition::CLIENT) { - if (queued_client_target) - continue; - - if (!buffer_.get()) { - ALOGE("Client composition requested but no client target buffer"); - return Error::BAD_LAYER; - } - - ComposerView::ComposerLayer client_target_layer = { - .buffer = buffer_, - .fence = fence_.get() ? fence_ : new Fence(-1), - .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()), - static_cast<int32_t>(buffer_->getHeight())}, - .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()), - static_cast<float>(buffer_->getHeight())}, - .blend_mode = IComposerClient::BlendMode::NONE, - }; - - frame.push_back(client_target_layer); - queued_client_target = true; - } else { - if (!layer.info.buffer.get() || !layer.info.fence.get()) { - ALOGV("Layer requested without valid buffer"); - continue; - } - - frame.push_back(layer.info); - } - } - - out_frames->swap(frame); - return Error::NONE; -} - -std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() { - std::vector<Layer> last_frame_layers; - last_frame_layers.swap(last_frame_layers_ids_); - - for (const auto& layer : layers_) - last_frame_layers_ids_.push_back(layer.info.id); - - return last_frame_layers; -} - -void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) { - color_transform_hint_ = hint; - if (matrix) - memcpy(color_transform_, matrix, sizeof(color_transform_)); -} - -void HwcDisplay::dumpDebugInfo(std::string* result) const { - if (!result) { - return; - } - *result += StringPrintf("HwcDisplay: width: %d, height: %d, layers size: %zu, colormode: %d\ - , config: %d\n", width_, height_, layers_.size(), color_mode_, active_config_); - *result += StringPrintf("HwcDisplay buffer metadata: width: %d, height: %d, stride: %d,\ - layerCount: %d, pixelFormat: %d\n", buffer_metadata_.width, buffer_metadata_.height, - buffer_metadata_.stride, buffer_metadata_.layerCount, buffer_metadata_.format); - for (const auto& layer : layers_) { - layer.dumpDebugInfo(result); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// VrHwcClient - -VrHwc::VrHwc() { - vsync_callback_ = new VsyncCallback; -} - -VrHwc::~VrHwc() { - vsync_callback_->SetEventCallback(nullptr); -} - -bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; } - -void VrHwc::registerEventCallback(EventCallback* callback) { - std::unique_lock<std::mutex> lock(mutex_); - event_callback_ = callback; - int32_t width, height; - GetPrimaryDisplaySize(&width, &height); - // Create the primary display late to avoid initialization issues between - // VR HWC and SurfaceFlinger. - displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height)); - - // Surface flinger will make calls back into vr_hwc when it receives the - // onHotplug() call, so it's important to release mutex_ here. - lock.unlock(); - event_callback_->onHotplug(kDefaultDisplayId, - hardware::graphics::composer::V2_1:: - IComposerCallback::Connection::CONNECTED); - lock.lock(); - UpdateVsyncCallbackEnabledLocked(); -} - -void VrHwc::unregisterEventCallback() { - std::lock_guard<std::mutex> guard(mutex_); - event_callback_ = nullptr; - UpdateVsyncCallbackEnabledLocked(); -} - -uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; } - -Error VrHwc::destroyVirtualDisplay(Display display) { - std::lock_guard<std::mutex> guard(mutex_); - if (display == kDefaultDisplayId || displays_.erase(display) == 0) - return Error::BAD_DISPLAY; - ComposerView::Frame frame; - frame.display_id = display; - frame.removed = true; - if (observer_) - observer_->OnNewFrame(frame); - return Error::NONE; -} - -Error VrHwc::createLayer(Display display, Layer* outLayer) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* layer = display_ptr->CreateLayer(); - *outLayer = layer->info.id; - return Error::NONE; -} - -Error VrHwc::destroyLayer(Display display, Layer layer) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) { - return Error::BAD_DISPLAY; - } - - return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER; -} - -Error VrHwc::getActiveConfig(Display display, Config* outConfig) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - *outConfig = kDefaultConfigId; - return Error::NONE; -} - -Error VrHwc::getDisplayAttribute(Display display, Config config, - IComposerClient::Attribute attribute, - int32_t* outValue) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) { - return Error::BAD_DISPLAY; - } - if (config != kDefaultConfigId) { - return Error::BAD_CONFIG; - } - - switch (attribute) { - case IComposerClient::Attribute::WIDTH: - *outValue = display_ptr->width(); - break; - case IComposerClient::Attribute::HEIGHT: - *outValue = display_ptr->height(); - break; - case IComposerClient::Attribute::VSYNC_PERIOD: - { - int error = 0; - auto display_client = display::DisplayClient::Create(&error); - if (!display_client) { - ALOGE("Could not connect to display service : %s(%d)", - strerror(error), error); - // Return a default value of 30 fps - *outValue = 1000 * 1000 * 1000 / 30; - } else { - auto metrics = display_client->GetDisplayMetrics(); - *outValue = metrics.get().vsync_period_ns; - } - } - break; - case IComposerClient::Attribute::DPI_X: - case IComposerClient::Attribute::DPI_Y: - { - constexpr int32_t kDefaultDPI = 300; - int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI); - if (dpi <= 0) { - dpi = kDefaultDPI; - } - *outValue = 1000 * dpi; - } - break; - default: - return Error::BAD_PARAMETER; - } - - return Error::NONE; -} - -Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - std::vector<Config> configs(1, kDefaultConfigId); - *outConfigs = hidl_vec<Config>(configs); - return Error::NONE; -} - -Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) { - *outName = hidl_string(); - return Error::NONE; -} - -Error VrHwc::getDisplayType(Display display, - IComposerClient::DisplayType* outType) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) { - *outType = IComposerClient::DisplayType::INVALID; - return Error::BAD_DISPLAY; - } - - if (display == kDefaultDisplayId) - *outType = IComposerClient::DisplayType::PHYSICAL; - else - *outType = IComposerClient::DisplayType::VIRTUAL; - - return Error::NONE; -} - -Error VrHwc::getDozeSupport(Display display, bool* outSupport) { - *outSupport = false; - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - return Error::NONE; -} - -Error VrHwc::setActiveConfig(Display display, Config config) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - if (config != kDefaultConfigId) - return Error::BAD_CONFIG; - - display_ptr->set_active_config(config); - return Error::NONE; -} - -Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (enabled != IComposerClient::Vsync::ENABLE && - enabled != IComposerClient::Vsync::DISABLE) { - return Error::BAD_PARAMETER; - } - - Error set_vsync_result = Error::NONE; - if (display == kDefaultDisplayId) { - sp<IVsyncService> vsync_service = interface_cast<IVsyncService>( - defaultServiceManager()->getService( - String16(IVsyncService::GetServiceName()))); - if (vsync_service == nullptr) { - ALOGE("Failed to get vsync service"); - return Error::NO_RESOURCES; - } - - if (enabled == IComposerClient::Vsync::ENABLE) { - ALOGI("Enable vsync"); - display_ptr->set_vsync_enabled(true); - status_t result = vsync_service->registerCallback(vsync_callback_); - if (result != OK) { - ALOGE("%s service registerCallback() failed: %s (%d)", - IVsyncService::GetServiceName(), strerror(-result), result); - set_vsync_result = Error::NO_RESOURCES; - } - } else if (enabled == IComposerClient::Vsync::DISABLE) { - ALOGI("Disable vsync"); - display_ptr->set_vsync_enabled(false); - status_t result = vsync_service->unregisterCallback(vsync_callback_); - if (result != OK) { - ALOGE("%s service unregisterCallback() failed: %s (%d)", - IVsyncService::GetServiceName(), strerror(-result), result); - set_vsync_result = Error::NO_RESOURCES; - } - } - - UpdateVsyncCallbackEnabledLocked(); - } - - return set_vsync_result; -} - -Error VrHwc::setColorTransform(Display display, const float* matrix, - int32_t hint) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - display_ptr->SetColorTransform(matrix, hint); - return Error::NONE; -} - -Error VrHwc::setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t /* dataspace */, - const std::vector<hwc_rect_t>& /* damage */) { - base::unique_fd fence(acquireFence); - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (target == nullptr) - return Error::NONE; - - if (!display_ptr->SetClientTarget(target, std::move(fence))) - return Error::BAD_PARAMETER; - - return Error::NONE; -} - -Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */, - int32_t releaseFence) { - base::unique_fd fence(releaseFence); - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - // TODO(dnicoara): Is it necessary to do anything here? - return Error::NONE; -} - -Error VrHwc::validateDisplay( - Display display, std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* /* outDisplayRequestMask */, - std::vector<Layer>* /* outRequestedLayers */, - std::vector<uint32_t>* /* outRequestMasks */) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - display_ptr->GetChangedCompositionTypes(outChangedLayers, - outCompositionTypes); - return Error::NONE; -} - -Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; } - -Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence, - std::vector<Layer>* outLayers, - std::vector<int32_t>* outReleaseFences) { - *outPresentFence = -1; - outLayers->clear(); - outReleaseFences->clear(); - - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - - if (!display_ptr) - return Error::BAD_DISPLAY; - - ComposerView::Frame frame; - std::vector<Layer> last_frame_layers; - Error status = display_ptr->GetFrame(&frame.layers); - frame.display_id = display; - frame.display_width = display_ptr->width(); - frame.display_height = display_ptr->height(); - frame.active_config = display_ptr->active_config(); - frame.power_mode = display_ptr->power_mode(); - frame.vsync_enabled = display_ptr->vsync_enabled() ? - IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE; - frame.color_transform_hint = display_ptr->color_transform_hint(); - frame.color_mode = display_ptr->color_mode(); - memcpy(frame.color_transform, display_ptr->color_transform(), - sizeof(frame.color_transform)); - if (status != Error::NONE) - return status; - - last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers(); - - base::unique_fd fence; - if (observer_) - fence = observer_->OnNewFrame(frame); - - if (fence.get() < 0) - return Error::NONE; - - *outPresentFence = dup(fence.get()); - outLayers->swap(last_frame_layers); - for (size_t i = 0; i < outLayers->size(); ++i) - outReleaseFences->push_back(dup(fence.get())); - - return Error::NONE; -} - -Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x, - int32_t y) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.cursor_x = x; - hwc_layer->info.cursor_y = y; - return Error::NONE; -} - -Error VrHwc::setLayerBuffer(Display display, Layer layer, - buffer_handle_t buffer, int32_t acquireFence) { - base::unique_fd fence(acquireFence); - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.buffer = CreateGraphicBuffer( - buffer, hwc_layer->buffer_metadata); - hwc_layer->info.fence = new Fence(fence.release()); - - return Error::NONE; -} - -Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer, - const std::vector<hwc_rect_t>& damage) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.damaged_regions = damage; - return Error::NONE; -} - -Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.blend_mode = - static_cast<ComposerView::ComposerLayer::BlendMode>(mode); - - return Error::NONE; -} - -Error VrHwc::setLayerColor(Display display, Layer layer, - IComposerClient::Color color) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.color = color; - return Error::NONE; -} - -Error VrHwc::setLayerCompositionType(Display display, Layer layer, - int32_t type) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type); - - return Error::NONE; -} - -Error VrHwc::setLayerDataspace(Display display, Layer layer, - int32_t dataspace) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.dataspace = dataspace; - return Error::NONE; -} - -Error VrHwc::setLayerDisplayFrame(Display display, Layer layer, - const hwc_rect_t& frame) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.display_frame = - {frame.left, frame.top, frame.right, frame.bottom}; - - return Error::NONE; -} - -Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.alpha = alpha; - - return Error::NONE; -} - -Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */, - buffer_handle_t /* stream */) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - return Error::NONE; -} - -Error VrHwc::setLayerSourceCrop(Display display, Layer layer, - const hwc_frect_t& crop) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom}; - - return Error::NONE; -} - -Error VrHwc::setLayerTransform(Display display, Layer layer, - int32_t transform) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.transform = transform; - return Error::NONE; -} - -Error VrHwc::setLayerVisibleRegion(Display display, Layer layer, - const std::vector<hwc_rect_t>& visible) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.visible_regions = visible; - return Error::NONE; -} - -Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.z_order = z; - - return Error::NONE; -} - -Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type, - uint32_t appId) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->info.type = type; - hwc_layer->info.app_id = appId; - - return Error::NONE; -} - -Error VrHwc::setClientTargetMetadata( - Display display, const IVrComposerClient::BufferMetadata& metadata) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - display_ptr->SetClientTargetMetadata(metadata); - - return Error::NONE; -} - -Error VrHwc::setLayerBufferMetadata( - Display display, Layer layer, - const IVrComposerClient::BufferMetadata& metadata) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - HwcLayer* hwc_layer = display_ptr->GetLayer(layer); - if (!hwc_layer) - return Error::BAD_LAYER; - - hwc_layer->buffer_metadata = metadata; - - return Error::NONE; -} - -Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) { - hidl_cb(hidl_vec<Capability>()); - return Void(); -} - -Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { - std::string result; - - { - std::lock_guard<std::mutex> guard(mutex_); - result = "\nVrHwc states:\n"; - for (const auto& pair : displays_) { - result += StringPrintf("Display id: %lu\n", (unsigned long)pair.first); - pair.second->dumpDebugInfo(&result); - } - result += "\n"; - } - - hidl_cb(hidl_string(result)); - return Void(); -} - -Return<void> VrHwc::createClient(createClient_cb hidl_cb) { - std::lock_guard<std::mutex> guard(mutex_); - - Error status = Error::NONE; - sp<VrComposerClient> client; - if (!client_.promote().get()) { - client = new VrComposerClient(*this); - } else { - ALOGE("Already have a client"); - status = Error::NO_RESOURCES; - } - - client_ = client; - hidl_cb(status, client); - return Void(); -} - -Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) { - std::lock_guard<std::mutex> guard(mutex_); - - Error status = Error::NONE; - sp<VrComposerClient> client; - if (!client_.promote().get()) { - client = new VrComposerClient(*this); - } else { - ALOGE("Already have a client"); - status = Error::NO_RESOURCES; - } - - client_ = client; - hidl_cb(status, client); - return Void(); -} - -void VrHwc::ForceDisplaysRefresh() { - std::lock_guard<std::mutex> guard(mutex_); - if (event_callback_ != nullptr) { - for (const auto& pair : displays_) - event_callback_->onRefresh(pair.first); - } -} - -void VrHwc::RegisterObserver(Observer* observer) { - std::lock_guard<std::mutex> guard(mutex_); - if (observer_) - ALOGE("Overwriting observer"); - else - observer_ = observer; -} - -void VrHwc::UnregisterObserver(Observer* observer) { - std::lock_guard<std::mutex> guard(mutex_); - if (observer != observer_) - ALOGE("Trying to unregister unknown observer"); - else - observer_ = nullptr; -} - -HwcDisplay* VrHwc::FindDisplay(Display display) { - auto iter = displays_.find(display); - return iter == displays_.end() ? nullptr : iter->second.get(); -} - -void VrHwc::UpdateVsyncCallbackEnabledLocked() { - auto primary_display = FindDisplay(kDefaultDisplayId); - LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr, - "Should have created the primary display by now"); - bool send_vsync = - event_callback_ != nullptr && primary_display->vsync_enabled(); - vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr); -} - -Return<void> VrHwc::debug(const hidl_handle& fd, - const hidl_vec<hidl_string>& args) { - std::string result; - - { - std::lock_guard<std::mutex> guard(mutex_); - for (const auto& pair : displays_) { - result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first)); - pair.second->dumpDebugInfo(&result); - } - result += "\n"; - } - - FILE* out = fdopen(dup(fd->data[0]), "w"); - fprintf(out, "%s", result.c_str()); - fclose(out); - - return Void(); -} - -void HwcLayer::dumpDebugInfo(std::string* result) const { - if (!result) { - return; - } - *result += StringPrintf("Layer: composition_type: %d, type: %d, app_id: %d, z_order: %d,\ - cursor_x: %d, cursor_y: %d, color(rgba): (%d,%d,%d,%d), dataspace: %d, transform: %d,\ - display_frame(LTRB): (%d,%d,%d,%d), crop(LTRB): (%.1f,%.1f,%.1f,%.1f), blend_mode: %d\n", - composition_type, info.type, info.app_id, info.z_order, info.cursor_x, info.cursor_y, - info.color.r, info.color.g, info.color.b, info.color.a, info.dataspace, info.transform, - info.display_frame.left, info.display_frame.top, info.display_frame.right, - info.display_frame.bottom, info.crop.left, info.crop.top, info.crop.right, - info.crop.bottom, info.blend_mode); - *result += StringPrintf("Layer buffer metadata: width: %d, height: %d, stride: %d, layerCount: %d\ - , pixelFormat: %d\n", buffer_metadata.width, buffer_metadata.height, buffer_metadata.stride, - buffer_metadata.layerCount, buffer_metadata.format); -} - -status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) { - ATRACE_NAME("vr_hwc onVsync"); - std::lock_guard<std::mutex> guard(mutex_); - if (callback_ != nullptr) - callback_->onVsync(kDefaultDisplayId, vsync_timestamp); - return OK; -} - -void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) { - std::lock_guard<std::mutex> guard(mutex_); - callback_ = callback; -} - -// composer::V2_2::ComposerHal -Error VrHwc::setReadbackBuffer(Display display, - const native_handle_t* bufferHandle, - android::base::unique_fd fenceFd) { - return Error::NONE; -} - -Error VrHwc::getReadbackBufferFence(Display display, - android::base::unique_fd* outFenceFd) { - return Error::NONE; -} - -Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height, - types::V1_1::PixelFormat* format, - Display* outDisplay) { - *format = types::V1_1::PixelFormat::RGBA_8888; - *outDisplay = display_count_; - displays_[display_count_].reset(new HwcDisplay(width, height)); - display_count_++; - return Error::NONE; -} - -Error VrHwc::setPowerMode_2_2(Display display, - IComposerClient::PowerMode mode) { - bool dozeSupported = false; - - Error dozeSupportError = getDozeSupport(display, &dozeSupported); - - if (dozeSupportError != Error::NONE) - return dozeSupportError; - - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (mode < IComposerClient::PowerMode::OFF || - mode > IComposerClient::PowerMode::DOZE_SUSPEND) { - return Error::BAD_PARAMETER; - } - - if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE || - mode == IComposerClient::PowerMode::DOZE_SUSPEND)) { - return Error::UNSUPPORTED; - } - - display_ptr->set_power_mode(mode); - return Error::NONE; -} - -Error VrHwc::setLayerFloatColor(Display display, Layer layer, - IComposerClient::FloatColor color) { - return Error::NONE; -} - -Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode, - std::vector<RenderIntent>* outIntents) { - return Error::NONE; -} - -std::array<float, 16> VrHwc::getDataspaceSaturationMatrix( - types::V1_1::Dataspace dataspace) { - return {}; -} - -// composer::V2_3::ComposerHal -Error VrHwc::getHdrCapabilities_2_3(Display /*display*/, - hidl_vec<Hdr>* /*outTypes*/, - float* outMaxLuminance, - float* outMaxAverageLuminance, - float* outMinLuminance) { - *outMaxLuminance = 0; - *outMaxAverageLuminance = 0; - *outMinLuminance = 0; - return Error::NONE; -} - -Error VrHwc::setLayerPerFrameMetadata_2_3( - Display display, Layer layer, - const std::vector<IComposerClient::PerFrameMetadata>& metadata) { - return Error::NONE; -} - -Error VrHwc::getPerFrameMetadataKeys_2_3( - Display display, - std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) { - return Error::NONE; -} - -Error VrHwc::setColorMode_2_3(Display display, ColorMode mode, - RenderIntent intent) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3) - return Error::BAD_PARAMETER; - - display_ptr->set_color_mode(mode); - return Error::NONE; -} - -Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode, - std::vector<RenderIntent>* outIntents) { - return Error::NONE; -} - -Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) { - return Error::NONE; -} - -Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width, - uint32_t height, PixelFormat format, - Dataspace dataspace) { - return Error::NONE; -} - -Error VrHwc::getReadbackBufferAttributes_2_3(Display display, - PixelFormat* outFormat, - Dataspace* outDataspace) { - return Error::NONE; -} - -Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort, - std::vector<uint8_t>* outData) { - int error = 0; - auto display_client = display::DisplayClient::Create(&error); - if (!display_client) { - ALOGE("Could not connect to display service : %s(%d)", strerror(error), - error); - return Error::BAD_CONFIG; - } - auto edid_data = display_client->GetConfigurationData( - display::ConfigFileType::kDeviceEdid); - auto display_identification_port = - display_client->GetDisplayIdentificationPort(); - *outPort = display_identification_port.get(); - - std::copy(edid_data.get().begin(), edid_data.get().end(), - std::back_inserter(*outData)); - return Error::NONE; -} - -Error VrHwc::setLayerColorTransform(Display display, Layer layer, - const float* matrix) { - return Error::NONE; -} - -Error VrHwc::getDisplayedContentSamplingAttributes( - Display display, PixelFormat& format, Dataspace& dataspace, - hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) { - return Error::NONE; -} - -Error VrHwc::setDisplayedContentSamplingEnabled( - Display display, IComposerClient::DisplayedContentSampling enable, - hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, - uint64_t maxFrames) { - return Error::NONE; -} - -Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames, - uint64_t timestamp, uint64_t& frameCount, - hidl_vec<uint64_t>& sampleComponent0, - hidl_vec<uint64_t>& sampleComponent1, - hidl_vec<uint64_t>& sampleComponent2, - hidl_vec<uint64_t>& sampleComponent3) { - return Error::NONE; -} - -Error VrHwc::getDisplayCapabilities( - Display display, - std::vector<IComposerClient::DisplayCapability>* outCapabilities) { - return Error::NONE; -} - -Error VrHwc::setLayerPerFrameMetadataBlobs( - Display display, Layer layer, - std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) { - return Error::NONE; -} - -Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) { - return Error::NONE; -} - -Error VrHwc::setDisplayBrightness(Display display, float brightness) { - return Error::NONE; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h deleted file mode 100644 index 3e3a6307fa..0000000000 --- a/services/vr/hardware_composer/impl/vr_hwc.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2016 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. - */ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H -#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H - -#include <android-base/unique_fd.h> -#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#include <android/hardware/graphics/composer/2.3/IComposer.h> -#include <composer-hal/2.3/ComposerHal.h> -#include <private/dvr/vsync_service.h> -#include <ui/Fence.h> -#include <ui/GraphicBuffer.h> -#include <utils/StrongPointer.h> - -#include <mutex> -#include <unordered_map> - -using namespace android::frameworks::vr::composer::V2_0; -using namespace android::hardware::graphics::common::V1_0; -using namespace android::hardware::graphics::composer::V2_3; - -using android::hardware::hidl_bitfield; -using android::hardware::hidl_handle; -using android::hardware::hidl_string; -using android::hardware::hidl_vec; -using android::hardware::Return; -using android::hardware::Void; -using android::hardware::graphics::composer::V2_1::Config; -using android::hardware::graphics::composer::V2_1::Display; -using android::hardware::graphics::composer::V2_1::Error; -using android::hardware::graphics::composer::V2_1::Layer; -using android::hardware::graphics::composer::V2_3::IComposerClient; - -namespace android { - -class Fence; - -namespace dvr { - -class VrComposerClient; - -using android::hardware::graphics::composer::V2_3::hal::ComposerHal; - -namespace types = android::hardware::graphics::common; - -using types::V1_1::RenderIntent; -using types::V1_2::ColorMode; -using types::V1_2::Dataspace; -using types::V1_2::Hdr; -using types::V1_2::PixelFormat; - -class ComposerView { - public: - struct ComposerLayer { - using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect; - using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect; - using BlendMode = - hardware::graphics::composer::V2_3::IComposerClient::BlendMode; - - Layer id; - sp<GraphicBuffer> buffer; - sp<Fence> fence; - Recti display_frame; - Rectf crop; - BlendMode blend_mode; - float alpha; - uint32_t type; - uint32_t app_id; - uint32_t z_order; - int32_t cursor_x; - int32_t cursor_y; - IComposerClient::Color color; - int32_t dataspace; - int32_t transform; - std::vector<hwc_rect_t> visible_regions; - std::vector<hwc_rect_t> damaged_regions; - }; - - struct Frame { - Display display_id; - // This is set to true to notify the upper layer that the display is - // being removed, or left false in the case of a normal frame. The upper - // layer tracks display IDs and will handle new ones showing up. - bool removed = false; - int32_t display_width; - int32_t display_height; - Config active_config; - ColorMode color_mode; - IComposerClient::PowerMode power_mode; - IComposerClient::Vsync vsync_enabled; - float color_transform[16]; - int32_t color_transform_hint; - std::vector<ComposerLayer> layers; - }; - - class Observer { - public: - virtual ~Observer() {} - - // Returns a list of layers that need to be shown together. Layers are - // returned in z-order, with the lowest layer first. - virtual base::unique_fd OnNewFrame(const Frame& frame) = 0; - }; - - virtual ~ComposerView() {} - - virtual void ForceDisplaysRefresh() = 0; - virtual void RegisterObserver(Observer* observer) = 0; - virtual void UnregisterObserver(Observer* observer) = 0; -}; - -struct HwcLayer { - using Composition = - hardware::graphics::composer::V2_3::IComposerClient::Composition; - - explicit HwcLayer(Layer new_id) { info.id = new_id; } - - void dumpDebugInfo(std::string* result) const; - - Composition composition_type; - ComposerView::ComposerLayer info; - IVrComposerClient::BufferMetadata buffer_metadata; -}; - -class HwcDisplay { - public: - HwcDisplay(int32_t width, int32_t height); - ~HwcDisplay(); - - int32_t width() const { return width_; } - int32_t height() const { return height_; } - - HwcLayer* CreateLayer(); - bool DestroyLayer(Layer id); - HwcLayer* GetLayer(Layer id); - - bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence); - void SetClientTargetMetadata( - const IVrComposerClient::BufferMetadata& metadata); - - void GetChangedCompositionTypes( - std::vector<Layer>* layer_ids, - std::vector<IComposerClient::Composition>* composition); - - Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame); - - std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers(); - - Config active_config() const { return active_config_; } - void set_active_config(Config config) { active_config_ = config; } - - ColorMode color_mode() const { return color_mode_; } - void set_color_mode(ColorMode mode) { color_mode_ = mode; } - - IComposerClient::PowerMode power_mode() const { return power_mode_; } - void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; } - - bool vsync_enabled() const { return vsync_enabled_; } - void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;} - - const float* color_transform() const { return color_transform_; } - int32_t color_transform_hint() const { return color_transform_hint_; } - void SetColorTransform(const float* matrix, int32_t hint); - - void dumpDebugInfo(std::string* result) const; - - private: - // The client target buffer and the associated fence. - sp<GraphicBuffer> buffer_; - IVrComposerClient::BufferMetadata buffer_metadata_; - sp<Fence> fence_; - - // List of currently active layers. - std::vector<HwcLayer> layers_; - - std::vector<Layer> last_frame_layers_ids_; - - // Layer ID generator. - uint64_t layer_ids_ = 1; - - int32_t width_; - int32_t height_; - - Config active_config_; - ColorMode color_mode_; - IComposerClient::PowerMode power_mode_; - bool vsync_enabled_ = false; - float color_transform_[16]; - int32_t color_transform_hint_; - - HwcDisplay(const HwcDisplay&) = delete; - void operator=(const HwcDisplay&) = delete; -}; - -class VrHwc : public IComposer, public ComposerHal, public ComposerView { - public: - VrHwc(); - ~VrHwc() override; - - Error setLayerInfo(Display display, Layer layer, uint32_t type, - uint32_t appId); - Error setClientTargetMetadata( - Display display, const IVrComposerClient::BufferMetadata& metadata); - Error setLayerBufferMetadata( - Display display, Layer layer, - const IVrComposerClient::BufferMetadata& metadata); - - // composer::V2_1::ComposerHal - bool hasCapability(hwc2_capability_t capability) override; - - std::string dumpDebugInfo() override { return {}; } - - void registerEventCallback(ComposerHal::EventCallback* callback) override; - void unregisterEventCallback() override; - - uint32_t getMaxVirtualDisplayCount() override; - Error destroyVirtualDisplay(Display display) override; - - Error createLayer(Display display, Layer* outLayer) override; - Error destroyLayer(Display display, Layer layer) override; - - Error getActiveConfig(Display display, Config* outConfig) override; - Error getDisplayAttribute(Display display, Config config, - IComposerClient::Attribute attribute, - int32_t* outValue) override; - Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; - Error getDisplayName(Display display, hidl_string* outName) override; - Error getDisplayType(Display display, - IComposerClient::DisplayType* outType) override; - Error getDozeSupport(Display display, bool* outSupport) override; - - Error setActiveConfig(Display display, Config config) override; - Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; - - Error setColorTransform(Display display, const float* matrix, - int32_t hint) override; - Error setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t dataspace, - const std::vector<hwc_rect_t>& damage) override; - Error setOutputBuffer(Display display, buffer_handle_t buffer, - int32_t releaseFence) override; - Error validateDisplay( - Display display, std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks) override; - Error acceptDisplayChanges(Display display) override; - Error presentDisplay(Display display, int32_t* outPresentFence, - std::vector<Layer>* outLayers, - std::vector<int32_t>* outReleaseFences) override; - - Error setLayerCursorPosition(Display display, Layer layer, int32_t x, - int32_t y) override; - Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, - int32_t acquireFence) override; - Error setLayerSurfaceDamage(Display display, Layer layer, - const std::vector<hwc_rect_t>& damage) override; - Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; - Error setLayerColor(Display display, Layer layer, - IComposerClient::Color color) override; - Error setLayerCompositionType(Display display, Layer layer, - int32_t type) override; - Error setLayerDataspace(Display display, Layer layer, - int32_t dataspace) override; - Error setLayerDisplayFrame(Display display, Layer layer, - const hwc_rect_t& frame) override; - Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; - Error setLayerSidebandStream(Display display, Layer layer, - buffer_handle_t stream) override; - Error setLayerSourceCrop(Display display, Layer layer, - const hwc_frect_t& crop) override; - Error setLayerTransform(Display display, Layer layer, - int32_t transform) override; - Error setLayerVisibleRegion(Display display, Layer layer, - const std::vector<hwc_rect_t>& visible) override; - Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; - - // composer::V2_2::ComposerHal - Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle, - android::base::unique_fd fenceFd) override; - Error getReadbackBufferFence(Display display, - android::base::unique_fd* outFenceFd) override; - Error createVirtualDisplay_2_2(uint32_t width, uint32_t height, - types::V1_1::PixelFormat* format, - Display* outDisplay) override; - Error setPowerMode_2_2(Display display, - IComposerClient::PowerMode mode) override; - Error setLayerFloatColor(Display display, Layer layer, - IComposerClient::FloatColor color) override; - Error getRenderIntents(Display display, types::V1_1::ColorMode mode, - std::vector<RenderIntent>* outIntents) override; - std::array<float, 16> getDataspaceSaturationMatrix( - types::V1_1::Dataspace dataspace) override; - - // composer::V2_3::ComposerHal - Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes, - float* outMaxLuminance, - float* outMaxAverageLuminance, - float* outMinLuminance) override; - Error setLayerPerFrameMetadata_2_3( - Display display, Layer layer, - const std::vector<IComposerClient::PerFrameMetadata>& metadata) override; - Error getPerFrameMetadataKeys_2_3( - Display display, - std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override; - Error setColorMode_2_3(Display display, ColorMode mode, - RenderIntent intent) override; - Error getRenderIntents_2_3(Display display, ColorMode mode, - std::vector<RenderIntent>* outIntents) override; - Error getColorModes_2_3(Display display, - hidl_vec<ColorMode>* outModes) override; - Error getClientTargetSupport_2_3(Display display, uint32_t width, - uint32_t height, PixelFormat format, - Dataspace dataspace) override; - Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat, - Dataspace* outDataspace) override; - Error getDisplayIdentificationData(Display display, uint8_t* outPort, - std::vector<uint8_t>* outData) override; - Error setLayerColorTransform(Display display, Layer layer, - const float* matrix) override; - Error getDisplayedContentSamplingAttributes( - Display display, PixelFormat& format, Dataspace& dataspace, - hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) - override; - Error setDisplayedContentSamplingEnabled( - Display display, IComposerClient::DisplayedContentSampling enable, - hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, - uint64_t maxFrames) override; - Error getDisplayedContentSample( - Display display, uint64_t maxFrames, uint64_t timestamp, - uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0, - hidl_vec<uint64_t>& sampleComponent1, - hidl_vec<uint64_t>& sampleComponent2, - hidl_vec<uint64_t>& sampleComponent3) override; - Error getDisplayCapabilities(Display display, - std::vector<IComposerClient::DisplayCapability>* - outCapabilities) override; - Error setLayerPerFrameMetadataBlobs( - Display display, Layer layer, - std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override; - Error getDisplayBrightnessSupport(Display display, bool* outSupport) override; - Error setDisplayBrightness(Display display, float brightness) override; - - // IComposer: - Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; - Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; - Return<void> createClient(createClient_cb hidl_cb) override; - Return<void> createClient_2_3( - IComposer::createClient_2_3_cb hidl_cb) override; - - // ComposerView: - void ForceDisplaysRefresh() override; - void RegisterObserver(Observer* observer) override; - void UnregisterObserver(Observer* observer) override; - - Return<void> debug(const hidl_handle& fd, - const hidl_vec<hidl_string>& args) override; - - private: - class VsyncCallback : public BnVsyncCallback { - public: - status_t onVsync(int64_t vsync_timestamp) override; - void SetEventCallback(EventCallback* callback); - private: - std::mutex mutex_; - EventCallback* callback_; - }; - - HwcDisplay* FindDisplay(Display display); - - // Re-evaluate whether or not we should start making onVsync() callbacks to - // the client. We need enableCallback(true) to have been called, and - // setVsyncEnabled() to have been called for the primary display. The caller - // must have mutex_ locked already. - void UpdateVsyncCallbackEnabledLocked(); - - wp<VrComposerClient> client_; - - // Guard access to internal state from binder threads. - std::mutex mutex_; - - std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_; - Display display_count_ = 2; - - EventCallback* event_callback_ = nullptr; - Observer* observer_ = nullptr; - - sp<VsyncCallback> vsync_callback_; - - VrHwc(const VrHwc&) = delete; - void operator=(const VrHwc&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp deleted file mode 100644 index 2e70928662..0000000000 --- a/services/vr/hardware_composer/tests/vr_composer_test.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include <android/dvr/BnVrComposerCallback.h> -#include <binder/IServiceManager.h> -#include <gtest/gtest.h> -#include <sys/eventfd.h> -#include <vr_composer.h> - -namespace android { -namespace dvr { -namespace { - -const char kVrDisplayName[] = "VrDisplay_Test"; - -class TestComposerView : public ComposerView { - public: - TestComposerView() {} - ~TestComposerView() override = default; - - size_t display_refresh_count() const { return display_refresh_count_; } - - void ForceDisplaysRefresh() override { display_refresh_count_++; } - void RegisterObserver(Observer* observer) override {} - void UnregisterObserver(Observer* observer) override {} - - TestComposerView(const TestComposerView&) = delete; - void operator=(const TestComposerView&) = delete; - - private: - size_t display_refresh_count_ = 0; -}; - -class TestComposerCallback : public BnVrComposerCallback { - public: - TestComposerCallback() {} - ~TestComposerCallback() override = default; - - ComposerView::Frame last_frame() const { return last_frame_; } - - binder::Status onNewFrame( - const ParcelableComposerFrame& frame, - ParcelableUniqueFd* /* fence */) override { - last_frame_ = frame.frame(); - return binder::Status::ok(); - } - - private: - ComposerView::Frame last_frame_; - - TestComposerCallback(const TestComposerCallback&) = delete; - void operator=(const TestComposerCallback&) = delete; -}; - -class TestComposerCallbackWithFence : public TestComposerCallback { - public: - ~TestComposerCallbackWithFence() override = default; - - binder::Status onNewFrame( - const ParcelableComposerFrame& frame, - ParcelableUniqueFd* fence) override { - binder::Status status = TestComposerCallback::onNewFrame(frame, fence); - - base::unique_fd fd(eventfd(0, 0)); - EXPECT_LE(0, fd.get()); - fence->set_fence(fd); - - return status; - } -}; - -sp<GraphicBuffer> CreateBuffer() { - return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_HW_TEXTURE); -} - -} // namespace - -class VrComposerTest : public testing::Test { - public: - VrComposerTest() : composer_(new VrComposer(&composer_view_)) {} - ~VrComposerTest() override = default; - - sp<IVrComposer> GetComposerProxy() const { - sp<IServiceManager> sm(defaultServiceManager()); - return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName))); - } - - void SetUp() override { - sp<IServiceManager> sm(defaultServiceManager()); - EXPECT_EQ(OK, - sm->addService(String16(kVrDisplayName), composer_, false)); - } - - protected: - TestComposerView composer_view_; - sp<VrComposer> composer_; - - VrComposerTest(const VrComposerTest&) = delete; - void operator=(const VrComposerTest&) = delete; -}; - -TEST_F(VrComposerTest, TestWithoutObserver) { - sp<IVrComposer> composer = GetComposerProxy(); - ComposerView::Frame frame; - - base::unique_fd fence = composer_->OnNewFrame(frame); - ASSERT_EQ(-1, fence.get()); -} - -TEST_F(VrComposerTest, TestWithObserver) { - sp<IVrComposer> composer = GetComposerProxy(); - sp<TestComposerCallback> callback = new TestComposerCallback(); - ASSERT_EQ(0, composer_view_.display_refresh_count()); - ASSERT_TRUE(composer->registerObserver(callback).isOk()); - ASSERT_EQ(1, composer_view_.display_refresh_count()); - - ComposerView::Frame frame; - base::unique_fd fence = composer_->OnNewFrame(frame); - ASSERT_EQ(-1, fence.get()); -} - -TEST_F(VrComposerTest, TestWithOneLayer) { - sp<IVrComposer> composer = GetComposerProxy(); - sp<TestComposerCallback> callback = new TestComposerCallbackWithFence(); - ASSERT_TRUE(composer->registerObserver(callback).isOk()); - - ComposerView::Frame frame; - frame.display_id = 1; - frame.removed = false; - frame.display_width = 600; - frame.display_height = 400; - frame.layers.push_back(ComposerView::ComposerLayer{ - .id = 1, - .buffer = CreateBuffer(), - .fence = new Fence(eventfd(0, 0)), - .display_frame = {0, 0, 600, 400}, - .crop = {0.0f, 0.0f, 600.0f, 400.0f}, - .blend_mode = IComposerClient::BlendMode::NONE, - .alpha = 1.0f, - .type = 1, - .app_id = 1, - }); - base::unique_fd fence = composer_->OnNewFrame(frame); - ASSERT_LE(0, fence.get()); - - ComposerView::Frame received_frame = callback->last_frame(); - ASSERT_EQ(frame.display_id, received_frame.display_id); - ASSERT_EQ(frame.display_width, received_frame.display_width); - ASSERT_EQ(frame.display_height, received_frame.display_height); - ASSERT_EQ(frame.removed, received_frame.removed); - ASSERT_EQ(1u, received_frame.layers.size()); - ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id); - ASSERT_NE(nullptr, received_frame.layers[0].buffer.get()); - ASSERT_TRUE(received_frame.layers[0].fence->isValid()); - ASSERT_EQ(frame.layers[0].display_frame.left, - received_frame.layers[0].display_frame.left); - ASSERT_EQ(frame.layers[0].display_frame.top, - received_frame.layers[0].display_frame.top); - ASSERT_EQ(frame.layers[0].display_frame.right, - received_frame.layers[0].display_frame.right); - ASSERT_EQ(frame.layers[0].display_frame.bottom, - received_frame.layers[0].display_frame.bottom); - ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left); - ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top); - ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right); - ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom); - ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode); - ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha); - ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type); - ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id); -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp deleted file mode 100644 index d93f370945..0000000000 --- a/services/vr/hardware_composer/vr_composer.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "vr_composer.h" - -#include <binder/IPCThreadState.h> -#include <binder/PermissionCache.h> - -namespace android { -namespace dvr { -namespace { - -bool CheckPermission() { - const android::IPCThreadState* ipc = android::IPCThreadState::self(); - const pid_t pid = ipc->getCallingPid(); - const uid_t uid = ipc->getCallingUid(); - const bool permission = PermissionCache::checkPermission( - String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid); - if (!permission) - ALOGE("permission denied to pid=%d uid=%u", pid, uid); - - return permission; -} - -} // namespace - -VrComposer::VrComposer(ComposerView* composer_view) - : composer_view_(composer_view) { - composer_view_->RegisterObserver(this); -} - -VrComposer::~VrComposer() { - composer_view_->UnregisterObserver(this); -} - -binder::Status VrComposer::registerObserver( - const sp<IVrComposerCallback>& callback) { - { - std::lock_guard<std::mutex> guard(mutex_); - - if (!CheckPermission()) - return binder::Status::fromStatusT(PERMISSION_DENIED); - - if (callback_.get()) { - ALOGE("Failed to register callback, already registered"); - return binder::Status::fromStatusT(ALREADY_EXISTS); - } - - callback_ = callback; - IInterface::asBinder(callback_)->linkToDeath(this); - } - - // Don't take the lock to force display refresh otherwise it could end in a - // deadlock since HWC calls this with new frames and it has a lock of its own - // to serialize access to the display information. - composer_view_->ForceDisplaysRefresh(); - return binder::Status::ok(); -} - -binder::Status VrComposer::clearObserver() { - std::lock_guard<std::mutex> guard(mutex_); - callback_ = nullptr; - return binder::Status::ok(); -} - -base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) { - std::lock_guard<std::mutex> guard(mutex_); - - if (!callback_.get()) - return base::unique_fd(); - - ParcelableComposerFrame parcelable_frame(frame); - ParcelableUniqueFd fence; - binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence); - if (!ret.isOk()) - ALOGE("Failed to send new frame: %s", ret.toString8().string()); - - return fence.fence(); -} - -void VrComposer::binderDied(const wp<IBinder>& /* who */) { - std::lock_guard<std::mutex> guard(mutex_); - - callback_ = nullptr; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h deleted file mode 100644 index 1273352ad0..0000000000 --- a/services/vr/hardware_composer/vr_composer.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H -#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H - -#include <android/dvr/BnVrComposer.h> -#include <impl/vr_hwc.h> - -namespace android { -namespace dvr { - -class VrComposerCallback; - -// Implementation of the IVrComposer service used to notify VR Window Manager -// when SurfaceFlinger presents 2D UI changes. -// -// VR HWC updates the presented frame via the ComposerView::Observer interface. -// On notification |callback_| is called to update VR Window Manager. -// NOTE: If VR Window Manager isn't connected, the notification is a no-op. -class VrComposer - : public BnVrComposer, - public ComposerView::Observer, - public IBinder::DeathRecipient { - public: - explicit VrComposer(ComposerView* composer_view); - ~VrComposer() override; - - // BnVrComposer: - binder::Status registerObserver( - const sp<IVrComposerCallback>& callback) override; - - binder::Status clearObserver() override; - - // ComposerView::Observer: - base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override; - - private: - // IBinder::DeathRecipient: - void binderDied(const wp<IBinder>& who) override; - - std::mutex mutex_; - - sp<IVrComposerCallback> callback_; - - ComposerView* composer_view_; // Not owned. - - VrComposer(const VrComposer&) = delete; - void operator=(const VrComposer&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index ba98696aef..40cf9fbd67 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -49,7 +49,13 @@ extern "C" { * in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the * pNext chain of VkBindImageMemoryInfo and passed down to the driver. */ -#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 +/* + * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9 + * + * This version of the extension is largely designed to clean up the mix of + * GrallocUsage and GrallocUsage2 + */ +#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" #define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \ @@ -61,6 +67,8 @@ extern "C" { VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1) #define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2) +#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \ + VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3) /* clang-format off */ typedef enum VkSwapchainImageUsageFlagBitsANDROID { @@ -90,6 +98,7 @@ typedef struct { * format: gralloc format requested when the buffer was allocated * usage: gralloc usage requested when the buffer was allocated * usage2: gralloc usage requested when the buffer was allocated + * usage3: gralloc usage requested when the buffer was allocated */ typedef struct { VkStructureType sType; @@ -98,7 +107,8 @@ typedef struct { int stride; int format; int usage; /* DEPRECATED in SPEC_VERSION 6 */ - VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */ + VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */ + uint64_t usage3; /* ADDED in SPEC_VERSION 9 */ } VkNativeBufferANDROID; /* @@ -127,6 +137,21 @@ typedef struct { VkBool32 sharedImage; } VkPhysicalDevicePresentationPropertiesANDROID; +/* + * struct VkGrallocUsageInfoANDROID + * + * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID + * pNext: NULL or a pointer to a structure extending this structure + * format: value specifying the format the image will be created with + * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage + */ +typedef struct { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageUsageFlags imageUsage; +} VkGrallocUsageInfoANDROID; + /* DEPRECATED in SPEC_VERSION 6 */ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)( VkDevice device, @@ -134,7 +159,7 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)( VkImageUsageFlags imageUsage, int* grallocUsage); -/* ADDED in SPEC_VERSION 6 */ +/* DEPRECATED in SPEC_VERSION 9 */ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)( VkDevice device, VkFormat format, @@ -143,6 +168,12 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)( uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); +/* ADDED in SPEC_VERSION 9 */ +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)( + VkDevice device, + const VkGrallocUsageInfoANDROID* grallocUsageInfo, + uint64_t* grallocUsage); + typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)( VkDevice device, VkImage image, @@ -167,7 +198,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID( int* grallocUsage ); -/* ADDED in SPEC_VERSION 6 */ +/* DEPRECATED in SPEC_VERSION 9 */ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID( VkDevice device, VkFormat format, @@ -177,6 +208,13 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID( uint64_t* grallocProducerUsage ); +/* ADDED in SPEC_VERSION 9 */ +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID( + VkDevice device, + const VkGrallocUsageInfoANDROID* grallocUsageInfo, + uint64_t* grallocUsage +); + VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID( VkDevice device, VkImage image, diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 766451824a..4927150b6a 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -1027,6 +1027,39 @@ void QueryPresentationProperties( } } +bool GetAndroidNativeBufferSpecVersion9Support( + VkPhysicalDevice physicalDevice) { + const InstanceData& data = GetData(physicalDevice); + + // Call to get propertyCount + uint32_t propertyCount = 0; + ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties"); + VkResult result = data.driver.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &propertyCount, nullptr); + ATRACE_END(); + + // Call to enumerate properties + std::vector<VkExtensionProperties> properties(propertyCount); + ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties"); + result = data.driver.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &propertyCount, properties.data()); + ATRACE_END(); + + for (uint32_t i = 0; i < propertyCount; i++) { + auto& prop = properties[i]; + + if (strcmp(prop.extensionName, + VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0) + continue; + + if (prop.specVersion >= 9) { + return true; + } + } + + return false; +} + VkResult EnumerateDeviceExtensionProperties( VkPhysicalDevice physicalDevice, const char* pLayerName, @@ -1061,6 +1094,37 @@ VkResult EnumerateDeviceExtensionProperties( VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION}); } + // Conditionally add VK_EXT_IMAGE_COMPRESSION_CONTROL* if feature and ANB + // support is provided by the driver + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT + swapchainCompFeats = {}; + swapchainCompFeats.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT; + swapchainCompFeats.pNext = nullptr; + VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {}; + imageCompFeats.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT; + imageCompFeats.pNext = &swapchainCompFeats; + + VkPhysicalDeviceFeatures2 feats2 = {}; + feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + feats2.pNext = &imageCompFeats; + + GetPhysicalDeviceFeatures2(physicalDevice, &feats2); + + bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice); + + if (anb9 && imageCompFeats.imageCompressionControl) { + loader_extensions.push_back( + {VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME, + VK_EXT_IMAGE_COMPRESSION_CONTROL_SPEC_VERSION}); + } + if (anb9 && swapchainCompFeats.imageCompressionControlSwapchain) { + loader_extensions.push_back( + {VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME, + VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION}); + } + // enumerate our extensions first if (!pLayerName && pProperties) { uint32_t count = std::min( @@ -1254,15 +1318,18 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, return VK_ERROR_INCOMPATIBLE_DRIVER; } - // sanity check ANDROID_native_buffer implementation, whose set of + // Confirming ANDROID_native_buffer implementation, whose set of // entrypoints varies according to the spec version. if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) && !data->driver.GetSwapchainGrallocUsageANDROID && - !data->driver.GetSwapchainGrallocUsage2ANDROID) { - ALOGE("Driver's implementation of ANDROID_native_buffer is broken;" - " must expose at least one of " - "vkGetSwapchainGrallocUsageANDROID or " - "vkGetSwapchainGrallocUsage2ANDROID"); + !data->driver.GetSwapchainGrallocUsage2ANDROID && + !data->driver.GetSwapchainGrallocUsage3ANDROID) { + ALOGE( + "Driver's implementation of ANDROID_native_buffer is broken;" + " must expose at least one of " + "vkGetSwapchainGrallocUsageANDROID or " + "vkGetSwapchainGrallocUsage2ANDROID or " + "vkGetSwapchainGrallocUsage3ANDROID"); data->driver.DestroyDevice(dev, pAllocator); FreeDeviceData(data, data_allocator); @@ -1441,10 +1508,83 @@ void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, if (driver.GetPhysicalDeviceFeatures2) { driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + } else { + driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); + } + + // Conditionally add imageCompressionControlSwapchain if + // imageCompressionControl is supported Check for imageCompressionControl in + // the pChain + bool imageCompressionControl = false; + bool imageCompressionControlInChain = false; + bool imageCompressionControlSwapchainInChain = false; + VkPhysicalDeviceFeatures2* pFeats = pFeatures; + while (pFeats) { + switch (pFeats->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT: { + const VkPhysicalDeviceImageCompressionControlFeaturesEXT* + compressionFeat = reinterpret_cast< + const VkPhysicalDeviceImageCompressionControlFeaturesEXT*>( + pFeats); + imageCompressionControl = + compressionFeat->imageCompressionControl; + imageCompressionControlInChain = true; + } break; + + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: { + imageCompressionControlSwapchainInChain = true; + } break; + + default: + break; + } + pFeats = reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext); + } + + if (!imageCompressionControlSwapchainInChain) { return; } - driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); + // If not in pchain, explicitly query for imageCompressionControl + if (!imageCompressionControlInChain) { + VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {}; + imageCompFeats.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT; + imageCompFeats.pNext = nullptr; + + VkPhysicalDeviceFeatures2 feats2 = {}; + feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + feats2.pNext = &imageCompFeats; + + if (driver.GetPhysicalDeviceFeatures2) { + driver.GetPhysicalDeviceFeatures2(physicalDevice, &feats2); + } else { + driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, &feats2); + } + + imageCompressionControl = imageCompFeats.imageCompressionControl; + } + + // Only enumerate imageCompressionControlSwapchin if imageCompressionControl + if (imageCompressionControl) { + pFeats = pFeatures; + while (pFeats) { + switch (pFeats->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: { + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT* + compressionFeat = reinterpret_cast< + VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>( + pFeats); + compressionFeat->imageCompressionControlSwapchain = true; + } break; + + default: + break; + } + pFeats = + reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext); + } + } } void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 14c516b16b..4d2bbd69e3 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -107,6 +107,8 @@ void QueryPresentationProperties( VkPhysicalDevice physicalDevice, VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties); +bool GetAndroidNativeBufferSpecVersion9Support(VkPhysicalDevice physicalDevice); + VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName); VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index b436db1de7..de98aa7e61 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -496,6 +496,13 @@ const ProcHook g_proc_hooks[] = { nullptr, }, { + "vkGetSwapchainGrallocUsage3ANDROID", + ProcHook::DEVICE, + ProcHook::ANDROID_native_buffer, + nullptr, + nullptr, + }, + { "vkGetSwapchainGrallocUsageANDROID", ProcHook::DEVICE, ProcHook::ANDROID_native_buffer, @@ -664,6 +671,7 @@ bool InitDriverTable(VkDevice dev, INIT_PROC(false, dev, GetDeviceQueue2); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID); + INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID); // clang-format on diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 079f9cca39..2f60086a27 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -123,6 +123,7 @@ struct DeviceDriverTable { PFN_vkGetDeviceQueue2 GetDeviceQueue2; PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID; PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID; + PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID; PFN_vkAcquireImageANDROID AcquireImageANDROID; PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID; // clang-format on diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt index f49e8f3e8d..b189c6888d 100644 --- a/vulkan/libvulkan/libvulkan.map.txt +++ b/vulkan/libvulkan/libvulkan.map.txt @@ -178,6 +178,7 @@ LIBVULKAN { vkGetImageSparseMemoryRequirements; vkGetImageSparseMemoryRequirements2; # introduced=28 vkGetImageSubresourceLayout; + vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake vkGetInstanceProcAddr; vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28 vkGetPhysicalDeviceExternalBufferProperties; # introduced=28 diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 87b3a89cce..475bc40d14 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -715,6 +715,17 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( capabilities->minImageExtent = VkExtent2D{1, 1}; capabilities->maxImageExtent = VkExtent2D{4096, 4096}; + if (capabilities->maxImageExtent.height < + capabilities->currentExtent.height) { + capabilities->maxImageExtent.height = + capabilities->currentExtent.height; + } + + if (capabilities->maxImageExtent.width < + capabilities->currentExtent.width) { + capabilities->maxImageExtent.width = capabilities->currentExtent.width; + } + capabilities->maxImageArrayLayers = 1; capabilities->supportedTransforms = kSupportedTransforms; @@ -743,7 +754,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); - bool wide_color_support = false; uint64_t consumer_usage = 0; bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); @@ -754,27 +764,15 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (!surfaceless_enabled) { return VK_ERROR_SURFACE_LOST_KHR; } - // Support for VK_GOOGLE_surfaceless_query. The EGL loader - // unconditionally supports wide color formats, even if they will cause - // a SurfaceFlinger fallback. Based on that, wide_color_support will be - // set to true in this case. - wide_color_support = true; + // Support for VK_GOOGLE_surfaceless_query. // TODO(b/203826952): research proper value; temporarily use the // values seen on Pixel consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; } else { Surface& surface = *SurfaceFromHandle(surface_handle); - int err = native_window_get_wide_color_support(surface.window.get(), - &wide_color_support); - if (err) { - return VK_ERROR_SURFACE_LOST_KHR; - } - ALOGV("wide_color_support is: %d", wide_color_support); - consumer_usage = surface.consumer_usage; } - wide_color_support = wide_color_support && colorspace_ext; AHardwareBuffer_Desc desc = {}; desc.width = 1; @@ -796,9 +794,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}); all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); - } - - if (wide_color_support) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -828,8 +823,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - } - if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}); @@ -848,8 +841,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - } - if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); @@ -948,11 +939,60 @@ VkResult GetPhysicalDeviceSurfaceFormats2KHR( surface_formats.data()); if (result == VK_SUCCESS || result == VK_INCOMPLETE) { + const auto& driver = GetData(physicalDevice).driver; + // marshal results individually due to stride difference. - // completely ignore any chained extension structs. uint32_t formats_to_marshal = *pSurfaceFormatCount; for (uint32_t i = 0u; i < formats_to_marshal; i++) { pSurfaceFormats[i].surfaceFormat = surface_formats[i]; + + // Query the compression properties for the surface format + if (pSurfaceFormats[i].pNext) { + VkImageCompressionPropertiesEXT* surfaceCompressionProps = + reinterpret_cast<VkImageCompressionPropertiesEXT*>( + pSurfaceFormats[i].pNext); + + if (surfaceCompressionProps && + driver.GetPhysicalDeviceImageFormatProperties2KHR) { + VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {}; + imageFormatInfo.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; + imageFormatInfo.format = + pSurfaceFormats[i].surfaceFormat.format; + imageFormatInfo.pNext = nullptr; + + VkImageCompressionControlEXT compressionControl = {}; + compressionControl.sType = + VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT; + compressionControl.pNext = imageFormatInfo.pNext; + + imageFormatInfo.pNext = &compressionControl; + + VkImageCompressionPropertiesEXT compressionProps = {}; + compressionProps.sType = + VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT; + compressionProps.pNext = nullptr; + + VkImageFormatProperties2KHR imageFormatProps = {}; + imageFormatProps.sType = + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR; + imageFormatProps.pNext = &compressionProps; + + VkResult compressionRes = + driver.GetPhysicalDeviceImageFormatProperties2KHR( + physicalDevice, &imageFormatInfo, + &imageFormatProps); + if (compressionRes == VK_SUCCESS) { + surfaceCompressionProps->imageCompressionFlags = + compressionProps.imageCompressionFlags; + surfaceCompressionProps + ->imageCompressionFixedRateFlags = + compressionProps.imageCompressionFixedRateFlags; + } else { + return compressionRes; + } + } + } } } @@ -1370,8 +1410,48 @@ VkResult CreateSwapchainKHR(VkDevice device, num_images = 1; } + void* usage_info_pNext = nullptr; + VkImageCompressionControlEXT image_compression = {}; uint64_t native_usage = 0; - if (dispatch.GetSwapchainGrallocUsage2ANDROID) { + if (dispatch.GetSwapchainGrallocUsage3ANDROID) { + ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID"); + VkGrallocUsageInfoANDROID gralloc_usage_info = {}; + gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID; + gralloc_usage_info.format = create_info->imageFormat; + gralloc_usage_info.imageUsage = create_info->imageUsage; + + // Look through the pNext chain for an image compression control struct + // if one is found AND the appropriate extensions are enabled, + // append it to be the gralloc usage pNext chain + const VkSwapchainCreateInfoKHR* create_infos = create_info; + while (create_infos->pNext) { + create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( + create_infos->pNext); + switch (create_infos->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { + const VkImageCompressionControlEXT* compression_infos = + reinterpret_cast<const VkImageCompressionControlEXT*>( + create_infos); + image_compression = *compression_infos; + image_compression.pNext = nullptr; + usage_info_pNext = &image_compression; + } break; + + default: + // Ignore all other info structs + break; + } + } + gralloc_usage_info.pNext = usage_info_pNext; + + result = dispatch.GetSwapchainGrallocUsage3ANDROID( + device, &gralloc_usage_info, &native_usage); + ATRACE_END(); + if (result != VK_SUCCESS) { + ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result); + return VK_ERROR_SURFACE_LOST_KHR; + } + } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) { uint64_t consumer_usage, producer_usage; ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID"); result = dispatch.GetSwapchainGrallocUsage2ANDROID( @@ -1383,7 +1463,7 @@ VkResult CreateSwapchainKHR(VkDevice device, return VK_ERROR_SURFACE_LOST_KHR; } native_usage = - convertGralloc1ToBufferUsage(consumer_usage, producer_usage); + convertGralloc1ToBufferUsage(producer_usage, consumer_usage); } else if (dispatch.GetSwapchainGrallocUsageANDROID) { ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID"); int32_t legacy_usage = 0; @@ -1437,7 +1517,7 @@ VkResult CreateSwapchainKHR(VkDevice device, #pragma clang diagnostic ignored "-Wold-style-cast" .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID, #pragma clang diagnostic pop - .pNext = nullptr, + .pNext = usage_info_pNext, .usage = swapchain_image_usage, }; VkNativeBufferANDROID image_native_buffer = { @@ -1495,6 +1575,7 @@ VkResult CreateSwapchainKHR(VkDevice device, android_convertGralloc0To1Usage(int(img.buffer->usage), &image_native_buffer.usage2.producer, &image_native_buffer.usage2.consumer); + image_native_buffer.usage3 = img.buffer->usage; ATRACE_BEGIN("CreateImage"); result = diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index 3c91150d45..f998b1ad18 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -948,6 +948,17 @@ VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice, return VK_SUCCESS; } +VkResult GetSwapchainGrallocUsage3ANDROID( + VkDevice, + const VkGrallocUsageInfoANDROID* grallocUsageInfo, + uint64_t* grallocUsage) { + // The null driver never reads or writes the gralloc buffer + ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__, + grallocUsageInfo->format); + *grallocUsage = 0; + return VK_SUCCESS; +} + VkResult AcquireImageANDROID(VkDevice, VkImage, int fence, diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index f6dcf0900c..0cb7bd3185 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -261,6 +261,7 @@ const NameProc kInstanceProcs[] = { {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))}, {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))}, {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))}, + {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))}, {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))}, {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))}, {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))}, diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index 3e003e3189..5c7fea0fa8 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -209,6 +209,7 @@ VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueu VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage); VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); +VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage); VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py index 4176509447..c25c6cbda0 100644 --- a/vulkan/scripts/generator_common.py +++ b/vulkan/scripts/generator_common.py @@ -69,6 +69,7 @@ _EXPORTED_EXTENSIONS = [ _OPTIONAL_COMMANDS = [ 'vkGetSwapchainGrallocUsageANDROID', 'vkGetSwapchainGrallocUsage2ANDROID', + 'vkGetSwapchainGrallocUsage3ANDROID', ] # Dict for mapping dispatch table to a type. |