diff options
76 files changed, 1401 insertions, 1517 deletions
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index ff73c9499f..e54f9d3df7 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -353,8 +353,16 @@ bool ListCommand::addEntryWithInstance(const TableEntry& entry, return false; } + auto vintfFqInstance = vintf::FqInstance::from(fqInstance.string()); + if (!vintfFqInstance.has_value()) { + err() << "Unable to convert " << fqInstance.string() << " to vintf::FqInstance" + << std::endl; + return false; + } + std::string e; - if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) { + if (!manifest->insertInstance(*vintfFqInstance, entry.transport, arch, vintf::HalFormat::HIDL, + &e)) { err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl; return false; } diff --git a/include/android/configuration.h b/include/android/configuration.h index 88019ae054..46c7dfeceb 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -471,10 +471,36 @@ enum { */ ACONFIGURATION_COLOR_MODE = 0x10000, /** + * Bit mask for + * <a href="/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier">grammatical gender</a> + * configuration. + */ + ACONFIGURATION_GRAMMATICAL_GENDER = 0x20000, + /** * Constant used to to represent MNC (Mobile Network Code) zero. * 0 cannot be used, since it is used to represent an undefined MNC. */ ACONFIGURATION_MNC_ZERO = 0xffff, + + /** + * <a href="/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier">Grammatical gender</a>: not specified. + */ + ACONFIGURATION_GRAMMATICAL_GENDER_ANY = 0, + + /** + * <a href="/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier">Grammatical gender</a>: neuter. + */ + ACONFIGURATION_GRAMMATICAL_GENDER_NEUTER = 1, + + /** + * <a href="/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier">Grammatical gender</a>: feminine. + */ + ACONFIGURATION_GRAMMATICAL_GENDER_FEMININE = 2, + + /** + * <a href="/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier">Grammatical gender</a>: masculine. + */ + ACONFIGURATION_GRAMMATICAL_GENDER_MASCULINE = 3, }; /** @@ -726,6 +752,24 @@ int32_t AConfiguration_getLayoutDirection(AConfiguration* config) __INTRODUCED_I void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) __INTRODUCED_IN(17); /** + * Return the configuration's grammatical gender, or ACONFIGURATION_GRAMMATICAL_GENDER_ANY if + * not set. + * + * Available since API level 34. + */ +int32_t AConfiguration_getGrammaticalGender(AConfiguration* config) + __INTRODUCED_IN(__ANDROID_API_U__); + +/** + * Set the configuration's grammatical gender to one of the + * ACONFIGURATION_GRAMMATICAL_GENDER_* constants. + * + * Available since API level 34. + */ +void AConfiguration_setGrammaticalGender(AConfiguration* config, int32_t value) + __INTRODUCED_IN(__ANDROID_API_U__); + +/** * Perform a diff between two configurations. Returns a bit mask of * ACONFIGURATION_* constants, each bit set meaning that configuration element * is different between them. diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 62c3ae15ce..da97c3e855 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -51,33 +51,24 @@ public: static const size_t MAX_DEGREE = 4; // Estimator time base. - nsecs_t time; + nsecs_t time = 0; // Polynomial coefficients describing motion. - float coeff[MAX_DEGREE + 1]; + std::array<float, MAX_DEGREE + 1> coeff{}; // Polynomial degree (number of coefficients), or zero if no information is // available. - uint32_t degree; + uint32_t degree = 0; // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). - float confidence; - - inline void clear() { - time = 0; - degree = 0; - confidence = 0; - for (size_t i = 0; i <= MAX_DEGREE; i++) { - coeff[i] = 0; - } - } + float confidence = 0; }; /* * Contains all available velocity data from a VelocityTracker. */ struct ComputedVelocity { - inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const { + inline std::optional<float> getVelocity(int32_t axis, int32_t id) const { const auto& axisVelocities = mVelocities.find(axis); if (axisVelocities == mVelocities.end()) { return {}; @@ -91,7 +82,7 @@ public: return axisIdVelocity->second; } - inline void addVelocity(int32_t axis, uint32_t id, float velocity) { + inline void addVelocity(int32_t axis, int32_t id, float velocity) { mVelocities[axis][id] = velocity; } @@ -112,19 +103,13 @@ public: // Resets the velocity tracker state. void clear(); - // Resets the velocity tracker state for specific pointers. + // Resets the velocity tracker state for a specific pointer. // Call this method when some pointers have changed and may be reusing // an id that was assigned to a different pointer earlier. - void clearPointers(BitSet32 idBits); + void clearPointer(int32_t pointerId); - // Adds movement information for a set of pointers. - // The idBits bitfield specifies the pointer ids of the pointers whose data points - // are included in the movement. - // The positions map contains a mapping of an axis to positions array. - // The positions arrays contain information for each pointer in order by increasing id. - // Each array's size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::map<int32_t, std::vector<float>>& positions); + // Adds movement information for a pointer for a specific axis + void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); @@ -132,7 +117,7 @@ public: // Returns the velocity of the specified pointer id and axis in position units per second. // Returns empty optional if there is insufficient movement information for the pointer, or if // the given axis is not supported for velocity tracking. - std::optional<float> getVelocity(int32_t axis, uint32_t id) const; + std::optional<float> getVelocity(int32_t axis, int32_t pointerId) const; // Returns a ComputedVelocity instance with all available velocity data, using the given units // (reference: units == 1 means "per millisecond"), and clamping each velocity between @@ -142,15 +127,15 @@ public: // Gets an estimator for the recent movements of the specified pointer id for the given axis. // Returns false and clears the estimator if there is no information available // about the pointer. - bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const; + std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const; // Gets the active pointer id, or -1 if none. - inline int32_t getActivePointerId() const { return mActivePointerId; } + inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); } private: nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; - int32_t mActivePointerId; + std::optional<int32_t> mActivePointerId; // An override strategy passed in the constructor to be used for all axes. // This strategy will apply to all axes, unless the default strategy is specified here. @@ -182,10 +167,9 @@ protected: public: virtual ~VelocityTrackerStrategy() { } - virtual void clearPointers(BitSet32 idBits) = 0; - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) = 0; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; + virtual void clearPointer(int32_t pointerId) = 0; + virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0; + virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0; }; @@ -194,29 +178,28 @@ public: */ class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { public: - enum Weighting { + enum class Weighting { // No weights applied. All data points are equally reliable. - WEIGHTING_NONE, + NONE, // Weight by time delta. Data points clustered together are weighted less. - WEIGHTING_DELTA, + DELTA, // Weight such that points within a certain horizon are weighed more than those // outside of that horizon. - WEIGHTING_CENTRAL, + CENTRAL, // Weight such that points older than a certain amount are weighed less. - WEIGHTING_RECENT, + RECENT, }; // Degree must be no greater than Estimator::MAX_DEGREE. - LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE); - virtual ~LeastSquaresVelocityTrackerStrategy(); + LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE); + ~LeastSquaresVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Sample horizon. @@ -229,18 +212,15 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; - float chooseWeight(uint32_t index) const; + float chooseWeight(int32_t pointerId, uint32_t index) const; const uint32_t mDegree; const Weighting mWeighting; - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; @@ -251,12 +231,11 @@ class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy { public: // Degree must be 1 or 2. IntegratingVelocityTrackerStrategy(uint32_t degree); - ~IntegratingVelocityTrackerStrategy(); + ~IntegratingVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Current state estimate for a particular pointer. @@ -283,12 +262,11 @@ private: class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { public: LegacyVelocityTrackerStrategy(); - virtual ~LegacyVelocityTrackerStrategy(); + ~LegacyVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Oldest sample to consider when calculating the velocity. @@ -302,25 +280,21 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { public: ImpulseVelocityTrackerStrategy(bool deltaValues); - virtual ~ImpulseVelocityTrackerStrategy(); + ~ImpulseVelocityTrackerStrategy() override; - virtual void clearPointers(BitSet32 idBits); - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; - virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; + std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: // Sample horizon. @@ -333,10 +307,7 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; // Whether or not the input movement values for the strategy come in the form of delta values. @@ -344,8 +315,8 @@ private: // velocity calculation. const bool mDeltaValues; - size_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; } // namespace android diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 8cc8105ff9..dc572ac953 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -230,6 +230,7 @@ constexpr const char* const kManualInterfaces[] = { "android.graphicsenv.IGpuService", "android.gui.IConsumerListener", "android.gui.IGraphicBufferConsumer", + "android.gui.ITransactionComposerListener", "android.gui.SensorEventConnection", "android.gui.SensorServer", "android.hardware.ICamera", diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index a0e75ffe49..a77ca04943 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -42,7 +42,6 @@ using namespace aidl::android::hardware::graphics; namespace android { -using gui::CallbackId; using gui::DisplayCaptureArgs; using gui::IDisplayEventConnection; using gui::IRegionSamplingListener; diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 23d7d500c8..2b25b614e9 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -21,11 +21,22 @@ #include <optional> #include <gui/ISurfaceComposer.h> +#include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> -#include <gui/ListenerStats.h> #include <private/gui/ParcelUtils.h> -namespace android::gui { +namespace android { + +namespace { // Anonymous + +enum class Tag : uint32_t { + ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION, + ON_RELEASE_BUFFER, + ON_TRANSACTION_QUEUE_STALLED, + LAST = ON_TRANSACTION_QUEUE_STALLED, +}; + +} // Anonymous namespace status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t err = output->writeUint64(frameNumber); @@ -263,6 +274,60 @@ ListenerStats ListenerStats::createEmpty( return listenerStats; } +class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> { +public: + explicit BpTransactionCompletedListener(const sp<IBinder>& impl) + : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") { + } + + ~BpTransactionCompletedListener() override; + + void onTransactionCompleted(ListenerStats stats) override { + callRemoteAsync<decltype(&ITransactionCompletedListener:: + onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED, + stats); + } + + void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence, + uint32_t currentMaxAcquiredBufferCount) override { + callRemoteAsync<decltype(&ITransactionCompletedListener:: + onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId, + releaseFence, + currentMaxAcquiredBufferCount); + } + + void onTransactionQueueStalled(const String8& reason) override { + callRemoteAsync< + decltype(&ITransactionCompletedListener:: + onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED, + reason); + } +}; + +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpTransactionCompletedListener::~BpTransactionCompletedListener() = default; + +IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener"); + +status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_TRANSACTION_COMPLETED: + return callLocalAsync(data, reply, + &ITransactionCompletedListener::onTransactionCompleted); + case Tag::ON_RELEASE_BUFFER: + return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer); + case Tag::ON_TRANSACTION_QUEUE_STALLED: + return callLocalAsync(data, reply, + &ITransactionCompletedListener::onTransactionQueueStalled); + } +} + ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const { std::vector<CallbackId> filteredCallbackIds; for (const auto& callbackId : callbackIds) { @@ -301,4 +366,4 @@ status_t ReleaseCallbackId::readFromParcel(const Parcel* input) { const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0); -}; // namespace android::gui +}; // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 0d1a69b898..59b62fe58c 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -51,7 +51,6 @@ namespace android { -using gui::CallbackId; using gui::FocusRequest; using gui::WindowInfoHandle; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a2ed8aa8ab..92125ead1f 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -314,8 +314,7 @@ void TransactionCompletedListener::addSurfaceControlToCallbacks( } } -binder::Status TransactionCompletedListener::onTransactionCompleted( - const ListenerStats& listenerStats) { +void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; std::multimap<int32_t, sp<JankDataListener>> jankListenersMap; { @@ -455,10 +454,9 @@ binder::Status TransactionCompletedListener::onTransactionCompleted( } } } - return binder::Status::ok(); } -binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) { +void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) { std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy; { std::scoped_lock<std::mutex> lock(mMutex); @@ -467,7 +465,6 @@ binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std for (auto const& it : callbackCopy) { it.second(reason.c_str()); } - return binder::Status::ok(); } void TransactionCompletedListener::addQueueStallListener( @@ -481,12 +478,9 @@ void TransactionCompletedListener::removeQueueStallListener(void* id) { mQueueStallListeners.erase(id); } -binder::Status TransactionCompletedListener::onReleaseBuffer( - const ReleaseCallbackId& callbackId, - const std::optional<os::ParcelFileDescriptor>& releaseFenceFd, - int32_t currentMaxAcquiredBufferCount) { - sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get())) - : Fence::NO_FENCE); +void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId, + sp<Fence> releaseFence, + uint32_t currentMaxAcquiredBufferCount) { ReleaseBufferCallback callback; { std::scoped_lock<std::mutex> lock(mMutex); @@ -495,14 +489,13 @@ binder::Status TransactionCompletedListener::onReleaseBuffer( if (!callback) { ALOGE("Could not call release buffer callback, buffer not found %s", callbackId.to_string().c_str()); - return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT); + return; } std::optional<uint32_t> optionalMaxAcquiredBufferCount = - static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX + currentMaxAcquiredBufferCount == UINT_MAX ? std::nullopt : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount); callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount); - return binder::Status::ok(); } ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked( @@ -832,11 +825,7 @@ void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_ ->mReleaseCallbackThread .addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence); } else { - std::optional<os::ParcelFileDescriptor> fenceFd; - if (fence != Fence::NO_FENCE) { - fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get()))); - } - listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX); + listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX); } } @@ -2496,6 +2485,20 @@ status_t SurfaceComposerClient::clearBootDisplayMode(const sp<IBinder>& display) return statusTFromBinderStatus(status); } +status_t SurfaceComposerClient::getHdrConversionCapabilities( + std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) { + binder::Status status = ComposerServiceAIDL::getComposerService()->getHdrConversionCapabilities( + hdrConversionCapabilities); + return statusTFromBinderStatus(status); +} + +status_t SurfaceComposerClient::setHdrConversionStrategy( + gui::HdrConversionStrategy hdrConversionStrategy) { + binder::Status status = ComposerServiceAIDL::getComposerService()->setHdrConversionStrategy( + hdrConversionStrategy); + return statusTFromBinderStatus(status); +} + status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) { binder::Status status = ComposerServiceAIDL::getComposerService()->setOverrideFrameRate(uid, frameRate); @@ -2866,11 +2869,7 @@ void ReleaseCallbackThread::threadMain() { while (!callbackInfos.empty()) { auto [callbackId, releaseFence] = callbackInfos.front(); - std::optional<os::ParcelFileDescriptor> fenceFd; - if (releaseFence != Fence::NO_FENCE) { - fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get()))); - } - listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX); + listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX); callbackInfos.pop(); } diff --git a/libs/gui/aidl/android/gui/ListenerStats.aidl b/libs/gui/aidl/android/gui/HdrConversionCapability.aidl index 63248b2bf3..1bcfd38eff 100644 --- a/libs/gui/aidl/android/gui/ListenerStats.aidl +++ b/libs/gui/aidl/android/gui/HdrConversionCapability.aidl @@ -16,4 +16,10 @@ package android.gui; -parcelable ListenerStats cpp_header "gui/ListenerStats.h"; +// TODO(b/265277221): use android.hardware.graphics.common.HdrConversionCapability.aidl +/** @hide */ +parcelable HdrConversionCapability { + int sourceType; + int outputType; + boolean addsLatency; +}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl b/libs/gui/aidl/android/gui/HdrConversionStrategy.aidl index c86de34de9..1be74b46e7 100644 --- a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl +++ b/libs/gui/aidl/android/gui/HdrConversionStrategy.aidl @@ -16,4 +16,10 @@ package android.gui; -parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h"; +// TODO(b/265277221): use android.hardware.graphics.common.HdrConversionStrategy.aidl +/** @hide */ +union HdrConversionStrategy { + boolean passthrough = true; + int[] autoAllowedHdrTypes; + int forceHdrConversion; +} diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index a0b613c059..c08a7c67ae 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -30,6 +30,8 @@ import android.gui.DisplayStatInfo; import android.gui.DynamicDisplayInfo; import android.gui.FrameEvent; import android.gui.FrameStats; +import android.gui.HdrConversionCapability; +import android.gui.HdrConversionStrategy; import android.gui.IDisplayEventConnection; import android.gui.IFpsListener; import android.gui.IHdrLayerInfoListener; @@ -162,6 +164,26 @@ interface ISurfaceComposer { boolean getBootDisplayModeSupport(); /** + * Gets the HDR conversion capabilities of the device. The conversion capability defines whether + * conversion from sourceType to outputType is possible (with or without latency). + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + List<HdrConversionCapability> getHdrConversionCapabilities(); + + /** + * Sets the HDR conversion strategy of the device. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + void setHdrConversionStrategy(in HdrConversionStrategy hdrConversionStrategy); + + /** + * Gets whether HDR output conversion operations are supported on the device. + */ + boolean getHdrOutputConversionSupport(); + + /** * Switches Auto Low Latency Mode on/off on the connected display, if it is * available. This should only be called if the display supports Auto Low * Latency Mode as reported in #getDynamicDisplayInfo. diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index 8810e4e83a..685bd9290d 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -91,6 +91,11 @@ public: MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override)); MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override)); MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override)); + MOCK_METHOD(binder::Status, getHdrConversionCapabilities, + (std::vector<gui::HdrConversionCapability>*), (override)); + MOCK_METHOD(binder::Status, setHdrConversionStrategy, (const gui::HdrConversionStrategy&), + (override)); + MOCK_METHOD(binder::Status, getHdrOutputConversionSupport, (bool*), (override)); MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override)); MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override)); MOCK_METHOD(binder::Status, captureDisplay, diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 06a246eddf..045cc2a184 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -23,11 +23,11 @@ #include <android/gui/IHdrLayerInfoListener.h> #include <android/gui/IRegionSamplingListener.h> #include <android/gui/IScreenCaptureListener.h> -#include <android/gui/ITransactionCompletedListener.h> #include <android/gui/ITunnelModeEnabledListener.h> #include <android/gui/IWindowInfosListener.h> #include <binder/IBinder.h> #include <binder/IInterface.h> +#include <gui/ITransactionCompletedListener.h> #include <gui/SpHash.h> #include <math/vec4.h> #include <stdint.h> @@ -66,7 +66,6 @@ using gui::FrameTimelineInfo; using gui::IDisplayEventConnection; using gui::IRegionSamplingListener; using gui::IScreenCaptureListener; -using gui::ListenerCallbacks; using gui::SpHash; namespace gui { diff --git a/libs/gui/include/gui/ListenerStats.h b/libs/gui/include/gui/ITransactionCompletedListener.h index 3a12802146..453e8f3ef5 100644 --- a/libs/gui/include/gui/ListenerStats.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -24,8 +24,6 @@ #include <binder/SafeInterface.h> #include <gui/FrameTimestamps.h> -#include <gui/ReleaseCallbackId.h> - #include <ui/Fence.h> #include <utils/Timers.h> @@ -34,7 +32,10 @@ #include <unordered_set> #include <variant> -namespace android::gui { +namespace android { + +class ITransactionCompletedListener; +class ListenerCallbacks; class CallbackId : public Parcelable { public: @@ -53,6 +54,30 @@ struct CallbackIdHash { std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); } }; +class ReleaseCallbackId : public Parcelable { +public: + static const ReleaseCallbackId INVALID_ID; + + uint64_t bufferId; + uint64_t framenumber; + ReleaseCallbackId() {} + ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber) + : bufferId(bufferId), framenumber(framenumber) {} + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + bool operator==(const ReleaseCallbackId& rhs) const { + return bufferId == rhs.bufferId && framenumber == rhs.framenumber; + } + bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); } + std::string to_string() const { + if (*this == INVALID_ID) return "INVALID_ID"; + + return "bufferId:" + std::to_string(bufferId) + + " framenumber:" + std::to_string(framenumber); + } +}; + struct ReleaseBufferCallbackIdHash { std::size_t operator()(const ReleaseCallbackId& key) const { return std::hash<uint64_t>()(key.bufferId); @@ -161,6 +186,27 @@ public: std::vector<TransactionStats> transactionStats; }; +class ITransactionCompletedListener : public IInterface { +public: + DECLARE_META_INTERFACE(TransactionCompletedListener) + + virtual void onTransactionCompleted(ListenerStats stats) = 0; + + virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence, + uint32_t currentMaxAcquiredBufferCount) = 0; + + virtual void onTransactionQueueStalled(const String8& name) = 0; +}; + +class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> { +public: + BnTransactionCompletedListener() + : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + class ListenerCallbacks { public: ListenerCallbacks(const sp<IBinder>& listener, @@ -222,4 +268,4 @@ struct ListenerCallbacksHash { } }; -} // namespace android::gui +} // namespace android diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index c5fdf82d4f..45a84f6c66 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -21,10 +21,10 @@ #include <stdint.h> #include <sys/types.h> -#include <android/gui/ITransactionCompletedListener.h> #include <android/gui/IWindowInfosReportedListener.h> #include <android/native_window.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ITransactionCompletedListener.h> #include <math/mat4.h> #include <android/gui/DropInputMode.h> @@ -35,7 +35,6 @@ #include <gui/ISurfaceComposer.h> #include <gui/LayerCaptureArgs.h> #include <gui/LayerMetadata.h> -#include <gui/ReleaseCallbackId.h> #include <gui/SpHash.h> #include <gui/SurfaceControl.h> #include <gui/WindowInfo.h> @@ -57,9 +56,6 @@ class Parcel; using gui::ISurfaceComposerClient; using gui::LayerMetadata; -using gui::ITransactionCompletedListener; -using gui::ReleaseCallbackId; - struct client_cache_t { wp<IBinder> token = nullptr; uint64_t id; diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h deleted file mode 100644 index 142ee5a727..0000000000 --- a/libs/gui/include/gui/ReleaseCallbackId.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 <binder/Parcel.h> -#include <binder/Parcelable.h> - -#include <cstdint> - -namespace android::gui { - -class ReleaseCallbackId : public Parcelable { -public: - static const ReleaseCallbackId INVALID_ID; - - uint64_t bufferId; - uint64_t framenumber; - ReleaseCallbackId() {} - ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber) - : bufferId(bufferId), framenumber(framenumber) {} - status_t writeToParcel(Parcel* output) const override; - status_t readFromParcel(const Parcel* input) override; - - bool operator==(const ReleaseCallbackId& rhs) const { - return bufferId == rhs.bufferId && framenumber == rhs.framenumber; - } - bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); } - std::string to_string() const { - if (*this == INVALID_ID) return "INVALID_ID"; - - return "bufferId:" + std::to_string(bufferId) + - " framenumber:" + std::to_string(framenumber); - } -}; - -} // namespace android::gui diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index cc459c53b5..2458a40ce0 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -42,13 +42,10 @@ #include <android/gui/ISurfaceComposerClient.h> -#include <android/gui/BnTransactionCompletedListener.h> - #include <gui/CpuConsumer.h> #include <gui/ISurfaceComposer.h> +#include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> -#include <gui/ListenerStats.h> -#include <gui/ReleaseCallbackId.h> #include <gui/SurfaceControl.h> #include <gui/WindowInfosListenerReporter.h> #include <math/vec3.h> @@ -62,21 +59,11 @@ class IGraphicBufferProducer; class ITunnelModeEnabledListener; class Region; -using gui::BnTransactionCompletedListener; -using gui::CallbackId; -using gui::CallbackIdHash; using gui::DisplayCaptureArgs; -using gui::FrameEventHistoryStats; using gui::IRegionSamplingListener; using gui::ISurfaceComposerClient; -using gui::ITransactionCompletedListener; -using gui::JankData; using gui::LayerCaptureArgs; using gui::LayerMetadata; -using gui::ListenerStats; -using gui::ReleaseBufferCallbackIdHash; -using gui::ReleaseCallbackId; -using gui::SurfaceStats; struct SurfaceControlStats { SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, @@ -200,6 +187,11 @@ public: // Clears the user-preferred display mode static status_t clearBootDisplayMode(const sp<IBinder>& display); + // Gets the HDR conversion capabilities of the device + static status_t getHdrConversionCapabilities(std::vector<gui::HdrConversionCapability>*); + // Sets the HDR conversion strategy for the device + static status_t setHdrConversionStrategy(gui::HdrConversionStrategy hdrConversionStrategy); + // Sets the frame rate of a particular app (uid). This is currently called // by GameManager. static status_t setOverrideFrameRate(uid_t uid, float frameRate); @@ -841,17 +833,17 @@ public: void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback); // BnTransactionCompletedListener overrides - binder::Status onTransactionCompleted(const ListenerStats& stats) override; - binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId, - const std::optional<os::ParcelFileDescriptor>& releaseFenceFd, - int32_t currentMaxAcquiredBufferCount) override; - binder::Status onTransactionQueueStalled(const std::string& reason) override; + void onTransactionCompleted(ListenerStats stats) override; + void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, + uint32_t currentMaxAcquiredBufferCount) override; void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId); // For Testing Only static void setInstance(const sp<TransactionCompletedListener>&); + void onTransactionQueueStalled(const String8& reason) override; + private: ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&); static sp<TransactionCompletedListener> sInstance; diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 55242dfd39..30148042fb 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -819,6 +819,20 @@ public: return binder::Status::ok(); } + binder::Status getHdrConversionCapabilities( + std::vector<gui::HdrConversionCapability>*) override { + return binder::Status::ok(); + } + + binder::Status setHdrConversionStrategy( + const gui::HdrConversionStrategy& /*hdrConversionStrategy*/) override { + return binder::Status::ok(); + } + + binder::Status getHdrOutputConversionSupport(bool* /*outSupport*/) override { + return binder::Status::ok(); + } + binder::Status setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override { return binder::Status::ok(); } diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index 5c008b1158..5720099033 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -70,9 +70,10 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (deltaY) { mRawPositionY += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), - {{AMOTION_EVENT_AXIS_X, {mRawPositionX}}, - {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}}); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, + mRawPositionX); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, + mRawPositionY); std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 19b4684e4a..3632914b93 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -23,6 +23,7 @@ #include <optional> #include <android-base/stringprintf.h> +#include <input/PrintTools.h> #include <input/VelocityTracker.h> #include <utils/BitSet.h> #include <utils/Timers.h> @@ -142,10 +143,7 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- VelocityTracker::VelocityTracker(const Strategy strategy) - : mLastEventTime(0), - mCurrentPointerIdBits(0), - mActivePointerId(-1), - mOverrideStrategy(strategy) {} + : mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {} VelocityTracker::~VelocityTracker() { } @@ -191,17 +189,17 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( return std::make_unique< LeastSquaresVelocityTrackerStrategy>(2, LeastSquaresVelocityTrackerStrategy:: - WEIGHTING_DELTA); + Weighting::DELTA); case VelocityTracker::Strategy::WLSQ2_CENTRAL: return std::make_unique< LeastSquaresVelocityTrackerStrategy>(2, LeastSquaresVelocityTrackerStrategy:: - WEIGHTING_CENTRAL); + Weighting::CENTRAL); case VelocityTracker::Strategy::WLSQ2_RECENT: return std::make_unique< LeastSquaresVelocityTrackerStrategy>(2, LeastSquaresVelocityTrackerStrategy:: - WEIGHTING_RECENT); + Weighting::RECENT); case VelocityTracker::Strategy::INT1: return std::make_unique<IntegratingVelocityTrackerStrategy>(1); @@ -220,30 +218,30 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( void VelocityTracker::clear() { mCurrentPointerIdBits.clear(); - mActivePointerId = -1; + mActivePointerId = std::nullopt; mConfiguredStrategies.clear(); } -void VelocityTracker::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); - mCurrentPointerIdBits = remainingIdBits; +void VelocityTracker::clearPointer(int32_t pointerId) { + mCurrentPointerIdBits.clearBit(pointerId); - if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { - mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; + if (mActivePointerId && *mActivePointerId == pointerId) { + // The active pointer id is being removed. Mark it invalid and try to find a new one + // from the remaining pointers. + mActivePointerId = std::nullopt; + if (!mCurrentPointerIdBits.isEmpty()) { + mActivePointerId = mCurrentPointerIdBits.firstMarkedBit(); + } } for (const auto& [_, strategy] : mConfiguredStrategies) { - strategy->clearPointers(idBits); + strategy->clearPointer(pointerId); } } -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::map<int32_t /*axis*/, std::vector<float>>& positions) { - while (idBits.count() > MAX_POINTERS) { - idBits.clearLastMarkedBit(); - } - - if ((mCurrentPointerIdBits.value & idBits.value) && +void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, + float position) { + if (mCurrentPointerIdBits.hasBit(pointerId) && std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) { ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.", toString(std::chrono::nanoseconds(eventTime - mLastEventTime)).c_str()); @@ -254,39 +252,28 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, } mLastEventTime = eventTime; - mCurrentPointerIdBits = idBits; - if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { - mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); + mCurrentPointerIdBits.markBit(pointerId); + if (!mActivePointerId) { + // Let this be the new active pointer if no active pointer is currently set + mActivePointerId = pointerId; } - for (const auto& [axis, positionValues] : positions) { - LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(), - "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", - idBits.count(), positionValues.size()); - if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { - configureStrategy(axis); - } - mConfiguredStrategies[axis]->addMovement(eventTime, idBits, positionValues); + if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { + configureStrategy(axis); } + mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position); if (DEBUG_VELOCITY) { - ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 - ", idBits=0x%08x, activePointerId=%d", - eventTime, idBits.value, mActivePointerId); - for (const auto& positionsEntry : positions) { - for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { - uint32_t id = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(id); - iterBits.clearBit(id); - Estimator estimator; - getEstimator(positionsEntry.first, id, &estimator); - ALOGD(" %d: axis=%d, position=%0.3f, " - "estimator (degree=%d, coeff=%s, confidence=%f)", - id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree), - vectorToString(estimator.coeff, estimator.degree + 1).c_str(), - estimator.confidence); - } - } + ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32 + ", activePointerId=%s", + eventTime, pointerId, toString(mActivePointerId).c_str()); + + std::optional<Estimator> estimator = getEstimator(axis, pointerId); + ALOGD(" %d: axis=%d, position=%0.3f, " + "estimator (degree=%d, coeff=%s, confidence=%f)", + pointerId, axis, position, int((*estimator).degree), + vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(), + (*estimator).confidence); } } @@ -296,94 +283,75 @@ void VelocityTracker::addMovement(const MotionEvent* event) { int32_t actionMasked = event->getActionMasked(); switch (actionMasked) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - // Clear all pointers on down before adding the new movement. - clear(); - axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); - break; - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - // Start a new movement trace for a pointer that just went down. - // We do this on down instead of on up because the client may want to query the - // final velocity for a pointer that just went up. - BitSet32 downIdBits; - downIdBits.markBit(event->getPointerId(event->getActionIndex())); - clearPointers(downIdBits); - axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); - break; - } - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); - break; - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_UP: { - std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); - if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { - ALOGD_IF(DEBUG_VELOCITY, - "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", - toString(delaySinceLastEvent).c_str()); - // We have not received any movements for too long. Assume that all pointers - // have stopped. - for (int32_t axis : PLANAR_AXES) { - mConfiguredStrategies.erase(axis); + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + // Clear all pointers on down before adding the new movement. + clear(); + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); + break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + // Start a new movement trace for a pointer that just went down. + // We do this on down instead of on up because the client may want to query the + // final velocity for a pointer that just went up. + clearPointer(event->getPointerId(event->getActionIndex())); + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); + break; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); + break; + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: { + std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); + if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { + ALOGD_IF(DEBUG_VELOCITY, + "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", + toString(delaySinceLastEvent).c_str()); + // We have not received any movements for too long. Assume that all pointers + // have stopped. + for (int32_t axis : PLANAR_AXES) { + mConfiguredStrategies.erase(axis); + } } + // These actions because they do not convey any new information about + // pointer movement. We also want to preserve the last known velocity of the pointers. + // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position + // of the pointers that went up. ACTION_POINTER_UP does include the new position of + // pointers that remained down but we will also receive an ACTION_MOVE with this + // information if any of them actually moved. Since we don't know how many pointers + // will be going up at once it makes sense to just wait for the following ACTION_MOVE + // before adding the movement. + return; } - // These actions because they do not convey any new information about - // pointer movement. We also want to preserve the last known velocity of the pointers. - // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position - // of the pointers that went up. ACTION_POINTER_UP does include the new position of - // pointers that remained down but we will also receive an ACTION_MOVE with this - // information if any of them actually moved. Since we don't know how many pointers - // will be going up at once it makes sense to just wait for the following ACTION_MOVE - // before adding the movement. - return; - } - case AMOTION_EVENT_ACTION_SCROLL: - axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); - break; - default: - // Ignore all other actions. - return; - } - - size_t pointerCount = event->getPointerCount(); - if (pointerCount > MAX_POINTERS) { - pointerCount = MAX_POINTERS; - } - - BitSet32 idBits; - for (size_t i = 0; i < pointerCount; i++) { - idBits.markBit(event->getPointerId(i)); - } - - uint32_t pointerIndex[MAX_POINTERS]; - for (size_t i = 0; i < pointerCount; i++) { - pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); - } - - std::map<int32_t, std::vector<float>> positions; - for (int32_t axis : axesToProcess) { - positions[axis].resize(pointerCount); + case AMOTION_EVENT_ACTION_SCROLL: + axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); + break; + default: + // Ignore all other actions. + return; } - size_t historySize = event->getHistorySize(); + const size_t historySize = event->getHistorySize(); for (size_t h = 0; h <= historySize; h++) { - nsecs_t eventTime = event->getHistoricalEventTime(h); - for (int32_t axis : axesToProcess) { - for (size_t i = 0; i < pointerCount; i++) { - positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h); + const nsecs_t eventTime = event->getHistoricalEventTime(h); + for (size_t i = 0; i < event->getPointerCount(); i++) { + if (event->isResampled(i, h)) { + continue; // skip resampled samples + } + const int32_t pointerId = event->getPointerId(i); + for (int32_t axis : axesToProcess) { + const float position = event->getHistoricalAxisValue(axis, i, h); + addMovement(eventTime, pointerId, axis, position); } } - addMovement(eventTime, idBits, positions); } } -std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const { - Estimator estimator; - bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1; - if (validVelocity) { - return estimator.coeff[1]; +std::optional<float> VelocityTracker::getVelocity(int32_t axis, int32_t pointerId) const { + std::optional<Estimator> estimator = getEstimator(axis, pointerId); + if (estimator && (*estimator).degree >= 1) { + return (*estimator).coeff[1]; } return {}; } @@ -406,50 +374,55 @@ VelocityTracker::ComputedVelocity VelocityTracker::getComputedVelocity(int32_t u return computedVelocity; } -bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const { +std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis, + int32_t pointerId) const { const auto& it = mConfiguredStrategies.find(axis); if (it == mConfiguredStrategies.end()) { - return false; + return std::nullopt; } - return it->second->getEstimator(id, outEstimator); + return it->second->getEstimator(pointerId); } // --- LeastSquaresVelocityTrackerStrategy --- LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting) - : mDegree(degree), mWeighting(weighting), mIndex(0) {} + : mDegree(degree), mWeighting(weighting) {} LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { } -void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (mMovements[mIndex].eventTime != eventTime) { +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. - mIndex++; + index++; } - if (mIndex == HISTORY_SIZE) { - mIndex = 0; + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } /** @@ -502,7 +475,9 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet3 * http://en.wikipedia.org/wiki/Gram-Schmidt */ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y, - const std::vector<float>& w, uint32_t n, float* outB, float* outDet) { + const std::vector<float>& w, uint32_t n, + std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB, + float* outDet) { const size_t m = x.size(); ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), @@ -583,7 +558,7 @@ static bool solveLeastSquares(const std::vector<float>& x, const std::vector<flo outB[i] /= r[i][i]; } - ALOGD_IF(DEBUG_STRATEGY, " - b=%s", vectorToString(outB, n).c_str()); + ALOGD_IF(DEBUG_STRATEGY, " - b=%s", vectorToString(outB.data(), n).c_str()); // Calculate the coefficient of determination as 1 - (SSerr / SStot) where // SSerr is the residual sum of squares (variance of the error), @@ -671,37 +646,47 @@ static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( return std::make_optional(std::array<float, 3>({c, b, a})); } -bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - +std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data + } // Iterate over movement samples in reverse time order and collect samples. std::vector<float> positions; std::vector<float> w; std::vector<float> time; - uint32_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; + uint32_t index = mIndex.at(pointerId); + const Movement& newestMovement = movementIt->second[index]; do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(id)) { - break; - } + const Movement& movement = movementIt->second[index]; nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } - - positions.push_back(movement.getPosition(id)); - w.push_back(chooseWeight(index)); + if (movement.eventTime == 0 && index != 0) { + // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's + // possible that not all entries are valid. We use a time=0 as a signal for those + // uninitialized values. If we encounter a time of 0 in a position + // that's > 0, it means that we hit the block where the data wasn't initialized. + // We still don't know whether the value at index=0, with eventTime=0 is valid. + // However, that's only possible when the value is by itself. So there's no hard in + // processing it anyways, since the velocity for a single point is zero, and this + // situation will only be encountered in artificial circumstances (in tests). + // In practice, time will never be 0. + break; + } + positions.push_back(movement.position); + w.push_back(chooseWeight(pointerId, index)); time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (positions.size() < HISTORY_SIZE); const size_t m = positions.size(); if (m == 0) { - return false; // no data + return std::nullopt; // no data } // Calculate a least squares polynomial fit. @@ -710,112 +695,116 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, degree = m - 1; } - if (degree == 2 && mWeighting == WEIGHTING_NONE) { + if (degree == 2 && mWeighting == Weighting::NONE) { // Optimize unweighted, quadratic polynomial fit std::optional<std::array<float, 3>> coeff = solveUnweightedLeastSquaresDeg2(time, positions); if (coeff) { - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 2; - outEstimator->confidence = 1; - for (size_t i = 0; i <= outEstimator->degree; i++) { - outEstimator->coeff[i] = (*coeff)[i]; + VelocityTracker::Estimator estimator; + estimator.time = newestMovement.eventTime; + estimator.degree = 2; + estimator.confidence = 1; + for (size_t i = 0; i <= estimator.degree; i++) { + estimator.coeff[i] = (*coeff)[i]; } - return true; + return estimator; } } else if (degree >= 1) { // General case for an Nth degree polynomial fit float det; uint32_t n = degree + 1; - if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) { - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = degree; - outEstimator->confidence = det; + VelocityTracker::Estimator estimator; + if (solveLeastSquares(time, positions, w, n, estimator.coeff, &det)) { + estimator.time = newestMovement.eventTime; + estimator.degree = degree; + estimator.confidence = det; ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f", - int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(), - outEstimator->confidence); + int(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(), + estimator.confidence); - return true; + return estimator; } } // No velocity data available for this pointer, but we do have its current position. - outEstimator->coeff[0] = positions[0]; - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 0; - outEstimator->confidence = 1; - return true; + VelocityTracker::Estimator estimator; + estimator.coeff[0] = positions[0]; + estimator.time = newestMovement.eventTime; + estimator.degree = 0; + estimator.confidence = 1; + return estimator; } -float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { +float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const { + const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId); switch (mWeighting) { - case WEIGHTING_DELTA: { - // Weight points based on how much time elapsed between them and the next - // point so that points that "cover" a shorter time span are weighed less. - // delta 0ms: 0.5 - // delta 10ms: 1.0 - if (index == mIndex) { + case Weighting::DELTA: { + // Weight points based on how much time elapsed between them and the next + // point so that points that "cover" a shorter time span are weighed less. + // delta 0ms: 0.5 + // delta 10ms: 1.0 + if (index == mIndex.at(pointerId)) { + return 1.0f; + } + uint32_t nextIndex = (index + 1) % HISTORY_SIZE; + float deltaMillis = + (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f; + if (deltaMillis < 0) { + return 0.5f; + } + if (deltaMillis < 10) { + return 0.5f + deltaMillis * 0.05; + } return 1.0f; } - uint32_t nextIndex = (index + 1) % HISTORY_SIZE; - float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime) - * 0.000001f; - if (deltaMillis < 0) { + + case Weighting::CENTRAL: { + // Weight points based on their age, weighing very recent and very old points less. + // age 0ms: 0.5 + // age 10ms: 1.0 + // age 50ms: 1.0 + // age 60ms: 0.5 + float ageMillis = + (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * + 0.000001f; + if (ageMillis < 0) { + return 0.5f; + } + if (ageMillis < 10) { + return 0.5f + ageMillis * 0.05; + } + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 60) { + return 0.5f + (60 - ageMillis) * 0.05; + } return 0.5f; } - if (deltaMillis < 10) { - return 0.5f + deltaMillis * 0.05; - } - return 1.0f; - } - case WEIGHTING_CENTRAL: { - // Weight points based on their age, weighing very recent and very old points less. - // age 0ms: 0.5 - // age 10ms: 1.0 - // age 50ms: 1.0 - // age 60ms: 0.5 - float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) - * 0.000001f; - if (ageMillis < 0) { + case Weighting::RECENT: { + // Weight points based on their age, weighing older points less. + // age 0ms: 1.0 + // age 50ms: 1.0 + // age 100ms: 0.5 + float ageMillis = + (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * + 0.000001f; + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 100) { + return 0.5f + (100 - ageMillis) * 0.01f; + } return 0.5f; } - if (ageMillis < 10) { - return 0.5f + ageMillis * 0.05; - } - if (ageMillis < 50) { - return 1.0f; - } - if (ageMillis < 60) { - return 0.5f + (60 - ageMillis) * 0.05; - } - return 0.5f; - } - case WEIGHTING_RECENT: { - // Weight points based on their age, weighing older points less. - // age 0ms: 1.0 - // age 50ms: 1.0 - // age 100ms: 0.5 - float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) - * 0.000001f; - if (ageMillis < 50) { + case Weighting::NONE: return 1.0f; - } - if (ageMillis < 100) { - return 0.5f + (100 - ageMillis) * 0.01f; - } - return 0.5f; - } - - case WEIGHTING_NONE: - default: - return 1.0f; } } - // --- IntegratingVelocityTrackerStrategy --- IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : @@ -825,38 +814,32 @@ IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { } -void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - mPointerIdBits.value &= ~idBits.value; +void IntegratingVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mPointerIdBits.clearBit(pointerId); } -void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - uint32_t index = 0; - for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { - uint32_t id = iterIdBits.clearFirstMarkedBit(); - State& state = mPointerState[id]; - const float position = positions[index++]; - if (mPointerIdBits.hasBit(id)) { - updateState(state, eventTime, position); - } else { - initState(state, eventTime, position); - } +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + State& state = mPointerState[pointerId]; + if (mPointerIdBits.hasBit(pointerId)) { + updateState(state, eventTime, position); + } else { + initState(state, eventTime, position); } - mPointerIdBits = idBits; + mPointerIdBits.markBit(pointerId); } -bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - if (mPointerIdBits.hasBit(id)) { - const State& state = mPointerState[id]; - populateEstimator(state, outEstimator); - return true; +std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + if (mPointerIdBits.hasBit(pointerId)) { + const State& state = mPointerState[pointerId]; + VelocityTracker::Estimator estimator; + populateEstimator(state, &estimator); + return estimator; } - return false; + return std::nullopt; } void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime, @@ -916,49 +899,60 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, // --- LegacyVelocityTrackerStrategy --- -LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() : mIndex(0) {} +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {} LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { } -void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (++mIndex == HISTORY_SIZE) { - mIndex = 0; +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { + // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates + // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include + // the new pointer. If the eventtimes for both events are identical, just update the data + // for this time. + // We only compare against the last value, as it is likely that addMovement is called + // in chronological order as events occur. + index++; + } + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } -bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); - - const Movement& newestMovement = mMovements[mIndex]; - if (!newestMovement.idBits.hasBit(id)) { - return false; // no data +std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data } + const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)]; // Find the oldest sample that contains the pointer and that is not older than HORIZON. nsecs_t minTime = newestMovement.eventTime - HORIZON; - uint32_t oldestIndex = mIndex; + uint32_t oldestIndex = mIndex.at(pointerId); uint32_t numTouches = 1; do { uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; - const Movement& nextOldestMovement = mMovements[nextOldestIndex]; - if (!nextOldestMovement.idBits.hasBit(id) - || nextOldestMovement.eventTime < minTime) { + const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex]; + if (nextOldestMovement.eventTime < minTime) { break; } oldestIndex = nextOldestIndex; @@ -978,22 +972,22 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, float accumV = 0; uint32_t index = oldestIndex; uint32_t samplesUsed = 0; - const Movement& oldestMovement = mMovements[oldestIndex]; - float oldestPosition = oldestMovement.getPosition(id); + const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex]; + float oldestPosition = oldestMovement.position; nsecs_t lastDuration = 0; while (numTouches-- > 1) { if (++index == HISTORY_SIZE) { index = 0; } - const Movement& movement = mMovements[index]; + const Movement& movement = mMovements.at(pointerId)[index]; nsecs_t duration = movement.eventTime - oldestMovement.eventTime; // If the duration between samples is small, we may significantly overestimate // the velocity. Consequently, we impose a minimum duration constraint on the // samples that we include in the calculation. if (duration >= MIN_DURATION) { - float position = movement.getPosition(id); + float position = movement.position; float scale = 1000000000.0f / duration; // one over time delta in seconds float v = (position - oldestPosition) * scale; accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration); @@ -1003,54 +997,59 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, } // Report velocity. - float newestPosition = newestMovement.getPosition(id); - outEstimator->time = newestMovement.eventTime; - outEstimator->confidence = 1; - outEstimator->coeff[0] = newestPosition; + float newestPosition = newestMovement.position; + VelocityTracker::Estimator estimator; + estimator.time = newestMovement.eventTime; + estimator.confidence = 1; + estimator.coeff[0] = newestPosition; if (samplesUsed) { - outEstimator->coeff[1] = accumV; - outEstimator->degree = 1; + estimator.coeff[1] = accumV; + estimator.degree = 1; } else { - outEstimator->degree = 0; + estimator.degree = 0; } - return true; + return estimator; } // --- ImpulseVelocityTrackerStrategy --- ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues) - : mDeltaValues(deltaValues), mIndex(0) {} + : mDeltaValues(deltaValues) {} ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { } -void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (mMovements[mIndex].eventTime != eventTime) { +void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. - mIndex++; + index++; } - if (mIndex == HISTORY_SIZE) { - mIndex = 0; + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } /** @@ -1178,55 +1177,61 @@ static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t c return kineticEnergyToVelocity(work); } -bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, - VelocityTracker::Estimator* outEstimator) const { - outEstimator->clear(); +std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator( + int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data + } // Iterate over movement samples in reverse time order and collect samples. float positions[HISTORY_SIZE]; nsecs_t time[HISTORY_SIZE]; size_t m = 0; // number of points that will be used for fitting - size_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; + size_t index = mIndex.at(pointerId); + const Movement& newestMovement = movementIt->second[index]; do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(id)) { - break; - } + const Movement& movement = movementIt->second[index]; nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } + if (movement.eventTime == 0 && index != 0) { + // All eventTime's are initialized to 0. If we encounter a time of 0 in a position + // that's >0, it means that we hit the block where the data wasn't initialized. + // It's also possible that the sample at 0 would be invalid, but there's no harm in + // processing it, since it would be just a single point, and will only be encountered + // in artificial circumstances (in tests). + break; + } - positions[m] = movement.getPosition(id); + positions[m] = movement.position; time[m] = movement.eventTime; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); if (m == 0) { - return false; // no data + return std::nullopt; // no data } - outEstimator->coeff[0] = 0; - outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues); - outEstimator->coeff[2] = 0; + VelocityTracker::Estimator estimator; + estimator.coeff[0] = 0; + estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues); + estimator.coeff[2] = 0; - outEstimator->time = newestMovement.eventTime; - outEstimator->degree = 2; // similar results to 2nd degree fit - outEstimator->confidence = 1; + estimator.time = newestMovement.eventTime; + estimator.degree = 2; // similar results to 2nd degree fit + estimator.confidence = 1; - ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]); + ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]); if (DEBUG_IMPULSE) { // TODO(b/134179997): delete this block once the switch to 'impulse' is complete. // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons. // X axis chosen arbitrarily for velocity comparisons. VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2); - BitSet32 idBits; - const uint32_t pointerId = 0; - idBits.markBit(pointerId); for (ssize_t i = m - 1; i >= 0; i--) { - lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}}); + lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]); } std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId); if (v) { @@ -1235,7 +1240,7 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, ALOGD("lsq2 velocity: could not compute velocity"); } } - return true; + return estimator; } } // namespace android diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 54feea2644..c6ad3a2218 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -84,6 +84,8 @@ struct Position { float x; float y; + bool isResampled = false; + /** * If both values are NAN, then this is considered to be an empty entry (no pointer data). * If only one of the values is NAN, this is still a valid entry, @@ -203,10 +205,11 @@ static std::vector<MotionEvent> createTouchMotionEventStream( coords[pointerIndex].clear(); // We are treating column positions as pointerId - EXPECT_TRUE(entry.positions[pointerId].isValid()) << - "The entry at pointerId must be valid"; - coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_X, entry.positions[pointerId].x); - coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_Y, entry.positions[pointerId].y); + const Position& position = entry.positions[pointerId]; + EXPECT_TRUE(position.isValid()) << "The entry at " << pointerId << " must be valid"; + coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); + coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); + coords[pointerIndex].isResampled = position.isResampled; properties[pointerIndex].id = pointerId; properties[pointerIndex].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; @@ -288,13 +291,13 @@ static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEvent for (MotionEvent event : events) { vt.addMovement(&event); } - VelocityTracker::Estimator estimatorX; - VelocityTracker::Estimator estimatorY; - EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX)); - EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY)); + std::optional<VelocityTracker::Estimator> estimatorX = vt.getEstimator(AMOTION_EVENT_AXIS_X, 0); + std::optional<VelocityTracker::Estimator> estimatorY = vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0); + EXPECT_TRUE(estimatorX); + EXPECT_TRUE(estimatorY); for (size_t i = 0; i< coefficients.size(); i++) { - checkCoefficient(estimatorX.coeff[i], coefficients[i]); - checkCoefficient(estimatorY.coeff[i], coefficients[i]); + checkCoefficient((*estimatorX).coeff[i], coefficients[i]); + checkCoefficient((*estimatorY).coeff[i], coefficients[i]); } } @@ -375,6 +378,44 @@ TEST_F(VelocityTrackerTest, TestComputedVelocity) { EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID + 1)); } +/** + * For a single pointer, the resampled data is ignored. + */ +TEST_F(VelocityTrackerTest, SinglePointerResampledData) { + std::vector<PlanarMotionEventEntry> motions = {{10ms, {{1, 2}}}, + {20ms, {{2, 4}}}, + {30ms, {{3, 6}}}, + {35ms, {{30, 60, .isResampled = true}}}, + {40ms, {{4, 8}}}}; + + computeAndCheckVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_X, 100); + computeAndCheckVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_Y, 200); +} + +/** + * For multiple pointers, the resampled data is ignored on a per-pointer basis. If a certain pointer + * does not have a resampled value, all of the points are used. + */ +TEST_F(VelocityTrackerTest, MultiPointerResampledData) { + std::vector<PlanarMotionEventEntry> motions = { + {0ms, {{0, 0}}}, + {10ms, {{1, 0}, {1, 0}}}, + {20ms, {{2, 0}, {2, 0}}}, + {30ms, {{3, 0}, {3, 0}}}, + {35ms, {{30, 0, .isResampled = true}, {30, 0}}}, + {40ms, {{4, 0}, {4, 0}}}, + {45ms, {{5, 0}}}, // ACTION_UP + }; + + // Sample at t=35ms breaks trend. It's marked as resampled for the first pointer, so it should + // be ignored, and the resulting velocity should be linear. For the second pointer, it's not + // resampled, so it should cause the velocity to be non-linear. + computeAndCheckVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_X, 100, + /*pointerId=*/0); + computeAndCheckVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_X, 3455, + /*pointerId=*/1); +} + TEST_F(VelocityTrackerTest, TestGetComputedVelocity) { std::vector<PlanarMotionEventEntry> motions = { {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}}, @@ -420,8 +461,7 @@ TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); - VelocityTracker::Estimator estimator; - EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID, &estimator)); + EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000); for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) { @@ -432,7 +472,7 @@ TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { EXPECT_EQ(-1, vt.getActivePointerId()); // Make sure that the clearing functions execute without an issue. - vt.clearPointers(BitSet32(7U)); + vt.clearPointer(7U); vt.clear(); } diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp index f67f5dff39..6fbc6b0118 100644 --- a/libs/jpegrecoverymap/jpegdecoder.cpp +++ b/libs/jpegrecoverymap/jpegdecoder.cpp @@ -32,6 +32,10 @@ const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC const std::string kXmpNameSpace = "http://ns.adobe.com/xap/1.0/"; const std::string kExifIdCode = "Exif"; +constexpr uint32_t kICCMarkerHeaderSize = 14; +constexpr uint8_t kICCSig[] = { + 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0', +}; struct jpegr_source_mgr : jpeg_source_mgr { jpegr_source_mgr(const uint8_t* ptr, int len); @@ -336,8 +340,22 @@ bool JpegDecoder::getCompressedImageParameters(const void* image, int length, *pWidth = cinfo.image_width; *pHeight = cinfo.image_height; - //TODO: Parse iccProfile - (void)iccData; + if (iccData != nullptr) { + for (jpeg_marker_struct* marker = cinfo.marker_list; marker; + marker = marker->next) { + if (marker->marker != kAPP2Marker) { + continue; + } + if (marker->data_length <= kICCMarkerHeaderSize || + memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) { + continue; + } + + const unsigned int len = marker->data_length - kICCMarkerHeaderSize; + const uint8_t *src = marker->data + kICCMarkerHeaderSize; + iccData->insert(iccData->end(), src, src+len); + } + } if (exifData != nullptr) { bool exifAppears = false; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index b8fd1b2292..8d19c45527 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -133,8 +133,6 @@ cc_library_static { "-fvisibility=hidden", "-Werror=format", "-Wno-unused-parameter", - // TODO: Investigate reducing pinned-memory usage (b/263377839) - "-DRE_SKIAVK", ], srcs: [ ":librenderengine_sources", diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 341c011dc9..d08c2213ad 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -39,12 +39,8 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg 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( @@ -54,16 +50,12 @@ 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/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 2b8495c3f7..8d99f3d320 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -14,10 +14,6 @@ * 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" @@ -67,7 +63,7 @@ struct VulkanInterface { GrVkExtensions grExtensions; VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; - VkPhysicalDeviceProtectedMemoryProperties* protectedMemoryFeatures = nullptr; + VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr; GrVkGetProc grGetProc; bool isProtected; bool isRealtimePriority; @@ -390,7 +386,7 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext; if (protectedContent) { - interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryProperties; + interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures; interface.protectedMemoryFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; interface.protectedMemoryFeatures->pNext = nullptr; @@ -677,4 +673,3 @@ void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { } // namespace skia } // namespace renderengine } // namespace android -#endif // RE_SKIAVK diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 1e42b80c10..2e0cf45220 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -17,10 +17,6 @@ #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" @@ -59,5 +55,4 @@ private: } // namespace renderengine } // namespace android -#endif // RE_SKIAVK -#endif // SF_SKIAVKRENDERENGINE_H_ +#endif diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 7db95a7ea0..f3f2da8a0e 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -112,7 +112,6 @@ public: virtual bool useColorManagement() const = 0; }; -#ifdef RE_SKIAVK class SkiaVkRenderEngineFactory : public RenderEngineFactory { public: std::string name() override { return "SkiaVkRenderEngineFactory"; } @@ -153,8 +152,6 @@ 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"; } @@ -1560,17 +1557,11 @@ 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()) { diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h index cd3116760b..a461cb4e68 100644 --- a/libs/ui/include_types/ui/DataspaceUtils.h +++ b/libs/ui/include_types/ui/DataspaceUtils.h @@ -22,10 +22,8 @@ namespace android { inline bool isHdrDataspace(ui::Dataspace dataspace) { const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK; - const auto range = dataspace & HAL_DATASPACE_RANGE_MASK; - return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG || - range == HAL_DATASPACE_RANGE_EXTENDED; + return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG; } -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp index ffe64382d0..3e0967182b 100644 --- a/libs/ui/tests/DataspaceUtils_test.cpp +++ b/libs/ui/tests/DataspaceUtils_test.cpp @@ -29,13 +29,12 @@ TEST_F(DataspaceUtilsTest, isHdrDataspace) { EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ)); EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ)); EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG)); - // The original formulation of scRGB indicates the same white points as that - // of sRGB, however scRGB may be used to implement HDR. - EXPECT_TRUE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR)); - EXPECT_TRUE(isHdrDataspace(ui::Dataspace::V0_SCRGB)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR)); + // scRGB defines a very wide gamut but not an expanded luminance range + EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB)); + EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625)); EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525)); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 4d51aeef5d..8cd2cf0255 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -2703,18 +2703,15 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Update the velocity tracker. { - std::vector<float> positionsX; - std::vector<float> positionsY; for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - positionsX.push_back(pointer.x * mPointerXMovementScale); - positionsY.push_back(pointer.y * mPointerYMovementScale); + const float x = pointer.x * mPointerXMovementScale; + const float y = pointer.y * mPointerYMovementScale; + mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_X, x); + mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_Y, y); } - mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - {{AMOTION_EVENT_AXIS_X, positionsX}, - {AMOTION_EVENT_AXIS_Y, positionsY}}); } // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index e0dcab54d7..5647b73a6a 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -176,7 +176,6 @@ filegroup { "RefreshRateOverlay.cpp", "RegionSamplingThread.cpp", "RenderArea.cpp", - "Scheduler/DispSyncSource.cpp", "Scheduler/EventThread.cpp", "Scheduler/FrameRateOverrideMappings.cpp", "Scheduler/OneShotTimer.cpp", diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 33caa7a9b8..6199a5ae40 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -112,6 +112,11 @@ public: MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId)); MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); MOCK_METHOD0(getBootDisplayModeSupport, bool()); + MOCK_CONST_METHOD0( + getHdrConversionCapabilities, + std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>()); + MOCK_METHOD1(setHdrConversionStrategy, + status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy)); MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool)); MOCK_METHOD(status_t, getSupportedContentTypes, (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override)); diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 96ae77fbff..098207709a 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -171,7 +171,7 @@ auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo { void DisplayDevice::setPowerMode(hal::PowerMode mode) { if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) { - if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (mStagedBrightness && mBrightness != mStagedBrightness) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); mBrightness = *mStagedBrightness; } @@ -298,7 +298,7 @@ void DisplayDevice::stageBrightness(float brightness) { } void DisplayDevice::persistBrightness(bool needsComposite) { - if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (mStagedBrightness && mBrightness != mStagedBrightness) { if (needsComposite) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); } @@ -410,7 +410,8 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } -void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) { +void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate, + bool showInMiddle) { if (!enable) { mRefreshRateOverlay.reset(); return; @@ -425,6 +426,10 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool features |= RefreshRateOverlay::Features::RenderRate; } + if (showInMiddle) { + features |= RefreshRateOverlay::Features::ShowInMiddle; + } + const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange(); mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features); mRefreshRateOverlay->setLayerStack(getLayerStack()); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index d757673176..370bd66b9e 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -237,8 +237,8 @@ public: } // Enables an overlay to be displayed with the current refresh rate - void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) - REQUIRES(kMainThreadContext); + void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate, + bool showInMiddle) REQUIRES(kMainThreadContext); bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; } bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired); void animateRefreshRateOverlay(); @@ -272,7 +272,7 @@ private: std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode; std::optional<float> mStagedBrightness; - float mBrightness = -1.f; + std::optional<float> mBrightness; // TODO(b/182939859): Remove special cases for primary display. const bool mIsPrimary; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index e372b72b33..9470552e41 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -56,6 +56,9 @@ using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::D using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute; using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability; using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities; +using AidlHdrConversionCapability = + aidl::android::hardware::graphics::common::HdrConversionCapability; +using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy; using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties; using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata; using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey; @@ -1396,6 +1399,27 @@ Error AidlComposer::getPreferredBootDisplayConfig(Display display, Config* confi return Error::NONE; } +Error AidlComposer::getHdrConversionCapabilities( + std::vector<AidlHdrConversionCapability>* hdrConversionCapabilities) { + const auto status = + mAidlComposerClient->getHdrConversionCapabilities(hdrConversionCapabilities); + if (!status.isOk()) { + hdrConversionCapabilities = {}; + ALOGE("getHdrConversionCapabilities failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + +Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConversionStrategy) { + const auto status = mAidlComposerClient->setHdrConversionStrategy(hdrConversionStrategy); + if (!status.isOk()) { + ALOGE("setHdrConversionStrategy failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + Error AidlComposer::getClientTargetProperty( Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) { Error error = Error::NONE; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 9a7ade7437..a5ddf7457f 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -48,6 +48,8 @@ namespace android::Hwc2 { using aidl::android::hardware::graphics::common::DisplayDecorationSupport; +using aidl::android::hardware::graphics::common::HdrConversionCapability; +using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::ComposerClientReader; using aidl::android::hardware::graphics::composer3::ComposerClientWriter; using aidl::android::hardware::graphics::composer3::OverlayProperties; @@ -235,6 +237,8 @@ public: AidlTransform* outDisplayOrientation) override; void onHotplugConnect(Display) override; void onHotplugDisconnect(Display) override; + Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override; + Error setHdrConversionStrategy(HdrConversionStrategy) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 1c2b8b52d9..82b677e7d4 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -32,6 +32,8 @@ #include <utils/StrongPointer.h> #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> +#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h> +#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h> #include <aidl/android/hardware/graphics/composer3/Capability.h> #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Color.h> @@ -288,6 +290,10 @@ public: virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0; virtual void onHotplugConnect(Display) = 0; virtual void onHotplugDisconnect(Display) = 0; + virtual Error getHdrConversionCapabilities( + std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0; + virtual Error setHdrConversionStrategy( + ::aidl::android::hardware::graphics::common::HdrConversionStrategy) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 10fde2af8e..7dde6b4e44 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -70,6 +70,8 @@ #define RETURN_IF_HWC_ERROR(error, displayId, ...) \ RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__) +using aidl::android::hardware::graphics::common::HdrConversionCapability; +using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; namespace hal = android::hardware::graphics::composer::hal; @@ -97,6 +99,7 @@ void HWComposer::setCallback(HWC2::ComposerCallback& callback) { loadCapabilities(); loadLayerMetadataSupport(); loadOverlayProperties(); + loadHdrConversionCapabilities(); if (mRegisteredCallback) { ALOGW("Callback already registered. Ignored extra registration attempt."); @@ -787,6 +790,18 @@ std::optional<hal::HWConfigId> HWComposer::getPreferredBootDisplayMode( return displayModeId; } +std::vector<HdrConversionCapability> HWComposer::getHdrConversionCapabilities() const { + return mHdrConversionCapabilities; +} + +status_t HWComposer::setHdrConversionStrategy(HdrConversionStrategy hdrConversionStrategy) { + const auto error = mComposer->setHdrConversionStrategy(hdrConversionStrategy); + if (error != hal::Error::NONE) { + ALOGE("Error in setting HDR conversion strategy %s", to_string(error).c_str()); + } + return NO_ERROR; +} + status_t HWComposer::getDisplayDecorationSupport( PhysicalDisplayId displayId, std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* @@ -979,6 +994,14 @@ void HWComposer::loadOverlayProperties() { mComposer->getOverlaySupport(&mOverlayProperties); } +void HWComposer::loadHdrConversionCapabilities() { + const auto error = mComposer->getHdrConversionCapabilities(&mHdrConversionCapabilities); + if (error != hal::Error::NONE) { + ALOGE("Error in fetching HDR conversion capabilities %s", to_string(error).c_str()); + mHdrConversionCapabilities = {}; + } +} + 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 78d4a68617..f6155d24ad 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -44,6 +44,8 @@ #include "Hal.h" #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> +#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h> +#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h> #include <aidl/android/hardware/graphics/composer3/Capability.h> #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> @@ -286,6 +288,10 @@ public: virtual status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) = 0; virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const = 0; virtual Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const = 0; + virtual std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability> + getHdrConversionCapabilities() const = 0; + virtual status_t setHdrConversionStrategy( + aidl::android::hardware::graphics::common::HdrConversionStrategy) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -437,6 +443,10 @@ public: status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) override; bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const override; Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const override; + std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability> + getHdrConversionCapabilities() const override; + status_t setHdrConversionStrategy( + aidl::android::hardware::graphics::common::HdrConversionStrategy) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -490,12 +500,16 @@ private: void loadCapabilities(); void loadLayerMetadataSupport(); void loadOverlayProperties(); + void loadHdrConversionCapabilities(); 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::vector<aidl::android::hardware::graphics::common::HdrConversionCapability> + mHdrConversionCapabilities = {}; + std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata; bool mRegisteredCallback = false; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index c9e1e794ab..6fdb2d79f3 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -36,6 +36,8 @@ #include <algorithm> #include <cinttypes> +using aidl::android::hardware::graphics::common::HdrConversionCapability; +using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; using aidl::android::hardware::graphics::composer3::DimmingStage; @@ -1348,6 +1350,14 @@ Error HidlComposer::getPreferredBootDisplayConfig(Display /*displayId*/, Config* return Error::UNSUPPORTED; } +Error HidlComposer::getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setHdrConversionStrategy(HdrConversionStrategy) { + return Error::UNSUPPORTED; +} + Error HidlComposer::getClientTargetProperty( Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) { IComposerClient::ClientTargetProperty property; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 921add530a..8280af271e 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -343,6 +343,11 @@ public: AidlTransform* outDisplayOrientation) override; void onHotplugConnect(Display) override; void onHotplugDisconnect(Display) override; + Error getHdrConversionCapabilities( + std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>*) + override; + Error setHdrConversionStrategy( + aidl::android::hardware::graphics::common::HdrConversionStrategy) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp index c2109b3aa5..8629671214 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp @@ -177,7 +177,7 @@ void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId, } mStalledTransactions.push_back(transactionId); - listener->onTransactionQueueStalled(reason); + listener->onTransactionQueueStalled(String8(reason.c_str())); } void TransactionHandler::removeFromStalledTransactions(uint64_t id) { diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h index 475ff1b1dc..a06b870549 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.h +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h @@ -29,7 +29,6 @@ namespace android { class TestableSurfaceFlinger; -using gui::IListenerHash; namespace surfaceflinger::frontend { class TransactionHandler { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 1f159ae41a..de9ea0420c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2604,12 +2604,9 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l return; } ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); - std::optional<os::ParcelFileDescriptor> fenceFd; - if (releaseFence) { - fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get()))); - } - listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd, - static_cast<int32_t>(currentMaxAcquiredBufferCount)); + listener->onReleaseBuffer({buffer->getId(), framenumber}, + releaseFence ? releaseFence : Fence::NO_FENCE, + currentMaxAcquiredBufferCount); } void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 7aa7e17b3b..0ade4679a3 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -320,7 +320,12 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { const auto width = std::min({kMaxWidth, viewport.width, viewport.height}); const auto height = 2 * width; Rect frame((5 * width) >> 4, height >> 5); - frame.offsetBy(width >> 5, height >> 4); + + if (!mFeatures.test(Features::ShowInMiddle)) { + frame.offsetBy(width >> 5, height >> 4); + } else { + frame.offsetBy(width >> 1, height >> 4); + } createTransaction(mSurfaceControl->get()) .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index d6f828f7f1..b68a88c928 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -54,6 +54,7 @@ public: enum class Features { Spinner = 1 << 0, RenderRate = 1 << 1, + ShowInMiddle = 1 << 2, }; RefreshRateOverlay(FpsRange, ftl::Flags<Features>); diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp deleted file mode 100644 index 4af1f5c65f..0000000000 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2018 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 - -#include "DispSyncSource.h" - -#include <android-base/stringprintf.h> -#include <utils/Trace.h> -#include <mutex> - -#include "EventThread.h" -#include "VSyncTracker.h" -#include "VsyncController.h" - -namespace android::scheduler { -using base::StringAppendF; -using namespace std::chrono_literals; - -class CallbackRepeater { -public: - CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name, - std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, - std::chrono::nanoseconds notBefore) - : mName(name), - mCallback(cb), - mRegistration(dispatch, - std::bind(&CallbackRepeater::callback, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - mName), - mStarted(false), - mWorkDuration(workDuration), - mReadyDuration(readyDuration), - mLastCallTime(notBefore) {} - - ~CallbackRepeater() { - std::lock_guard lock(mMutex); - mRegistration.cancel(); - } - - void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { - std::lock_guard lock(mMutex); - mStarted = true; - mWorkDuration = workDuration; - mReadyDuration = readyDuration; - - auto const scheduleResult = - mRegistration.schedule({.workDuration = mWorkDuration.count(), - .readyDuration = mReadyDuration.count(), - .earliestVsync = mLastCallTime.count()}); - LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback"); - } - - void stop() { - std::lock_guard lock(mMutex); - LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped"); - mStarted = false; - mRegistration.cancel(); - } - - void dump(std::string& result) const { - std::lock_guard lock(mMutex); - const auto relativeLastCallTime = - mLastCallTime - std::chrono::steady_clock::now().time_since_epoch(); - StringAppendF(&result, "\t%s: ", mName.c_str()); - StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ", - mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f); - StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f, - mStarted ? "running" : "stopped"); - } - -private: - void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { - { - std::lock_guard lock(mMutex); - mLastCallTime = std::chrono::nanoseconds(vsyncTime); - } - - mCallback(vsyncTime, wakeupTime, readyTime); - - { - std::lock_guard lock(mMutex); - if (!mStarted) { - return; - } - auto const scheduleResult = - mRegistration.schedule({.workDuration = mWorkDuration.count(), - .readyDuration = mReadyDuration.count(), - .earliestVsync = vsyncTime}); - LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback"); - } - } - - const std::string mName; - scheduler::VSyncDispatch::Callback mCallback; - - mutable std::mutex mMutex; - VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex); - bool mStarted GUARDED_BY(mMutex) = false; - std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns; - std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns; - std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns; -}; - -DispSyncSource::DispSyncSource(VSyncDispatch& vSyncDispatch, VSyncTracker& vSyncTracker, - std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration, bool traceVsync, - const char* name) - : mName(name), - mValue(base::StringPrintf("VSYNC-%s", name), 0), - mTraceVsync(traceVsync), - mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), - mVSyncTracker(vSyncTracker), - mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration), - mReadyDuration(readyDuration) { - mCallbackRepeater = - std::make_unique<CallbackRepeater>(vSyncDispatch, - std::bind(&DispSyncSource::onVsyncCallback, this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3), - name, workDuration, readyDuration, - std::chrono::steady_clock::now().time_since_epoch()); -} - -DispSyncSource::~DispSyncSource() = default; - -void DispSyncSource::setVSyncEnabled(bool enable) { - std::lock_guard lock(mVsyncMutex); - if (enable) { - mCallbackRepeater->start(mWorkDuration, mReadyDuration); - // ATRACE_INT(mVsyncOnLabel.c_str(), 1); - } else { - mCallbackRepeater->stop(); - // ATRACE_INT(mVsyncOnLabel.c_str(), 0); - } - mEnabled = enable; -} - -void DispSyncSource::setCallback(VSyncSource::Callback* callback) { - std::lock_guard lock(mCallbackMutex); - mCallback = callback; -} - -void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration) { - std::lock_guard lock(mVsyncMutex); - mWorkDuration = workDuration; - mReadyDuration = readyDuration; - - // If we're not enabled, we don't need to mess with the listeners - if (!mEnabled) { - return; - } - - mCallbackRepeater->start(mWorkDuration, mReadyDuration); -} - -void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, - nsecs_t readyTime) { - VSyncSource::Callback* callback; - { - std::lock_guard lock(mCallbackMutex); - callback = mCallback; - } - - if (mTraceVsync) { - mValue = (mValue + 1) % 2; - } - - if (callback != nullptr) { - callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime}); - } -} - -VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const { - std::lock_guard lock(mVsyncMutex); - nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom( - systemTime() + mWorkDuration.get().count() + mReadyDuration.count()); - nsecs_t deadline = expectedPresentationTime - mReadyDuration.count(); - return {expectedPresentationTime, deadline}; -} - -void DispSyncSource::dump(std::string& result) const { - std::lock_guard lock(mVsyncMutex); - StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled"); -} - -} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h deleted file mode 100644 index edcd3ac709..0000000000 --- a/services/surfaceflinger/Scheduler/DispSyncSource.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2018 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 <mutex> -#include <string> - -#include "EventThread.h" -#include "TracedOrdinal.h" -#include "VSyncDispatch.h" - -namespace android::scheduler { -class CallbackRepeater; -class VSyncTracker; - -class DispSyncSource final : public VSyncSource { -public: - DispSyncSource(VSyncDispatch& vSyncDispatch, VSyncTracker& vSyncTracker, - std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, - bool traceVsync, const char* name); - - ~DispSyncSource() override; - - // The following methods are implementation of VSyncSource. - const char* getName() const override { return mName; } - void setVSyncEnabled(bool enable) override; - void setCallback(VSyncSource::Callback* callback) override; - void setDuration(std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration) override; - VSyncData getLatestVSyncData() const override; - - void dump(std::string&) const override; - -private: - void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime); - - const char* const mName; - TracedOrdinal<int> mValue; - - const bool mTraceVsync; - const std::string mVsyncOnLabel; - - const VSyncTracker& mVSyncTracker; - - std::unique_ptr<CallbackRepeater> mCallbackRepeater; - - std::mutex mCallbackMutex; - VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; - - mutable std::mutex mVsyncMutex; - TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex); - std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex); - bool mEnabled GUARDED_BY(mVsyncMutex) = false; -}; - -} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 008d8c4461..a902a8ebde 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -28,6 +28,7 @@ #include <cstdint> #include <optional> #include <type_traits> +#include <utility> #include <android-base/stringprintf.h> @@ -43,6 +44,8 @@ #include "DisplayHardware/DisplayMode.h" #include "FrameTimeline.h" +#include "VSyncDispatch.h" +#include "VSyncTracker.h" #include "EventThread.h" @@ -235,20 +238,29 @@ EventThread::~EventThread() = default; namespace impl { -EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource, +EventThread::EventThread(const char* name, scheduler::VsyncSchedule& vsyncSchedule, android::frametimeline::TokenManager* tokenManager, ThrottleVsyncCallback throttleVsyncCallback, - GetVsyncPeriodFunction getVsyncPeriodFunction) - : mVSyncSource(std::move(vsyncSource)), + GetVsyncPeriodFunction getVsyncPeriodFunction, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) + : mThreadName(name), + mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0), + mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration), + mReadyDuration(readyDuration), + mVsyncSchedule(vsyncSchedule), + mVsyncRegistration( + vsyncSchedule.getDispatch(), + [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { + onVsync(vsyncTime, wakeupTime, readyTime); + }, + name), mTokenManager(tokenManager), mThrottleVsyncCallback(std::move(throttleVsyncCallback)), - mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)), - mThreadName(mVSyncSource->getName()) { + mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) { LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr, "getVsyncPeriodFunction must not be null"); - mVSyncSource->setCallback(this); - mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS { std::unique_lock<std::mutex> lock(mMutex); threadMain(lock); @@ -270,8 +282,6 @@ EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource, } EventThread::~EventThread() { - mVSyncSource->setCallback(nullptr); - { std::lock_guard<std::mutex> lock(mMutex); mState = State::Quit; @@ -283,7 +293,12 @@ EventThread::~EventThread() { void EventThread::setDuration(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { std::lock_guard<std::mutex> lock(mMutex); - mVSyncSource->setDuration(workDuration, readyDuration); + mWorkDuration = workDuration; + mReadyDuration = readyDuration; + + mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(), + .readyDuration = mReadyDuration.count(), + .earliestVsync = mLastVsyncCallbackTime.ns()}); } sp<EventThreadConnection> EventThread::createEventConnection( @@ -358,13 +373,14 @@ VsyncEventData EventThread::getLatestVsyncEventData( VsyncEventData vsyncEventData; nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid); vsyncEventData.frameInterval = frameInterval; - VSyncSource::VSyncData vsyncData; - { + const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> { std::lock_guard<std::mutex> lock(mMutex); - vsyncData = mVSyncSource->getLatestVSyncData(); - } + const auto vsyncTime = mVsyncSchedule.getTracker().nextAnticipatedVSyncTimeFrom( + systemTime() + mWorkDuration.get().count() + mReadyDuration.count()); + return {vsyncTime, vsyncTime - mReadyDuration.count()}; + }(); generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC), - vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp); + presentTime, deadline); return vsyncEventData; } @@ -388,13 +404,14 @@ void EventThread::onScreenAcquired() { mCondition.notify_all(); } -void EventThread::onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) { +void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { std::lock_guard<std::mutex> lock(mMutex); + mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime); LOG_FATAL_IF(!mVSyncState); - mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count, - vsyncData.expectedPresentationTime, - vsyncData.deadlineTimestamp)); + mVsyncTracer = (mVsyncTracer + 1) % 2; + mPendingEvents.push_back(makeVSync(mVSyncState->displayId, wakeupTime, ++mVSyncState->count, + vsyncTime, readyTime)); mCondition.notify_all(); } @@ -456,12 +473,12 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { auto it = mDisplayEventConnections.begin(); while (it != mDisplayEventConnections.end()) { if (const auto connection = it->promote()) { - vsyncRequested |= connection->vsyncRequest != VSyncRequest::None; - if (event && shouldConsumeEvent(*event, connection)) { consumers.push_back(connection); } + vsyncRequested |= connection->vsyncRequest != VSyncRequest::None; + ++it; } else { it = mDisplayEventConnections.erase(it); @@ -473,25 +490,24 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { consumers.clear(); } - State nextState; if (mVSyncState && vsyncRequested) { - nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; + mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; } else { ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected"); - nextState = State::Idle; + mState = State::Idle; } - if (mState != nextState) { - if (mState == State::VSync) { - mVSyncSource->setVSyncEnabled(false); - } else if (nextState == State::VSync) { - mVSyncSource->setVSyncEnabled(true); - } - - mState = nextState; + if (mState == State::VSync) { + const auto scheduleResult = + mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), + .readyDuration = mReadyDuration.count(), + .earliestVsync = mLastVsyncCallbackTime.ns()}); + LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback"); + } else { + mVsyncRegistration.cancel(); } - if (event) { + if (!mPendingEvents.empty()) { continue; } @@ -506,15 +522,6 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) { if (mState == State::VSync) { ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName); - std::string debugInfo = "VsyncSource debug info:\n"; - mVSyncSource->dump(debugInfo); - // Log the debug info line-by-line to avoid logcat overflow - auto pos = debugInfo.find('\n'); - while (pos != std::string::npos) { - ALOGW("%s", debugInfo.substr(0, pos).c_str()); - debugInfo = debugInfo.substr(pos + 1); - pos = debugInfo.find('\n'); - } } LOG_FATAL_IF(!mVSyncState); @@ -527,6 +534,8 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { } } } + // cancel any pending vsync event before exiting + mVsyncRegistration.cancel(); } bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, @@ -657,6 +666,12 @@ void EventThread::dump(std::string& result) const { StringAppendF(&result, "none\n"); } + const auto relativeLastCallTime = + ticks<std::milli, float>(mLastVsyncCallbackTime - TimePoint::now()); + StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ", + mWorkDuration.get().count() / 1e6f, mReadyDuration.count() / 1e6f); + StringAppendF(&result, "%.2fms relative to now\n", relativeLastCallTime); + StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size()); for (const auto& event : mPendingEvents) { StringAppendF(&result, " %s\n", toString(event).c_str()); diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 43c359833e..ab9085e44a 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -33,6 +33,9 @@ #include <vector> #include "DisplayHardware/DisplayMode.h" +#include "TracedOrdinal.h" +#include "VSyncDispatch.h" +#include "VsyncSchedule.h" // --------------------------------------------------------------------------- namespace android { @@ -64,32 +67,6 @@ enum class VSyncRequest { // Subsequent values are periods. }; -class VSyncSource { -public: - class VSyncData { - public: - nsecs_t expectedPresentationTime; - nsecs_t deadlineTimestamp; - }; - - class Callback { - public: - virtual ~Callback() {} - virtual void onVSyncEvent(nsecs_t when, VSyncData vsyncData) = 0; - }; - - virtual ~VSyncSource() {} - - virtual const char* getName() const = 0; - virtual void setVSyncEnabled(bool enable) = 0; - virtual void setCallback(Callback* callback) = 0; - virtual void setDuration(std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration) = 0; - virtual VSyncData getLatestVSyncData() const = 0; - - virtual void dump(std::string& result) const = 0; -}; - class EventThreadConnection : public gui::BnDisplayEventConnection { public: EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback, @@ -160,13 +137,14 @@ public: namespace impl { -class EventThread : public android::EventThread, private VSyncSource::Callback { +class EventThread : public android::EventThread { public: using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>; using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>; - EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, ThrottleVsyncCallback, - GetVsyncPeriodFunction); + EventThread(const char* name, scheduler::VsyncSchedule&, frametimeline::TokenManager*, + ThrottleVsyncCallback, GetVsyncPeriodFunction, + std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); ~EventThread(); sp<EventThreadConnection> createEventConnection( @@ -213,8 +191,7 @@ private: void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection) REQUIRES(mMutex); - // Implements VSyncSource::Callback - void onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) override; + void onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime); int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp, nsecs_t expectedPresentationTime) const; @@ -222,12 +199,17 @@ private: nsecs_t timestamp, nsecs_t preferredExpectedPresentationTime, nsecs_t preferredDeadlineTimestamp) const; - const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex); + const char* const mThreadName; + TracedOrdinal<int> mVsyncTracer; + TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex); + std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex); + scheduler::VsyncSchedule& mVsyncSchedule; + TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now(); + scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; const ThrottleVsyncCallback mThrottleVsyncCallback; const GetVsyncPeriodFunction mGetVsyncPeriodFunction; - const char* const mThreadName; std::thread mThread; mutable std::mutex mMutex; diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index ae10ff4989..e827c12f7c 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -78,7 +78,8 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, frametimeline::TokenManager& tokenManager, std::chrono::nanoseconds workDuration) { - setDuration(workDuration); + std::lock_guard lock(mVsync.mutex); + mVsync.workDuration = workDuration; mVsync.tokenManager = &tokenManager; mVsync.registration = std::make_unique< scheduler::VSyncCallbackRegistration>(dispatch, @@ -89,16 +90,20 @@ void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, "sf"); } +void MessageQueue::destroyVsync() { + std::lock_guard lock(mVsync.mutex); + mVsync.tokenManager = nullptr; + mVsync.registration.reset(); +} + void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { ATRACE_CALL(); std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; - if (mVsync.scheduledFrameTime) { - mVsync.scheduledFrameTime = - mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), - .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.ns()}); - } + mVsync.scheduledFrameTime = + mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(), + .readyDuration = 0, + .earliestVsync = mVsync.lastCallbackTime.ns()}); } void MessageQueue::waitMessage() { diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 04de492479..71f8645828 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -75,6 +75,7 @@ public: virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, std::chrono::nanoseconds workDuration) = 0; + virtual void destroyVsync() = 0; virtual void setDuration(std::chrono::nanoseconds workDuration) = 0; virtual void waitMessage() = 0; virtual void postMessage(sp<MessageHandler>&&) = 0; @@ -138,6 +139,7 @@ public: void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, std::chrono::nanoseconds workDuration) override; + void destroyVsync() override; void setDuration(std::chrono::nanoseconds workDuration) override; void waitMessage() override; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 74a81b7258..bc465ce932 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -42,7 +42,6 @@ #include <numeric> #include "../Layer.h" -#include "DispSyncSource.h" #include "Display/DisplayMap.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" @@ -65,6 +64,11 @@ Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, Feat : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {} Scheduler::~Scheduler() { + // MessageQueue depends on VsyncSchedule, so first destroy it. + // Otherwise, MessageQueue will get destroyed after Scheduler's dtor, + // which will cause a use-after-free issue. + Impl::destroyVsync(); + // Stop timers and wait for their threads to exit. mDisplayPowerTimer.reset(); mTouchTimer.reset(); @@ -142,14 +146,6 @@ void Scheduler::createVsyncSchedule(FeatureFlags features) { mVsyncSchedule.emplace(features); } -std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( - const char* name, std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration, bool traceVsync) { - return std::make_unique<scheduler::DispSyncSource>(mVsyncSchedule->getDispatch(), - mVsyncSchedule->getTracker(), workDuration, - readyDuration, traceVsync, name); -} - std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { const bool supportsFrameRateOverrideByContent = leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); @@ -194,12 +190,12 @@ ConnectionHandle Scheduler::createConnection(const char* connectionName, frametimeline::TokenManager* tokenManager, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { - auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration); auto throttleVsync = makeThrottleVsyncCallback(); auto getVsyncPeriod = makeGetVsyncPeriodFunction(); - auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager, - std::move(throttleVsync), - std::move(getVsyncPeriod)); + auto eventThread = + std::make_unique<impl::EventThread>(connectionName, *mVsyncSchedule, tokenManager, + std::move(throttleVsync), std::move(getVsyncPeriod), + workDuration, readyDuration); return createConnection(std::move(eventThread)); } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index f1894262ef..20221d1907 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -223,11 +223,6 @@ public: size_t getEventThreadConnectionCount(ConnectionHandle handle); - std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, - std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration, - bool traceVsync = true); - // Stores the preferred refresh rate that an app should run at. // FrameRateOverride.refreshRateHz == 0 means no preference. void setPreferredRefreshRateForUid(FrameRateOverride); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index 2bfe204a33..95201314a4 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -126,6 +126,17 @@ public: */ virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0; + /* + * Update the timing information for a scheduled callback. + * If the callback is not scheduled, then this function does nothing. + * + * \param [in] token The callback to schedule. + * \param [in] scheduleTiming The timing information for this schedule call + * \return The expected callback time if a callback was scheduled. + * std::nullopt if the callback is not registered. + */ + virtual ScheduleResult update(CallbackToken token, ScheduleTiming scheduleTiming) = 0; + /* Cancels a scheduled callback, if possible. * * \param [in] token The callback to cancel. @@ -159,6 +170,9 @@ public: // See documentation for VSyncDispatch::schedule. ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming); + // See documentation for VSyncDispatch::update. + ScheduleResult update(VSyncDispatch::ScheduleTiming scheduleTiming); + // See documentation for VSyncDispatch::cancel. CancelResult cancel(); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index cc9f7cfaaa..73d52cf986 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -347,38 +347,54 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, ScheduleTiming scheduleTiming) { - ScheduleResult result; - { - std::lock_guard lock(mMutex); - - auto it = mCallbacks.find(token); - if (it == mCallbacks.end()) { - return result; - } - auto& callback = it->second; - auto const now = mTimeKeeper->now(); + std::lock_guard lock(mMutex); + return scheduleLocked(token, scheduleTiming); +} - /* If the timer thread will run soon, we'll apply this work update via the callback - * timer recalculation to avoid cancelling a callback that is about to fire. */ - auto const rearmImminent = now > mIntendedWakeupTime; - if (CC_UNLIKELY(rearmImminent)) { - callback->addPendingWorkloadUpdate(scheduleTiming); - return getExpectedCallbackTime(mTracker, now, scheduleTiming); - } +ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token, + ScheduleTiming scheduleTiming) { + auto it = mCallbacks.find(token); + if (it == mCallbacks.end()) { + return {}; + } + auto& callback = it->second; + auto const now = mTimeKeeper->now(); + + /* If the timer thread will run soon, we'll apply this work update via the callback + * timer recalculation to avoid cancelling a callback that is about to fire. */ + auto const rearmImminent = now > mIntendedWakeupTime; + if (CC_UNLIKELY(rearmImminent)) { + callback->addPendingWorkloadUpdate(scheduleTiming); + return getExpectedCallbackTime(mTracker, now, scheduleTiming); + } - result = callback->schedule(scheduleTiming, mTracker, now); - if (!result.has_value()) { - return result; - } + const ScheduleResult result = callback->schedule(scheduleTiming, mTracker, now); + if (!result.has_value()) { + return {}; + } - if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) { - rearmTimerSkippingUpdateFor(now, it); - } + if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) { + rearmTimerSkippingUpdateFor(now, it); } return result; } +ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) { + std::lock_guard lock(mMutex); + const auto it = mCallbacks.find(token); + if (it == mCallbacks.end()) { + return {}; + } + + auto& callback = it->second; + if (!callback->targetVsync().has_value()) { + return {}; + } + + return scheduleLocked(token, scheduleTiming); +} + CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) { std::lock_guard lock(mMutex); @@ -451,6 +467,13 @@ ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming return mDispatch.get().schedule(mToken, scheduleTiming); } +ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) { + if (!mValidToken) { + return std::nullopt; + } + return mDispatch.get().update(mToken, scheduleTiming); +} + CancelResult VSyncCallbackRegistration::cancel() { if (!mValidToken) { return CancelResult::Error; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index 4f2f87a7d2..c3af136d66 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -127,6 +127,7 @@ public: CallbackToken registerCallback(Callback, std::string callbackName) final; void unregisterCallback(CallbackToken) final; ScheduleResult schedule(CallbackToken, ScheduleTiming) final; + ScheduleResult update(CallbackToken, ScheduleTiming) final; CancelResult cancel(CallbackToken) final; void dump(std::string&) const final; @@ -143,6 +144,7 @@ private: void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); + ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex); static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 8c17409b02..173b1d00cf 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -22,6 +22,14 @@ #include <scheduler/Features.h> #include <scheduler/Time.h> +namespace android { +class EventThreadTest; +} + +namespace android::fuzz { +class SchedulerFuzzer; +} + namespace android::scheduler { // TODO(b/185535769): Rename classes, and remove aliases. @@ -54,6 +62,8 @@ public: private: friend class TestableScheduler; + friend class android::EventThreadTest; + friend class android::fuzz::SchedulerFuzzer; using TrackerPtr = std::unique_ptr<VsyncTracker>; using DispatchPtr = std::unique_ptr<VsyncDispatch>; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ba5c4d7016..2d8b9c1672 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -461,6 +461,8 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0); mRefreshRateOverlayRenderRate = property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0); + mRefreshRateOverlayShowInMiddle = + property_get_bool("debug.sf.show_refresh_rate_overlay_in_middle", 0); if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) { mTransactionTracing.emplace(); @@ -1538,6 +1540,80 @@ status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) { return future.get(); } +status_t SurfaceFlinger::getHdrConversionCapabilities( + std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) const { + bool hdrOutputConversionSupport; + getHdrOutputConversionSupport(&hdrOutputConversionSupport); + if (hdrOutputConversionSupport == false) { + ALOGE("hdrOutputConversion is not supported by this device."); + return INVALID_OPERATION; + } + const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities(); + for (auto capability : aidlConversionCapability) { + gui::HdrConversionCapability tempCapability; + tempCapability.sourceType = static_cast<int>(capability.sourceType.hdr); + tempCapability.outputType = static_cast<int>(capability.outputType->hdr); + tempCapability.addsLatency = capability.addsLatency; + hdrConversionCapabilities->push_back(tempCapability); + } + return NO_ERROR; +} + +status_t SurfaceFlinger::setHdrConversionStrategy( + const gui::HdrConversionStrategy& hdrConversionStrategy) { + bool hdrOutputConversionSupport; + getHdrOutputConversionSupport(&hdrOutputConversionSupport); + if (hdrOutputConversionSupport == false) { + ALOGE("hdrOutputConversion is not supported by this device."); + return INVALID_OPERATION; + } + auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { + using AidlHdrConversionStrategy = + aidl::android::hardware::graphics::common::HdrConversionStrategy; + using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag; + AidlHdrConversionStrategy aidlConversionStrategy; + switch (hdrConversionStrategy.getTag()) { + case GuiHdrConversionStrategyTag::passthrough: { + aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::passthrough>( + hdrConversionStrategy.get<GuiHdrConversionStrategyTag::passthrough>()); + return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy); + } + case GuiHdrConversionStrategyTag::autoAllowedHdrTypes: { + auto autoHdrTypes = + hdrConversionStrategy + .get<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>(); + std::vector<aidl::android::hardware::graphics::common::Hdr> aidlAutoHdrTypes; + for (auto type : autoHdrTypes) { + aidlAutoHdrTypes.push_back( + static_cast<aidl::android::hardware::graphics::common::Hdr>(type)); + } + aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::autoAllowedHdrTypes>( + aidlAutoHdrTypes); + return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy); + } + case GuiHdrConversionStrategyTag::forceHdrConversion: { + auto forceHdrConversion = + hdrConversionStrategy + .get<GuiHdrConversionStrategyTag::forceHdrConversion>(); + aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::forceHdrConversion>( + static_cast<aidl::android::hardware::graphics::common::Hdr>( + forceHdrConversion)); + return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy); + } + } + }); + return future.get(); +} + +status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const { + auto future = mScheduler->schedule([this] { + return getHwComposer().hasCapability(Capability::HDR_OUTPUT_CONVERSION_CONFIG); + }); + + *outSupport = future.get(); + return NO_ERROR; +} + void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { @@ -6995,7 +7071,8 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) { if (const auto device = getDisplayDeviceLocked(id)) { device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner, - mRefreshRateOverlayRenderRate); + mRefreshRateOverlayRenderRate, + mRefreshRateOverlayShowInMiddle); } } } @@ -7157,7 +7234,8 @@ std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextur layerName, static_cast<uint32_t>(mMaxRenderTargetSize)); ALOGD("%s", errorMessage.c_str()); if (bufferData.releaseBufferListener) { - bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage); + bufferData.releaseBufferListener->onTransactionQueueStalled( + String8(errorMessage.c_str())); } return nullptr; } @@ -7175,7 +7253,7 @@ std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextur if (bufferData.releaseBufferListener) { bufferData.releaseBufferListener->onTransactionQueueStalled( - "Buffer processing hung due to full buffer cache"); + String8("Buffer processing hung due to full buffer cache")); } } @@ -7581,6 +7659,32 @@ binder::Status SurfaceComposerAIDL::getBootDisplayModeSupport(bool* outMode) { return binderStatusFromStatusT(status); } +binder::Status SurfaceComposerAIDL::getHdrConversionCapabilities( + std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->getHdrConversionCapabilities(hdrConversionCapabilities); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::setHdrConversionStrategy( + const gui::HdrConversionStrategy& hdrConversionStrategy) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->setHdrConversionStrategy(hdrConversionStrategy); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::getHdrOutputConversionSupport(bool* outMode) { + status_t status = checkAccessPermission(); + if (status == OK) { + status = mFlinger->getHdrOutputConversionSupport(outMode); + } + return binderStatusFromStatusT(status); +} + binder::Status SurfaceComposerAIDL::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) { status_t status = checkAccessPermission(); if (status != OK) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 37aa3d50a9..33f0402094 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -26,7 +26,6 @@ #include <android/gui/DisplayStatInfo.h> #include <android/gui/DisplayState.h> #include <android/gui/ISurfaceComposerClient.h> -#include <android/gui/ITransactionCompletedListener.h> #include <cutils/atomic.h> #include <cutils/compiler.h> #include <ftl/future.h> @@ -35,8 +34,8 @@ #include <gui/CompositorTiming.h> #include <gui/FrameTimestamps.h> #include <gui/ISurfaceComposer.h> +#include <gui/ITransactionCompletedListener.h> #include <gui/LayerDebugInfo.h> - #include <gui/LayerState.h> #include <layerproto/LayerProtoHeader.h> #include <math/mat4.h> @@ -127,9 +126,7 @@ using frontend::TransactionHandler; using gui::CaptureArgs; using gui::DisplayCaptureArgs; using gui::IRegionSamplingListener; -using gui::ITransactionCompletedListener; using gui::LayerCaptureArgs; - using gui::ScreenCaptureResults; namespace frametimeline { @@ -534,6 +531,10 @@ private: status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId); status_t getOverlaySupport(gui::OverlayProperties* outProperties) const; status_t clearBootDisplayMode(const sp<IBinder>& displayToken); + status_t getHdrConversionCapabilities( + std::vector<gui::HdrConversionCapability>* hdrConversionCapaabilities) const; + status_t setHdrConversionStrategy(const gui::HdrConversionStrategy& hdrConversionStrategy); + status_t getHdrOutputConversionSupport(bool* outSupport) const; void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on); void setGameContentType(const sp<IBinder>& displayToken, bool on); void setPowerMode(const sp<IBinder>& displayToken, int mode); @@ -653,6 +654,8 @@ private: bool mRefreshRateOverlaySpinner = false; // Show render rate with refresh rate overlay bool mRefreshRateOverlayRenderRate = false; + // Show render rate overlay offseted to the middle of the screen (e.g. for circular displays) + bool mRefreshRateOverlayShowInMiddle = false; void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); @@ -1432,6 +1435,11 @@ public: binder::Status clearBootDisplayMode(const sp<IBinder>& display) override; binder::Status getBootDisplayModeSupport(bool* outMode) override; binder::Status getOverlaySupport(gui::OverlayProperties* outProperties) override; + binder::Status getHdrConversionCapabilities( + std::vector<gui::HdrConversionCapability>*) override; + binder::Status setHdrConversionStrategy( + const gui::HdrConversionStrategy& hdrConversionStrategy) override; + binder::Status getHdrOutputConversionSupport(bool* outSupport) override; binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override; binder::Status setGameContentType(const sp<IBinder>& display, bool on) override; binder::Status captureDisplay(const DisplayCaptureArgs&, diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index c09bcce067..61ff9bce98 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -26,27 +26,14 @@ #include <unordered_set> #include <android-base/thread_annotations.h> -#include <android/gui/ITransactionCompletedListener.h> - #include <binder/IBinder.h> -#include <gui/ListenerStats.h> -#include <gui/ReleaseCallbackId.h> -#include <renderengine/RenderEngine.h> +#include <ftl/future.h> +#include <gui/ITransactionCompletedListener.h> #include <ui/Fence.h> #include <ui/FenceResult.h> namespace android { -using gui::CallbackId; -using gui::FrameEventHistoryStats; -using gui::IListenerHash; -using gui::ITransactionCompletedListener; -using gui::JankData; -using gui::ListenerCallbacks; -using gui::ListenerStats; -using gui::ReleaseCallbackId; -using gui::TransactionStats; - class CallbackHandle : public RefBase { public: CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids, diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 7959e52fba..0af3efaa02 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -21,13 +21,15 @@ #include <scheduler/PresentLatencyTracker.h> -#include "Scheduler/DispSyncSource.h" #include "Scheduler/OneShotTimer.h" #include "Scheduler/RefreshRateSelector.h" #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" +#include "mock/MockVSyncDispatch.h" +#include "mock/MockVSyncTracker.h" + #include "surfaceflinger_fuzzers_utils.h" #include "surfaceflinger_scheduler_fuzzer.h" @@ -53,7 +55,7 @@ void dump(T* component, FuzzedDataProvider* fdp) { component->dump(res); } -class SchedulerFuzzer : private VSyncSource::Callback { +class SchedulerFuzzer { public: SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; void process(); @@ -66,7 +68,6 @@ private: void fuzzVSyncPredictor(); void fuzzVSyncReactor(); void fuzzLayerHistory(); - void fuzzDispSyncSource(); void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch); void fuzzVSyncDispatchTimerQueue(); void fuzzOneShotTimer(); @@ -75,8 +76,7 @@ private: FuzzedDataProvider mFdp; -protected: - void onVSyncEvent(nsecs_t /* when */, VSyncSource::VSyncData) {} + std::optional<scheduler::VsyncSchedule> mVsyncSchedule; }; PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { @@ -90,10 +90,14 @@ PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { } void SchedulerFuzzer::fuzzEventThread() { + mVsyncSchedule.emplace(scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(), + std::make_unique<mock::VSyncDispatch>(), + nullptr)); const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); }; std::unique_ptr<android::impl::EventThread> thread = std::make_unique< - android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr, - nullptr, getVsyncPeriod); + android::impl::EventThread>("fuzzer", *mVsyncSchedule, nullptr, nullptr, getVsyncPeriod, + (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), + (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool()); sp<EventThreadConnection> connection = @@ -110,24 +114,6 @@ void SchedulerFuzzer::fuzzEventThread() { dump<android::impl::EventThread>(thread.get(), &mFdp); } -void SchedulerFuzzer::fuzzDispSyncSource() { - std::unique_ptr<FuzzImplVSyncDispatch> vSyncDispatch = - std::make_unique<FuzzImplVSyncDispatch>(); - std::unique_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_unique<FuzzImplVSyncTracker>(); - std::unique_ptr<scheduler::DispSyncSource> dispSyncSource = std::make_unique< - scheduler::DispSyncSource>(*vSyncDispatch, *vSyncTracker, - (std::chrono::nanoseconds) - mFdp.ConsumeIntegral<uint64_t>() /*workDuration*/, - (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>() - /*readyDuration*/, - mFdp.ConsumeBool(), - mFdp.ConsumeRandomLengthString(kRandomStringLength).c_str()); - dispSyncSource->setVSyncEnabled(true); - dispSyncSource->setCallback(this); - dispSyncSource->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), 0ns); - dump<scheduler::DispSyncSource>(dispSyncSource.get(), &mFdp); -} - void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) { scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback( [&](auto, auto, auto) { @@ -417,7 +403,6 @@ void SchedulerFuzzer::process() { fuzzVSyncPredictor(); fuzzVSyncReactor(); fuzzLayerHistory(); - fuzzDispSyncSource(); fuzzEventThread(); fuzzVSyncDispatchTimerQueue(); fuzzOneShotTimer(); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 2bc5b4676f..713b71042a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -78,22 +78,6 @@ public: sp<Layer> createClone() override { return nullptr; } }; -class FuzzImplVSyncSource : public VSyncSource { -public: - const char* getName() const override { return "fuzz"; } - - void setVSyncEnabled(bool /* enable */) override {} - - void setCallback(Callback* /* callback */) override {} - - void setDuration(std::chrono::nanoseconds /* workDuration */, - std::chrono::nanoseconds /* readyDuration */) override {} - - VSyncData getLatestVSyncData() const override { return {}; } - - void dump(std::string& /* result */) const override {} -}; - class FuzzImplVSyncTracker : public scheduler::VSyncTracker { public: FuzzImplVSyncTracker(nsecs_t period) { mPeriod = period; } diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 6d1b3fe5a8..824899b422 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -36,6 +36,7 @@ filegroup { "mock/MockNativeWindowSurface.cpp", "mock/MockTimeStats.cpp", "mock/MockVsyncController.cpp", + "mock/MockVSyncDispatch.cpp", "mock/MockVSyncTracker.cpp", "mock/system/window/MockNativeWindow.cpp", ], @@ -71,7 +72,6 @@ cc_test { "libsurfaceflinger_unittest_main.cpp", "AidlPowerHalWrapperTest.cpp", "CompositionTest.cpp", - "DispSyncSourceTest.cpp", "DisplayIdGeneratorTest.cpp", "DisplayTransactionTest.cpp", "DisplayDevice_GetBestColorModeTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp deleted file mode 100644 index 67ace1af83..0000000000 --- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright 2019 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. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" -#define LOG_NDEBUG 0 - -#include <inttypes.h> - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <log/log.h> - -#include "AsyncCallRecorder.h" -#include "Scheduler/DispSyncSource.h" -#include "Scheduler/VSyncDispatch.h" -#include "mock/MockVSyncTracker.h" - -namespace android { -namespace { - -using namespace std::chrono_literals; -using namespace testing; - -class MockVSyncDispatch : public scheduler::VSyncDispatch { -public: - MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override)); - MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override)); - MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override)); - MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken), (override)); - MOCK_METHOD(void, dump, (std::string&), (const, override)); - - MockVSyncDispatch() { - ON_CALL(*this, registerCallback) - .WillByDefault( - [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback, - std::string) { - CallbackToken token(mNextToken); - mNextToken++; - - mCallbacks.emplace(token, CallbackData(callback)); - ALOGD("registerCallback: %zu", token.value()); - return token; - }); - - ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) { - ALOGD("unregisterCallback: %zu", token.value()); - mCallbacks.erase(token); - }); - - ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) { - ALOGD("schedule: %zu", token.value()); - if (mCallbacks.count(token) == 0) { - ALOGD("schedule: callback %zu not registered", token.value()); - return scheduler::ScheduleResult{}; - } - - auto& callback = mCallbacks.at(token); - callback.scheduled = true; - callback.vsyncTime = timing.earliestVsync; - callback.targetWakeupTime = - timing.earliestVsync - timing.workDuration - timing.readyDuration; - ALOGD("schedule: callback %zu scheduled", token.value()); - return scheduler::ScheduleResult{callback.targetWakeupTime}; - }); - - ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) { - ALOGD("cancel: %zu", token.value()); - if (mCallbacks.count(token) == 0) { - ALOGD("cancel: callback %zu is not registered", token.value()); - return scheduler::CancelResult::Error; - } - - auto& callback = mCallbacks.at(token); - callback.scheduled = false; - ALOGD("cancel: callback %zu cancelled", token.value()); - return scheduler::CancelResult::Cancelled; - }); - } - - void triggerCallbacks() { - ALOGD("triggerCallbacks"); - for (auto& [token, callback] : mCallbacks) { - if (callback.scheduled) { - ALOGD("triggerCallbacks: callback %zu", token.value()); - callback.scheduled = false; - callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime); - } else { - ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value()); - } - } - } - -private: - struct CallbackData { - explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func) - : func(std::move(func)) {} - - std::function<void(nsecs_t, nsecs_t, nsecs_t)> func; - bool scheduled = false; - nsecs_t vsyncTime = 0; - nsecs_t targetWakeupTime = 0; - nsecs_t readyTime = 0; - }; - - std::unordered_map<CallbackToken, CallbackData> mCallbacks; - size_t mNextToken; -}; - -class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback { -protected: - DispSyncSourceTest(); - ~DispSyncSourceTest() override; - - void SetUp() override; - void createDispSyncSource(); - - void onVSyncEvent(nsecs_t when, VSyncSource::VSyncData) override; - - std::unique_ptr<MockVSyncDispatch> mVSyncDispatch; - std::unique_ptr<mock::VSyncTracker> mVSyncTracker; - std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource; - - AsyncCallRecorder<void (*)(nsecs_t, VSyncSource::VSyncData)> mVSyncEventCallRecorder; - - static constexpr std::chrono::nanoseconds mWorkDuration = 20ms; - static constexpr std::chrono::nanoseconds mReadyDuration = 10ms; - static constexpr int mIterations = 100; - const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398}; - const std::string mName = "DispSyncSourceTest"; -}; - -DispSyncSourceTest::DispSyncSourceTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); -} - -DispSyncSourceTest::~DispSyncSourceTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); -} - -void DispSyncSourceTest::SetUp() { - mVSyncDispatch = std::make_unique<MockVSyncDispatch>(); - mVSyncTracker = std::make_unique<mock::VSyncTracker>(); -} - -void DispSyncSourceTest::onVSyncEvent(nsecs_t when, VSyncSource::VSyncData vsyncData) { - ALOGD("onVSyncEvent: %" PRId64, when); - - mVSyncEventCallRecorder.recordCall(when, vsyncData); -} - -void DispSyncSourceTest::createDispSyncSource() { - mDispSyncSource = std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, *mVSyncTracker, - mWorkDuration, mReadyDuration, - true, mName.c_str()); - mDispSyncSource->setCallback(this); -} - -/* ------------------------------------------------------------------------ - * Test cases - */ - -TEST_F(DispSyncSourceTest, createDispSync) { - EXPECT_TRUE(mVSyncDispatch); -} - -TEST_F(DispSyncSourceTest, createDispSyncSource) { - InSequence seq; - EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken)) - .WillOnce(Return(scheduler::CancelResult::Cancelled)); - EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return()); - createDispSyncSource(); - - EXPECT_TRUE(mDispSyncSource); -} - -TEST_F(DispSyncSourceTest, noCallbackAfterInit) { - InSequence seq; - EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); - EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); - EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); - createDispSyncSource(); - - EXPECT_TRUE(mDispSyncSource); - - // DispSyncSource starts with Vsync disabled - mVSyncDispatch->triggerCallbacks(); - EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); -} - -TEST_F(DispSyncSourceTest, waitForCallbacks) { - InSequence seq; - EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); - EXPECT_CALL(*mVSyncDispatch, - schedule(_, Truly([&](auto timings) { - return timings.workDuration == mWorkDuration.count() && - timings.readyDuration == mReadyDuration.count(); - }))) - .Times(mIterations + 1); - EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); - EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); - createDispSyncSource(); - - EXPECT_TRUE(mDispSyncSource); - - mDispSyncSource->setVSyncEnabled(true); - for (int i = 0; i < mIterations; i++) { - mVSyncDispatch->triggerCallbacks(); - const auto callbackData = mVSyncEventCallRecorder.waitForCall(); - ASSERT_TRUE(callbackData.has_value()); - const auto [when, vsyncData] = callbackData.value(); - EXPECT_EQ(when, - vsyncData.expectedPresentationTime - mWorkDuration.count() - - mReadyDuration.count()); - } -} - -TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) { - InSequence seq; - EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); - EXPECT_CALL(*mVSyncDispatch, - schedule(_, Truly([&](auto timings) { - return timings.workDuration == mWorkDuration.count() && - timings.readyDuration == mReadyDuration.count(); - }))) - .Times(1); - - createDispSyncSource(); - - EXPECT_TRUE(mDispSyncSource); - - mDispSyncSource->setVSyncEnabled(true); - EXPECT_CALL(*mVSyncDispatch, - schedule(_, Truly([&](auto timings) { - return timings.workDuration == mWorkDuration.count() && - timings.readyDuration == mReadyDuration.count(); - }))) - .Times(mIterations); - for (int i = 0; i < mIterations; i++) { - mVSyncDispatch->triggerCallbacks(); - const auto callbackData = mVSyncEventCallRecorder.waitForCall(); - ASSERT_TRUE(callbackData.has_value()); - const auto [when, vsyncData] = callbackData.value(); - EXPECT_EQ(when, - vsyncData.expectedPresentationTime - mWorkDuration.count() - - mReadyDuration.count()); - } - - const auto newDuration = mWorkDuration / 2; - EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) { - return timings.workDuration == newDuration.count() && - timings.readyDuration == 0; - }))) - .Times(1); - mDispSyncSource->setDuration(newDuration, 0ns); - - EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) { - return timings.workDuration == newDuration.count() && - timings.readyDuration == 0; - }))) - .Times(mIterations); - for (int i = 0; i < mIterations; i++) { - mVSyncDispatch->triggerCallbacks(); - const auto callbackData = mVSyncEventCallRecorder.waitForCall(); - ASSERT_TRUE(callbackData.has_value()); - const auto [when, vsyncData] = callbackData.value(); - EXPECT_EQ(when, vsyncData.expectedPresentationTime - newDuration.count()); - } - - EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); - EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); -} - -TEST_F(DispSyncSourceTest, getLatestVsyncData) { - const nsecs_t now = systemTime(); - const nsecs_t expectedPresentationTime = - now + mWorkDuration.count() + mReadyDuration.count() + 1; - EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)) - .WillOnce(Return(expectedPresentationTime)); - { - InSequence seq; - EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); - EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); - EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); - } - - createDispSyncSource(); - EXPECT_TRUE(mDispSyncSource); - - const auto vsyncData = mDispSyncSource->getLatestVSyncData(); - ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime); - EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count()); -} - -} // namespace -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp index 225ad163d9..ac5e9274bb 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp @@ -96,5 +96,23 @@ TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithCompositeShortCircu EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness); } +TEST_F(SetDisplayBrightnessTest, firstDisplayBrightnessWithComposite) { + ftl::FakeGuard guard(kMainThreadContext); + sp<DisplayDevice> displayDevice = getDisplayDevice(); + + EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness()); + + constexpr float kDisplayBrightness = -1.0f; + displayDevice->stageBrightness(kDisplayBrightness); + + EXPECT_EQ(-1.0f, displayDevice->getStagedBrightness()); + + displayDevice->persistBrightness(true); + + EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness()); + EXPECT_EQ(kDisplayBrightness, + displayDevice->getCompositionDisplay()->getState().displayBrightness); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index dd87f9dadf..b3aba377ee 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -30,12 +30,16 @@ #include "DisplayHardware/DisplayMode.h" #include "FrameTimeline.h" #include "Scheduler/EventThread.h" +#include "mock/MockVSyncDispatch.h" +#include "mock/MockVSyncTracker.h" +#include "mock/MockVsyncController.h" using namespace std::chrono_literals; using namespace std::placeholders; using testing::_; using testing::Invoke; +using testing::Return; namespace android { @@ -50,24 +54,13 @@ constexpr PhysicalDisplayId DISPLAY_ID_64BIT = constexpr std::chrono::duration VSYNC_PERIOD(16ms); -class MockVSyncSource : public VSyncSource { -public: - const char* getName() const override { return "test"; } - - MOCK_METHOD1(setVSyncEnabled, void(bool)); - MOCK_METHOD1(setCallback, void(VSyncSource::Callback*)); - MOCK_METHOD2(setDuration, - void(std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration)); - MOCK_METHOD1(pauseVsyncCallback, void(bool)); - MOCK_METHOD(VSyncSource::VSyncData, getLatestVSyncData, (), (const, override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); -}; - } // namespace class EventThreadTest : public testing::Test { protected: + static constexpr std::chrono::nanoseconds kWorkDuration = 0ms; + static constexpr std::chrono::nanoseconds kReadyDuration = 3ms; + class MockEventThreadConnection : public EventThreadConnection { public: MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid, @@ -84,21 +77,21 @@ protected: EventThreadTest(); ~EventThreadTest() override; - void createThread(std::unique_ptr<VSyncSource>); + void createThread(); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, EventRegistrationFlags eventRegistration = {}, uid_t ownerUid = mConnectionUid); - void expectVSyncSetEnabledCallReceived(bool expectedState); + void expectVSyncCallbackScheduleReceived(bool expectState); void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration); - VSyncSource::Callback* expectVSyncSetCallbackCallReceived(); void expectVsyncEventReceivedByConnection(const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount); void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); - void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp, - VSyncSource::VSyncData preferredVsyncData); + void expectVsyncEventFrameTimelinesCorrect( + nsecs_t expectedTimestamp, + /*VSyncSource::VSyncData*/ gui::VsyncEventData::FrameTimeline preferredVsyncData); void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, bool expectedConnected); void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, @@ -108,17 +101,31 @@ protected: void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); - AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; - AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; - AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)> - mVSyncSetDurationCallRecorder; + void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime, + nsecs_t deadlineTimestamp) { + mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp); + } + + AsyncCallRecorderWithCannedReturn< + scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken, + scheduler::VSyncDispatch::ScheduleTiming)> + mVSyncCallbackScheduleRecorder{0}; + AsyncCallRecorderWithCannedReturn< + scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken, + scheduler::VSyncDispatch::ScheduleTiming)> + mVSyncCallbackUpdateRecorder{0}; + AsyncCallRecorderWithCannedReturn< + scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback, + std::string)> + mVSyncCallbackRegisterRecorder{scheduler::VSyncDispatch::CallbackToken(0)}; + AsyncCallRecorder<void (*)(scheduler::VSyncDispatch::CallbackToken)> + mVSyncCallbackUnregisterRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; - MockVSyncSource* mVSyncSource; - VSyncSource::Callback* mCallback = nullptr; + std::optional<scheduler::VsyncSchedule> mVsyncSchedule; std::unique_ptr<impl::EventThread> mThread; sp<MockEventThreadConnection> mConnection; sp<MockEventThreadConnection> mThrottledConnection; @@ -133,19 +140,22 @@ EventThreadTest::EventThreadTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - auto vsyncSource = std::make_unique<MockVSyncSource>(); - mVSyncSource = vsyncSource.get(); - - EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_)) - .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable())); - - EXPECT_CALL(*mVSyncSource, setCallback(_)) - .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable())); - - EXPECT_CALL(*mVSyncSource, setDuration(_, _)) - .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable())); - - createThread(std::move(vsyncSource)); + mVsyncSchedule.emplace(scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(), + std::make_unique<mock::VSyncDispatch>(), + nullptr)); + + mock::VSyncDispatch& mockDispatch = + *static_cast<mock::VSyncDispatch*>(&mVsyncSchedule->getDispatch()); + EXPECT_CALL(mockDispatch, registerCallback(_, _)) + .WillRepeatedly(Invoke(mVSyncCallbackRegisterRecorder.getInvocable())); + EXPECT_CALL(mockDispatch, schedule(_, _)) + .WillRepeatedly(Invoke(mVSyncCallbackScheduleRecorder.getInvocable())); + EXPECT_CALL(mockDispatch, update(_, _)) + .WillRepeatedly(Invoke(mVSyncCallbackUpdateRecorder.getInvocable())); + EXPECT_CALL(mockDispatch, unregisterCallback(_)) + .WillRepeatedly(Invoke(mVSyncCallbackUnregisterRecorder.getInvocable())); + + createThread(); mConnection = createConnection(mConnectionEventCallRecorder, gui::ISurfaceComposer::EventRegistration::modeChanged | @@ -164,11 +174,12 @@ EventThreadTest::~EventThreadTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + mThread.reset(); // EventThread should unregister itself as VSyncSource callback. - EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value()); } -void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { +void EventThreadTest::createThread() { const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) { mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid); return (uid == mThrottledConnectionUid); @@ -178,12 +189,13 @@ void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { }; mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); - mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(), - throttleVsync, getVsyncPeriod); + mThread = + std::make_unique<impl::EventThread>(/*std::move(source), */ "EventThreadTest", + *mVsyncSchedule, mTokenManager.get(), throttleVsync, + getVsyncPeriod, kWorkDuration, kReadyDuration); // EventThread should register itself as VSyncSource callback. - mCallback = expectVSyncSetCallbackCallReceived(); - ASSERT_TRUE(mCallback); + EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value()); } sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( @@ -197,23 +209,20 @@ sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection return connection; } -void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) { - auto args = mVSyncSetEnabledCallRecorder.waitForCall(); - ASSERT_TRUE(args.has_value()); - EXPECT_EQ(expectedState, std::get<0>(args.value())); +void EventThreadTest::expectVSyncCallbackScheduleReceived(bool expectState) { + if (expectState) { + ASSERT_TRUE(mVSyncCallbackScheduleRecorder.waitForCall().has_value()); + } else { + ASSERT_FALSE(mVSyncCallbackScheduleRecorder.waitForUnexpectedCall().has_value()); + } } void EventThreadTest::expectVSyncSetDurationCallReceived( std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) { - auto args = mVSyncSetDurationCallRecorder.waitForCall(); + auto args = mVSyncCallbackUpdateRecorder.waitForCall(); ASSERT_TRUE(args.has_value()); - EXPECT_EQ(expectedDuration, std::get<0>(args.value())); - EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value())); -} - -VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() { - auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall(); - return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr; + EXPECT_EQ(expectedDuration.count(), std::get<1>(args.value()).workDuration); + EXPECT_EQ(expectedReadyDuration.count(), std::get<1>(args.value()).readyDuration); } void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) { @@ -246,7 +255,7 @@ void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimes } void EventThreadTest::expectVsyncEventFrameTimelinesCorrect( - nsecs_t expectedTimestamp, VSyncSource::VSyncData preferredVsyncData) { + nsecs_t expectedTimestamp, VsyncEventData::FrameTimeline preferredVsyncData) { auto args = mConnectionEventCallRecorder.waitForCall(); ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp " << expectedTimestamp; @@ -335,9 +344,10 @@ using namespace testing; */ TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) { - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); - EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); - EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncCallbackUpdateRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncCallbackUnregisterRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value()); } @@ -350,7 +360,7 @@ TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) { mThread->requestNextVsync(mConnection); // EventThread should not enable vsync callbacks. - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + expectVSyncCallbackScheduleReceived(false); } TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { @@ -360,47 +370,51 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { // EventThread should immediately request a resync. EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value()); - // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + // EventThread should enable schedule a vsync callback + expectVSyncCallbackScheduleReceived(true); // Use the received callback to signal a first vsync event. // The throttler should receive the event, as well as the connection. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + // EventThread is requesting one more callback due to VsyncRequest::SingleSuppressCallback + expectVSyncCallbackScheduleReceived(true); + // Use the received callback to signal a second vsync event. // The throttler should receive the event, but the connection should // not as it was only interested in the first. - mCallback->onVSyncEvent(456, {123, 0}); + onVSyncEvent(456, 123, 0); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); // EventThread should also detect that at this point that it does not need // any more vsync events, and should disable their generation. - expectVSyncSetEnabledCallReceived(false); + expectVSyncCallbackScheduleReceived(false); } TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) { // Signal that we want the next vsync event to be posted to the connection mThread->requestNextVsync(mConnection); - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // Use the received callback to signal a vsync event. // The throttler should receive the event, as well as the connection. - VSyncSource::VSyncData vsyncData = {456, 789}; - mCallback->onVSyncEvent(123, vsyncData); - expectVsyncEventFrameTimelinesCorrect(123, vsyncData); + onVSyncEvent(123, 456, 789); + expectVsyncEventFrameTimelinesCorrect(123, {-1, 789, 456}); } TEST_F(EventThreadTest, getLatestVsyncEventData) { const nsecs_t now = systemTime(); - const nsecs_t preferredDeadline = now + 10000000; const nsecs_t preferredExpectedPresentationTime = now + 20000000; - const VSyncSource::VSyncData preferredData = {preferredExpectedPresentationTime, - preferredDeadline}; - EXPECT_CALL(*mVSyncSource, getLatestVSyncData()).WillOnce(Return(preferredData)); + const nsecs_t preferredDeadline = preferredExpectedPresentationTime - kReadyDuration.count(); + + mock::VSyncTracker& mockTracker = + *static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker()); + EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_)) + .WillOnce(Return(preferredExpectedPresentationTime)); VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection); @@ -451,8 +465,7 @@ TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { mThread->setVsyncRate(0, firstConnection); // By itself, this should not enable vsync events - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); - EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); + expectVSyncCallbackScheduleReceived(false); // However if there is another connection which wants events at a nonzero rate..... ConnectionEventRecorder secondConnectionEventRecorder{0}; @@ -461,12 +474,12 @@ TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { mThread->setVsyncRate(1, secondConnection); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // Send a vsync event. EventThread should then make a call to the // the second connection. The first connection should not // get the event. - mCallback->onVSyncEvent(123, {456, 0}); + onVSyncEvent(123, 0456, 0); EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value()); expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123, 1u); @@ -476,21 +489,21 @@ TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { mThread->setVsyncRate(1, mConnection); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // Send a vsync event. EventThread should then make a call to the // throttler, and the connection. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // A second event should go to the same places. - mCallback->onVSyncEvent(456, {123, 0}); + onVSyncEvent(456, 123, 0); expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); // A third event should go to the same places. - mCallback->onVSyncEvent(789, {777, 111}); + onVSyncEvent(789, 777, 111); expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); } @@ -499,25 +512,25 @@ TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { mThread->setVsyncRate(2, mConnection); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // The first event will not be seen by the connection. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The second event will be seen by the connection. - mCallback->onVSyncEvent(456, {123, 0}); + onVSyncEvent(456, 123, 0); expectVsyncEventReceivedByConnection(456, 2u); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The third event will not be seen by the connection. - mCallback->onVSyncEvent(789, {777, 744}); + onVSyncEvent(789, 777, 744); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The fourth event will be seen by the connection. - mCallback->onVSyncEvent(101112, {7847, 86}); + onVSyncEvent(101112, 7847, 86); expectVsyncEventReceivedByConnection(101112, 4u); } @@ -525,17 +538,17 @@ TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) { mThread->setVsyncRate(1, mConnection); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // Destroy the only (strong) reference to the connection. mConnection = nullptr; // The first event will not be seen by the connection. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 56, 789); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); // EventThread should disable vsync callbacks - expectVSyncSetEnabledCallReceived(false); + expectVSyncCallbackScheduleReceived(false); } TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { @@ -544,18 +557,22 @@ TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { mThread->setVsyncRate(1, errorConnection); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // The first event will be seen by the connection, which then returns an error. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); + // Another schedule is expected, since the connection is removed only after + // the next vsync is requested. + expectVSyncCallbackScheduleReceived(true); + // A subsequent event will not be seen by the connection. - mCallback->onVSyncEvent(456, {123, 0}); + onVSyncEvent(456, 123, 0); EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value()); // EventThread should disable vsync callbacks with the second event - expectVSyncSetEnabledCallReceived(false); + expectVSyncCallbackScheduleReceived(false); } TEST_F(EventThreadTest, tracksEventConnections) { @@ -571,10 +588,10 @@ TEST_F(EventThreadTest, tracksEventConnections) { EXPECT_EQ(4, mThread->getEventThreadConnectionCount()); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // The first event will be seen by the connection, which then returns an error. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123, 1u); @@ -587,19 +604,22 @@ TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { mThread->setVsyncRate(1, errorConnection); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // The first event will be seen by the connection, which then returns a non-fatal error. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); + expectVSyncCallbackScheduleReceived(true); // A subsequent event will be seen by the connection, which still then returns a non-fatal // error. - mCallback->onVSyncEvent(456, {123, 0}); + onVSyncEvent(456, 123, 0); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u); + expectVSyncCallbackScheduleReceived(true); // EventThread will not disable vsync callbacks as the errors are non-fatal. - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + onVSyncEvent(456, 123, 0); + expectVSyncCallbackScheduleReceived(true); } TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { @@ -718,24 +738,27 @@ TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) { EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value()); // EventThread should enable vsync callbacks. - expectVSyncSetEnabledCallReceived(true); + expectVSyncCallbackScheduleReceived(true); // Use the received callback to signal a first vsync event. // The throttler should receive the event, but not the connection. - mCallback->onVSyncEvent(123, {456, 789}); + onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mThrottledConnectionUid); mThrottledConnectionEventCallRecorder.waitForUnexpectedCall(); + expectVSyncCallbackScheduleReceived(true); // Use the received callback to signal a second vsync event. // The throttler should receive the event, but the connection should // not as it was only interested in the first. - mCallback->onVSyncEvent(456, {123, 0}); + onVSyncEvent(456, 123, 0); expectThrottleVsyncReceived(123, mThrottledConnectionUid); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + expectVSyncCallbackScheduleReceived(true); // EventThread should not change the vsync state as it didn't send the event // yet - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + onVSyncEvent(456, 123, 0); + expectVSyncCallbackScheduleReceived(true); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 8f89a8ca7a..afbc57add8 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -147,6 +147,7 @@ TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) { }), Return(hardware::graphics::composer::V2_4::Error::NONE))); EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE)); + EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::NONE)); EXPECT_CALL(*mHal, registerCallback(_)); @@ -165,6 +166,7 @@ TEST_F(HWComposerSetCallbackTest, handlesUnsupportedCallToGetLayerGenericMetadat EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)) .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED)); EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED)); + EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::UNSUPPORTED)); EXPECT_CALL(*mHal, registerCallback(_)); mHwc.setCallback(mCallback); diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 5e1042e91f..7aa5201f2f 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -23,6 +23,7 @@ #include "FrameTimeline.h" #include "Scheduler/MessageQueue.h" #include "SurfaceFlinger.h" +#include "mock/MockVSyncDispatch.h" namespace android { @@ -59,14 +60,6 @@ public: const sp<MockHandler> mHandler; }; -struct MockVSyncDispatch : scheduler::VSyncDispatch { - MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override)); - MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override)); - MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override)); - MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override)); - MOCK_METHOD(void, dump, (std::string&), (const, override)); -}; - struct MockTokenManager : frametimeline::TokenManager { MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction)); MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t)); @@ -79,7 +72,7 @@ struct MessageQueueTest : testing::Test { EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); } - MockVSyncDispatch mVSyncDispatch; + mock::VSyncDispatch mVSyncDispatch; MockTokenManager mTokenManager; TestableMessageQueue mEventQueue; diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index b8a60638ae..0f53eb69f3 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -26,6 +26,7 @@ #include "Scheduler/Scheduler.h" #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncController.h" +#include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" @@ -42,7 +43,9 @@ public: std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { - mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); + mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), + std::make_unique<mock::VSyncDispatch>(), + std::move(controller))); const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr)); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 113c518c4b..584d52ca02 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -231,6 +231,8 @@ public: std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps, hal::PowerMode::OFF); + mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); + using Callback = scheduler::ISchedulerCallback; Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp ? static_cast<Callback&>(mNoOpSchedulerCallback) @@ -248,6 +250,8 @@ public: std::move(selectorPtr), callback); } + mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms); + mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); resetScheduler(mScheduler); @@ -911,6 +915,7 @@ private: sp<SurfaceFlinger> mFlinger; scheduler::mock::SchedulerCallback mSchedulerCallback; scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; + std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; }; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 2b86e94244..14a2860378 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -270,6 +270,43 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { EXPECT_THAT(cb.mCalls[0], Eq(mPeriod)); } +TEST_F(VSyncDispatchTimerQueueTest, updateAlarmSettingFuture) { + auto intended = mPeriod - 230; + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq); + + CountingCallback cb(mDispatch); + auto result = mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = intended}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(900, *result); + + result = mDispatch.update(cb, + {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(700, *result); + + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(mPeriod)); + EXPECT_THAT(cb.mWakeupTime[0], Eq(700)); +} + +TEST_F(VSyncDispatchTimerQueueTest, updateDoesntSchedule) { + auto intended = mPeriod - 230; + EXPECT_CALL(mMockClock, alarmAt(_, _)).Times(0); + + CountingCallback cb(mDispatch); + const auto result = + mDispatch.update(cb, + {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + EXPECT_FALSE(result.has_value()); +} + TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) { EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); EXPECT_CALL(mMockClock, alarmAt(_, 1050)); diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 8bd689a61d..1fb2709f8d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -69,14 +69,6 @@ private: std::shared_ptr<Clock> const mClock; }; -struct MockVSyncDispatch : VSyncDispatch { - MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override)); - MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override)); - MOCK_METHOD(ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override)); - MOCK_METHOD(CancelResult, cancel, (CallbackToken), (override)); - MOCK_METHOD(void, dump, (std::string&), (const, override)); -}; - std::shared_ptr<android::FenceTime> generateInvalidFence() { sp<Fence> fence = sp<Fence>::make(); return std::make_shared<android::FenceTime>(fence); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 2f16b7b29f..5e29fe7539 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -145,6 +145,11 @@ public: MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config)); MOCK_METHOD1(clearBootDisplayConfig, Error(Display)); MOCK_METHOD2(getPreferredBootDisplayConfig, Error(Display, Config*)); + MOCK_METHOD1(getHdrConversionCapabilities, + Error(std::vector< + aidl::android::hardware::graphics::common::HdrConversionCapability>*)); + MOCK_METHOD1(setHdrConversionStrategy, + Error(aidl::android::hardware::graphics::common::HdrConversionStrategy)); MOCK_METHOD2(getSupportedContentTypes, V2_4::Error(Display, std::vector<IComposerClient::ContentType>*)); MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType)); diff --git a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp index dde4d38cba..2817514db6 100644 --- a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2023 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. @@ -14,18 +14,12 @@ * limitations under the License. */ -package android.gui; +#include "mock/MockVSyncDispatch.h" -import android.gui.ListenerStats; -import android.gui.ReleaseCallbackId; +namespace android::mock { -/** @hide */ -oneway interface ITransactionCompletedListener { - void onTransactionCompleted(in ListenerStats stats); +// Explicit default instantiation is recommended. +VSyncDispatch::VSyncDispatch() = default; +VSyncDispatch::~VSyncDispatch() = default; - void onReleaseBuffer(in ReleaseCallbackId callbackId, - in @nullable ParcelFileDescriptor releaseFenceFd, - int currentMaxAcquiredBufferCount); - - void onTransactionQueueStalled(@utf8InCpp String name); -} +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h new file mode 100644 index 0000000000..dc32ff969c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h @@ -0,0 +1,38 @@ +/* + * Copyright 2023 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 <gmock/gmock.h> + +#include "Scheduler/VSyncDispatch.h" + +namespace android::mock { + +class VSyncDispatch : public android::scheduler::VSyncDispatch { +public: + VSyncDispatch(); + ~VSyncDispatch() override; + + MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override)); + MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override)); + MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override)); + MOCK_METHOD(scheduler::ScheduleResult, update, (CallbackToken, ScheduleTiming), (override)); + MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override)); + MOCK_METHOD(void, dump, (std::string&), (const, override)); +}; + +} // namespace android::mock |