diff options
140 files changed, 5093 insertions, 1726 deletions
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS index 5f56531754..41bfd26774 100644 --- a/cmds/bugreport/OWNERS +++ b/cmds/bugreport/OWNERS @@ -1,5 +1,5 @@ set noparent -gavincorkery@google.com +ronish@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS index 5f56531754..41bfd26774 100644 --- a/cmds/bugreportz/OWNERS +++ b/cmds/bugreportz/OWNERS @@ -1,5 +1,5 @@ set noparent -gavincorkery@google.com +ronish@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS index ab81ecf4f4..c24bf39ad7 100644 --- a/cmds/dumpstate/OWNERS +++ b/cmds/dumpstate/OWNERS @@ -1,6 +1,6 @@ set noparent -gavincorkery@google.com +ronish@google.com nandana@google.com jsharkey@android.com smoreland@google.com
\ No newline at end of file diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS index 97a63ca8fe..03143ae659 100644 --- a/cmds/dumpsys/OWNERS +++ b/cmds/dumpsys/OWNERS @@ -1,6 +1,6 @@ set noparent -gavincorkery@google.com +ronish@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 870e8eb846..0c1feb8fec 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -731,16 +731,17 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, [hashChain](const auto& ret) { *hashChain = std::move(ret); }); if (!hashRet.isOk()) { handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); + break; // skip getHashChain } if (static_cast<size_t>(hashIndex) >= hashChain->size()) { handleError(BAD_IMPL, "interfaceChain indicates position " + std::to_string(hashIndex) + " but getHashChain returns " + std::to_string(hashChain->size()) + " hashes"); - } else { - auto&& hashArray = (*hashChain)[hashIndex]; - entry->hash = android::base::HexString(hashArray.data(), hashArray.size()); + break; // skip getHashChain } + auto&& hashArray = (*hashChain)[hashIndex]; + entry->hash = android::base::HexString(hashArray.data(), hashArray.size()); } while (0); if (status == OK) { entry->serviceStatus = ServiceStatus::ALIVE; diff --git a/include/android/OWNERS b/include/android/OWNERS index 38f9c5563a..fad8c1b890 100644 --- a/include/android/OWNERS +++ b/include/android/OWNERS @@ -1 +1,5 @@ -per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS +per-file input.h,keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS + +# Window manager +per-file surface_control_input_receiver.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS +per-file input_transfer_token.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS diff --git a/include/android/input_transfer_token_jni.h b/include/android/input_transfer_token_jni.h index ba5f6f2bdd..92fe9b6921 100644 --- a/include/android/input_transfer_token_jni.h +++ b/include/android/input_transfer_token_jni.h @@ -20,8 +20,9 @@ /** * @file input_transfer_token_jni.h */ -#ifndef ANDROID_INPUT_TRANSFER_TOKEN_JNI_H -#define ANDROID_INPUT_TRANSFER_TOKEN_JNI_H + +#pragma once + #include <sys/cdefs.h> #include <jni.h> @@ -60,9 +61,8 @@ jobject _Nonnull AInputTransferToken_toJava(JNIEnv* _Nonnull env, * * Available since API level 35. */ -void AInputTransferToken_release(AInputTransferToken* _Nonnull aInputTransferToken) +void AInputTransferToken_release(AInputTransferToken* _Nullable aInputTransferToken) __INTRODUCED_IN(__ANDROID_API_V__); __END_DECLS -#endif // ANDROID_INPUT_TRANSFER_TOKEN_JNI_H /** @} */ diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 082387e63a..fb1419f0b2 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -236,6 +236,10 @@ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_ * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1. * * Available since API level 29. + * + * @deprecated This may return SIGNAL_PENDING because the stats can arrive before the acquire + * fence has signaled, depending on internal timing differences. Therefore the caller should + * use the acquire fence passed in to setBuffer and query the signal time. */ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, ASurfaceControl* surface_control) diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h new file mode 100644 index 0000000000..bdc52490d5 --- /dev/null +++ b/include/android/surface_control_input_receiver.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 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. + */ +/** + * @addtogroup NativeActivity Native Activity + * @{ + */ +/** + * @file surface_control_input_receiver.h + */ + +#pragma once + +#include <stdint.h> +#include <android/input.h> +#include <android/surface_control.h> +#include <android/input_transfer_token_jni.h> + +__BEGIN_DECLS + +/** + * The AInputReceiver_onMotionEvent callback is invoked when the registered input channel receives + * a motion event. + * + * \param context Optional context provided by the client that is passed when creating the + * AInputReceiverCallbacks. + * + * \param motionEvent The motion event. This must be released with AInputEvent_release. + * + * Available since API level 35. + */ +typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context, + AInputEvent *_Nonnull motionEvent) + __INTRODUCED_IN(__ANDROID_API_V__); +/** + * The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives + * a key event. + * + * \param context Optional context provided by the client that is passed when creating the + * AInputReceiverCallbacks. + * + * \param keyEvent The key event. This must be released with AInputEvent_release. + * + * Available since API level 35. + */ +typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context, + AInputEvent *_Nonnull keyEvent) + __INTRODUCED_IN(__ANDROID_API_V__); + +struct AInputReceiverCallbacks; + +struct AInputReceiver; + +/** + * The InputReceiver that holds the reference to the registered input channel. This must be released + * using AInputReceiver_release + * + * Available since API level 35. + */ +typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Registers an input receiver for an ASurfaceControl that will receive batched input event. For + * those events that are batched, the invocation will happen once per AChoreographer frame, and + * other input events will be delivered immediately. + * + * This is different from AInputReceiver_createUnbatchedInputReceiver in that the input events are + * received batched. The caller must invoke AInputReceiver_release to clean up the resources when + * no longer needing to use the input receiver. + * + * \param aChoreographer The AChoreographer used for batching. This should match the + * rendering AChoreographer. + * \param hostInputTransferToken The host token to link the embedded. This is used to handle + * transferring touch gesture from host to embedded and for ANRs + * to ensure the host receives the ANR if any issues with + * touch on the embedded. This can be retrieved for the host window + * by calling AttachedSurfaceControl#getInputTransferToken() + * \param aSurfaceControl The ASurfaceControl to register the InputChannel for + * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events + * + * Returns the reference to AInputReceiver to clean up resources when done. + * + * Available since API level 35. + */ +AInputReceiver* _Nonnull +AInputReceiver_createBatchedInputReceiver(AChoreographer* _Nonnull aChoreographer, + const AInputTransferToken* _Nonnull hostInputTransferToken, + const ASurfaceControl* _Nonnull aSurfaceControl, + AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Registers an input receiver for an ASurfaceControl that will receive every input event. + * This is different from AInputReceiver_createBatchedInputReceiver in that the input events are + * received unbatched. The caller must invoke AInputReceiver_release to clean up the resources when + * no longer needing to use the input receiver. + * + * \param aLooper The looper to use when invoking callbacks. + * \param hostInputTransferToken The host token to link the embedded. This is used to handle + * transferring touch gesture from host to embedded and for ANRs + * to ensure the host receives the ANR if any issues with + * touch on the embedded. This can be retrieved for the host window + * by calling AttachedSurfaceControl#getInputTransferToken() + * \param aSurfaceControl The ASurfaceControl to register the InputChannel for + * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events + * + * Returns the reference to AInputReceiver to clean up resources when done. + * + * Available since API level 35. + */ +AInputReceiver* _Nonnull +AInputReceiver_createUnbatchedInputReceiver(ALooper* _Nonnull aLooper, + const AInputTransferToken* _Nonnull hostInputTransferToken, + const ASurfaceControl* _Nonnull aSurfaceControl, + AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Returns the AInputTransferToken that can be used to transfer touch gesture to or from other + * windows. This InputTransferToken is associated with the SurfaceControl that registered an input + * receiver and can be used with the host token for things like transfer touch gesture via + * WindowManager#transferTouchGesture(). + * + * This must be released with AInputTransferToken_release. + * + * \param aInputReceiver The inputReceiver object to retrieve the AInputTransferToken for. + * + * Available since API level 35. + */ +const AInputTransferToken *_Nonnull +AInputReceiver_getInputTransferToken(AInputReceiver *_Nonnull aInputReceiver) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Unregisters the input channel and deletes the AInputReceiver. This must be called on the same + * looper thread it was created with. + * + * \param aInputReceiver The inputReceiver object to release. + * + * Available since API level 35. + */ +void +AInputReceiver_release(AInputReceiver *_Nullable aInputReceiver) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Creates a AInputReceiverCallbacks object that is used when registering for an AInputReceiver. + * This must be released using AInputReceiverCallbacks_release + * + * \param context Optional context provided by the client that will be passed into the callbacks. + * + * Available since API level 35. + */ +AInputReceiverCallbacks* _Nonnull AInputReceiverCallbacks_create(void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Releases the AInputReceiverCallbacks. This must be called on the same + * looper thread the AInputReceiver was created with. The receiver will not invoke any callbacks + * once it's been released. + * + * Available since API level 35 + */ +void AInputReceiverCallbacks_release(AInputReceiverCallbacks* _Nullable callbacks) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets a AInputReceiver_onMotionEvent callback for an AInputReceiverCallbacks + * + * \param callbacks The callback object to set the motion event on. + * \param onMotionEvent The motion event that will be invoked + * + * Available since API level 35. + */ +void AInputReceiverCallbacks_setMotionEventCallback(AInputReceiverCallbacks* _Nonnull callbacks, + AInputReceiver_onMotionEvent _Nonnull onMotionEvent) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets a AInputReceiver_onKeyEvent callback for an AInputReceiverCallbacks + * + * \param callbacks The callback object to set the motion event on. + * \param onMotionEvent The key event that will be invoked + * + * Available since API level 35. + */ +void AInputReceiverCallbacks_setKeyEventCallback(AInputReceiverCallbacks* _Nonnull callbacks, + AInputReceiver_onKeyEvent _Nonnull onKeyEvent) + __INTRODUCED_IN(__ANDROID_API_V__); + +__END_DECLS diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h new file mode 100644 index 0000000000..9e48b0872d --- /dev/null +++ b/include/input/InputConsumerNoResampling.h @@ -0,0 +1,211 @@ +/** + * Copyright 2024 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 <utils/Looper.h> +#include "InputTransport.h" + +namespace android { + +/** + * An interface to receive batched input events. Even if you don't want batching, you still have to + * use this interface, and some of the events will be batched if your implementation is slow to + * handle the incoming input. + */ +class InputConsumerCallbacks { +public: + virtual ~InputConsumerCallbacks(){}; + virtual void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) = 0; + virtual void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) = 0; + /** + * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents". + * If you don't want batching, then call "consumeBatchedInputEvents" immediately with + * std::nullopt frameTime to receive the pending motion event(s). + * @param pendingBatchSource the source of the pending batch. + */ + virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0; + virtual void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) = 0; + virtual void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) = 0; + virtual void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) = 0; + virtual void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) = 0; +}; + +/** + * Consumes input events from an input channel. + * + * This is a re-implementation of InputConsumer that does not have resampling at the current moment. + * A lot of the higher-level logic has been folded into this class, to make it easier to use. + * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer, + * as well as various actions like adding the fd to the Choreographer. + * + * TODO(b/297226446): use this instead of "InputConsumer": + * - Add resampling to this class + * - Allow various resampling strategies to be specified + * - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer". + * - Add tracing + * - Update all tests to use the new InputConsumer + * + * This class is not thread-safe. We are currently allowing the constructor to run on any thread, + * but all of the remaining APIs should be invoked on the looper thread only. + */ +class InputConsumerNoResampling final { +public: + explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, + sp<Looper> looper, InputConsumerCallbacks& callbacks); + ~InputConsumerNoResampling(); + + /** + * Must be called exactly once for each event received through the callbacks. + */ + void finishInputEvent(uint32_t seq, bool handled); + void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime); + /** + * If you want to consume all events immediately (disable batching), the you still must call + * this. For frameTime, use a std::nullopt. + * @param frameTime the time up to which consume the events. When there's double (or triple) + * buffering, you may want to not consume all events currently available, because you could be + * still working on an older frame, but there could already have been events that arrived that + * are more recent. + * @return whether any events were actually consumed + */ + bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime); + /** + * Returns true when there is *likely* a pending batch or a pending event in the channel. + * + * This is only a performance hint and may return false negative results. Clients should not + * rely on availability of the message based on the return value. + */ + bool probablyHasInput() const; + + std::string getName() { return mChannel->getName(); } + + std::string dump() const; + +private: + std::shared_ptr<InputChannel> mChannel; + sp<Looper> mLooper; + InputConsumerCallbacks& mCallbacks; + + // Looper-related infrastructure + /** + * This class is needed to associate the function "handleReceiveCallback" with the provided + * looper. The callback sent to the looper is RefBase - based, so we can't just send a reference + * of this class directly to the looper. + */ + class LooperEventCallback : public LooperCallback { + public: + LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {} + int handleEvent(int /*fd*/, int events, void* /*data*/) override { + return mCallback(events); + } + + private: + std::function<int(int events)> mCallback; + }; + sp<LooperEventCallback> mCallback; + /** + * The actual code that executes when the looper encounters available data on the InputChannel. + */ + int handleReceiveCallback(int events); + int mFdEvents; + void setFdEvents(int events); + + void ensureCalledOnLooperThread(const char* func) const; + + // Event-reading infrastructure + /** + * A fifo queue of events to be sent to the InputChannel. We can't send all InputMessages to + * the channel immediately when they are produced, because it's possible that the InputChannel + * is blocked (if the channel buffer is full). When that happens, we don't want to drop the + * events. Therefore, events should only be erased from the queue after they've been + * successfully written to the InputChannel. + */ + std::queue<InputMessage> mOutboundQueue; + /** + * Try to send all of the events in mOutboundQueue over the InputChannel. Not all events might + * actually get sent, because it's possible that the channel is blocked. + */ + void processOutboundEvents(); + + /** + * The time at which each event with the sequence number 'seq' was consumed. + * This data is provided in 'finishInputEvent' so that the receiving end can measure the latency + * This collection is populated when the event is received, and the entries are erased when the + * events are finished. It should not grow infinitely because if an event is not ack'd, ANR + * will be raised for that connection, and no further events will be posted to that channel. + */ + std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes; + /** + * Find and return the consumeTime associated with the provided sequence number. Crashes if + * the provided seq number is not found. + */ + nsecs_t popConsumeTime(uint32_t seq); + + // Event reading and processing + /** + * Read all of the available events from the InputChannel + */ + std::vector<InputMessage> readAllMessages(); + + /** + * Send InputMessage to the corresponding InputConsumerCallbacks function. + * @param msg + */ + void handleMessage(const InputMessage& msg) const; + + // Batching + /** + * Batch messages that can be batched. When an unbatchable message is encountered, send it + * to the InputConsumerCallbacks immediately. If there are batches remaining, + * notify InputConsumerCallbacks. + */ + void handleMessages(std::vector<InputMessage>&& messages); + /** + * Batched InputMessages, per deviceId. + * For each device, we are storing a queue of batched messages. These will all be collapsed into + * a single MotionEvent (up to a specific frameTime) when the consumer calls + * `consumeBatchedInputEvents`. + */ + std::map<DeviceId, std::queue<InputMessage>> mBatches; + /** + * A map from a single sequence number to several sequence numbers. This is needed because of + * batching. When batching is enabled, a single MotionEvent will contain several samples. Each + * sample came from an individual InputMessage of Type::Motion, and therefore will have to be + * finished individually. Therefore, when the app calls "finish" on a (possibly batched) + * MotionEvent, we will need to check this map in case there are multiple sequence numbers + * associated with a single number that the app provided. + * + * For example: + * Suppose we received 4 InputMessage's of type Motion, with action MOVE: + * InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE) + * seq=10 seq=11 seq=12 seq=13 + * The app consumed them all as a batch, which means that the app received a single MotionEvent + * with historySize=3 and seq = 10. + * + * This map will look like: + * { + * 10: [11, 12, 13], + * } + * So the sequence number 10 will have 3 other sequence numbers associated with it. + * When the app calls 'finish' for seq=10, we need to call 'finish' 4 times total, for sequence + * numbers 10, 11, 12, 13. The app is not aware of the sequence numbers of each sample inside + * the batched MotionEvent that it received. + */ + std::map<uint32_t, std::vector<uint32_t>> mBatchedSequenceNumbers; +}; + +} // namespace android diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp index 69b11c0ee9..7b58046d36 100644 --- a/libs/binder/IBatteryStats.cpp +++ b/libs/binder/IBatteryStats.cpp @@ -66,14 +66,14 @@ public: Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); - remote()->transact(NOTE_START_AUDIO_TRANSACTION, data, &reply); + remote()->transact(NOTE_START_AUDIO_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void noteStopAudio(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); - remote()->transact(NOTE_STOP_AUDIO_TRANSACTION, data, &reply); + remote()->transact(NOTE_STOP_AUDIO_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void noteResetVideo() { @@ -85,7 +85,7 @@ public: virtual void noteResetAudio() { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); - remote()->transact(NOTE_RESET_AUDIO_TRANSACTION, data, &reply); + remote()->transact(NOTE_RESET_AUDIO_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void noteFlashlightOn(int uid) { diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index ccf3ce891f..30dbdddc60 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -40,6 +40,7 @@ cc_library { llndk: { symbol_file: "libbinder_ndk.map.txt", + export_llndk_headers: ["libvendorsupport_llndk_headers"], }, export_include_dirs: [ @@ -79,9 +80,11 @@ cc_library { ], header_libs: [ + "libvendorsupport_llndk_headers", "jni_headers", ], export_header_lib_headers: [ + "libvendorsupport_llndk_headers", "jni_headers", ], diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index bf7a0ba5f0..e2ede3f52a 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -258,11 +258,24 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce } } +void ABBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */, + void* /* cookie */) { + LOG_ALWAYS_FATAL("Should not reach this. Can't linkToDeath local binders."); +} + ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) : AIBinder(nullptr /*clazz*/), mRemote(binder) { LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); } -ABpBinder::~ABpBinder() {} + +ABpBinder::~ABpBinder() { + for (auto& recip : mDeathRecipients) { + sp<AIBinder_DeathRecipient> strongRecip = recip.recipient.promote(); + if (strongRecip) { + strongRecip->pruneThisTransferEntry(getBinder(), recip.cookie); + } + } +} sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) { if (binder == nullptr) { @@ -301,6 +314,12 @@ sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android:: return ret; } +void ABpBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient, + void* cookie) { + std::lock_guard<std::mutex> l(mDeathRecipientsMutex); + mDeathRecipients.emplace_back(recipient, cookie); +} + struct AIBinder_Weak { wp<AIBinder> binder; }; @@ -426,6 +445,17 @@ AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinde LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr"); } +void AIBinder_DeathRecipient::pruneThisTransferEntry(const sp<IBinder>& who, void* cookie) { + std::lock_guard<std::mutex> l(mDeathRecipientsMutex); + mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(), + [&](const sp<TransferDeathRecipient>& tdr) { + auto tdrWho = tdr->getWho(); + return tdrWho != nullptr && tdrWho.promote() == who && + cookie == tdr->getCookie(); + }), + mDeathRecipients.end()); +} + void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(), [](const sp<TransferDeathRecipient>& tdr) { @@ -554,8 +584,11 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* return STATUS_UNEXPECTED_NULL; } - // returns binder_status_t - return recipient->linkToDeath(binder->getBinder(), cookie); + binder_status_t ret = recipient->linkToDeath(binder->getBinder(), cookie); + if (ret == STATUS_OK) { + binder->addDeathRecipient(recipient, cookie); + } + return ret; } binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 9d5368f674..f5b738c1ef 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -51,6 +51,8 @@ struct AIBinder : public virtual ::android::RefBase { ::android::sp<::android::IBinder> binder = const_cast<AIBinder*>(this)->getBinder(); return binder->remoteBinder() != nullptr; } + virtual void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient, + void* cookie) = 0; private: // AIBinder instance is instance of this class for a local object. In order to transact on a @@ -78,6 +80,8 @@ struct ABBinder : public AIBinder, public ::android::BBinder { ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override; ::android::status_t onTransact(uint32_t code, const ::android::Parcel& data, ::android::Parcel* reply, binder_flags_t flags) override; + void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */, + void* /* cookie */) override; private: ABBinder(const AIBinder_Class* clazz, void* userData); @@ -106,12 +110,20 @@ struct ABpBinder : public AIBinder { bool isServiceFuzzing() const { return mServiceFuzzing; } void setServiceFuzzing() { mServiceFuzzing = true; } + void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient, + void* cookie) override; private: friend android::sp<ABpBinder>; explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); ::android::sp<::android::IBinder> mRemote; bool mServiceFuzzing = false; + struct DeathRecipientInfo { + android::wp<AIBinder_DeathRecipient> recipient; + void* cookie; + }; + std::mutex mDeathRecipientsMutex; + std::vector<DeathRecipientInfo> mDeathRecipients; }; struct AIBinder_Class { @@ -183,6 +195,7 @@ struct AIBinder_DeathRecipient : ::android::RefBase { binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie); binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie); void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked); + void pruneThisTransferEntry(const ::android::sp<::android::IBinder>&, void* cookie); private: // When the user of this API deletes a Bp object but not the death recipient, the diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h index 864ff50831..d570eab976 100644 --- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h +++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h @@ -22,6 +22,19 @@ #include <set> #include <sstream> +// Include llndk-versioning.h only for vendor build as it is not available for NDK headers. +#if defined(__ANDROID_VENDOR__) +#include <android/llndk-versioning.h> +#else // __ANDROID_VENDOR__ +#if defined(API_LEVEL_AT_LEAST) +// Redefine API_LEVEL_AT_LEAST here to replace the version to __ANDROID_API_FUTURE__ as a workaround +#undef API_LEVEL_AT_LEAST +#endif +// TODO(b/322384429) switch this __ANDROID_API_FUTURE__ to sdk_api_level when V is finalized +#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \ + (__builtin_available(android __ANDROID_API_FUTURE__, *)) +#endif // __ANDROID_VENDOR__ + namespace aidl::android::os { /** @@ -32,7 +45,7 @@ namespace aidl::android::os { class PersistableBundle { public: PersistableBundle() noexcept { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { mPBundle = APersistableBundle_new(); } } @@ -42,13 +55,13 @@ class PersistableBundle { PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {} // duplicates, does not take ownership of the APersistableBundle* PersistableBundle(const PersistableBundle& other) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { mPBundle = APersistableBundle_dup(other.mPBundle); } } // duplicates, does not take ownership of the APersistableBundle* PersistableBundle& operator=(const PersistableBundle& other) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { mPBundle = APersistableBundle_dup(other.mPBundle); } return *this; @@ -58,7 +71,7 @@ class PersistableBundle { binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { reset(); - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_readFromParcel(parcel, &mPBundle); } else { return STATUS_INVALID_OPERATION; @@ -69,7 +82,7 @@ class PersistableBundle { if (!mPBundle) { return STATUS_BAD_VALUE; } - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_writeToParcel(mPBundle, parcel); } else { return STATUS_INVALID_OPERATION; @@ -84,7 +97,7 @@ class PersistableBundle { */ void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept { if (mPBundle) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_delete(mPBundle); } mPBundle = nullptr; @@ -97,7 +110,7 @@ class PersistableBundle { * what should be used to check for equality. */ bool deepEquals(const PersistableBundle& rhs) const { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_isEqual(get(), rhs.get()); } else { return false; @@ -136,7 +149,7 @@ class PersistableBundle { inline std::string toString() const { if (!mPBundle) { return "<PersistableBundle: null>"; - } else if (__builtin_available(android __ANDROID_API_V__, *)) { + } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { std::ostringstream os; os << "<PersistableBundle: "; os << "size: " << std::to_string(APersistableBundle_size(mPBundle)); @@ -147,7 +160,7 @@ class PersistableBundle { } int32_t size() const { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_size(mPBundle); } else { return 0; @@ -155,7 +168,7 @@ class PersistableBundle { } int32_t erase(const std::string& key) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_erase(mPBundle, key.c_str()); } else { return 0; @@ -163,37 +176,37 @@ class PersistableBundle { } void putBoolean(const std::string& key, bool val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_putBoolean(mPBundle, key.c_str(), val); } } void putInt(const std::string& key, int32_t val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_putInt(mPBundle, key.c_str(), val); } } void putLong(const std::string& key, int64_t val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_putLong(mPBundle, key.c_str(), val); } } void putDouble(const std::string& key, double val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_putDouble(mPBundle, key.c_str(), val); } } void putString(const std::string& key, const std::string& val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_putString(mPBundle, key.c_str(), val.c_str()); } } void putBooleanVector(const std::string& key, const std::vector<bool>& vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { // std::vector<bool> has no ::data(). int32_t num = vec.size(); if (num > 0) { @@ -210,7 +223,7 @@ class PersistableBundle { } void putIntVector(const std::string& key, const std::vector<int32_t>& vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { int32_t num = vec.size(); if (num > 0) { APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num); @@ -218,7 +231,7 @@ class PersistableBundle { } } void putLongVector(const std::string& key, const std::vector<int64_t>& vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { int32_t num = vec.size(); if (num > 0) { APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num); @@ -226,7 +239,7 @@ class PersistableBundle { } } void putDoubleVector(const std::string& key, const std::vector<double>& vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { int32_t num = vec.size(); if (num > 0) { APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num); @@ -234,7 +247,7 @@ class PersistableBundle { } } void putStringVector(const std::string& key, const std::vector<std::string>& vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { int32_t num = vec.size(); if (num > 0) { char** inVec = (char**)malloc(num * sizeof(char*)); @@ -249,13 +262,13 @@ class PersistableBundle { } } void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle); } } bool getBoolean(const std::string& key, bool* _Nonnull val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_getBoolean(mPBundle, key.c_str(), val); } else { return false; @@ -263,7 +276,7 @@ class PersistableBundle { } bool getInt(const std::string& key, int32_t* _Nonnull val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_getInt(mPBundle, key.c_str(), val); } else { return false; @@ -271,7 +284,7 @@ class PersistableBundle { } bool getLong(const std::string& key, int64_t* _Nonnull val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_getLong(mPBundle, key.c_str(), val); } else { return false; @@ -279,7 +292,7 @@ class PersistableBundle { } bool getDouble(const std::string& key, double* _Nonnull val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return APersistableBundle_getDouble(mPBundle, key.c_str(), val); } else { return false; @@ -291,7 +304,7 @@ class PersistableBundle { } bool getString(const std::string& key, std::string* _Nonnull val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { char* outString = nullptr; bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString, &stringAllocator, nullptr); @@ -309,7 +322,7 @@ class PersistableBundle { const char* _Nonnull, T* _Nullable, int32_t), const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, std::vector<T>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { int32_t bytes = 0; // call first with nullptr to get required size in bytes bytes = getVec(pBundle, key, nullptr, 0); @@ -331,28 +344,28 @@ class PersistableBundle { } bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), vec); } return false; } bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), vec); } return false; } bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), vec); } return false; } bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(), vec); } @@ -377,7 +390,7 @@ class PersistableBundle { } bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, &stringAllocator, nullptr); if (bytes > 0) { @@ -394,7 +407,7 @@ class PersistableBundle { } bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { APersistableBundle* bundle = nullptr; bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle); if (ret) { @@ -426,77 +439,77 @@ class PersistableBundle { } std::set<std::string> getBooleanKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getBooleanKeys, mPBundle); } else { return {}; } } std::set<std::string> getIntKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getIntKeys, mPBundle); } else { return {}; } } std::set<std::string> getLongKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getLongKeys, mPBundle); } else { return {}; } } std::set<std::string> getDoubleKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getDoubleKeys, mPBundle); } else { return {}; } } std::set<std::string> getStringKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getStringKeys, mPBundle); } else { return {}; } } std::set<std::string> getBooleanVectorKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getIntVectorKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getLongVectorKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getDoubleVectorKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getStringVectorKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getPersistableBundleKeys() { - if (__builtin_available(android __ANDROID_API_V__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle); } else { return {}; diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h index 98c0cb2514..42ae15ae61 100644 --- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h +++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h @@ -17,6 +17,13 @@ #pragma once #include <android/binder_parcel.h> +#if defined(__ANDROID_VENDOR__) +#include <android/llndk-versioning.h> +#else +#if !defined(__INTRODUCED_IN_LLNDK) +#define __INTRODUCED_IN_LLNDK(level) __attribute__((annotate("introduced_in_llndk=" #level))) +#endif +#endif // __ANDROID_VENDOR__ #include <stdbool.h> #include <stdint.h> #include <sys/cdefs.h> @@ -67,25 +74,26 @@ typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t s /** * Create a new APersistableBundle. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \return Pointer to a new APersistableBundle */ -APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__); +APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Create a new APersistableBundle based off an existing APersistableBundle. * This is a deep copy, so the new APersistableBundle has its own values from * copying the original underlying PersistableBundle. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to duplicate * * \return Pointer to a new APersistableBundle */ APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Delete an APersistableBundle. This must always be called when finished using @@ -93,15 +101,15 @@ APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _ * * \param pBundle to delete. No-op if null. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_delete(APersistableBundle* _Nullable pBundle) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Check for equality of APersistableBundles. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param lhs bundle to compare against the other param * \param rhs bundle to compare against the other param @@ -110,12 +118,12 @@ void APersistableBundle_delete(APersistableBundle* _Nullable pBundle) */ bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, const APersistableBundle* _Nonnull rhs) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Read an APersistableBundle from an AParcel. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param parcel to read from * \param outPBundle bundle to write to @@ -129,12 +137,12 @@ bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, */ binder_status_t APersistableBundle_readFromParcel( const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Write an APersistableBundle to an AParcel. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle bundle to write to the parcel * \param parcel to write to @@ -149,25 +157,25 @@ binder_status_t APersistableBundle_readFromParcel( */ binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle, AParcel* _Nonnull parcel) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get the size of an APersistableBundle. This is the number of mappings in the * object. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to get the size of (number of mappings) * * \return number of mappings in the object */ int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Erase any entries added with the provided key. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 to erase @@ -175,7 +183,7 @@ int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle) * \return number of entries erased. Either 0 or 1. */ int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Put a boolean associated with the provided key. @@ -185,10 +193,11 @@ int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const cha * \param key for the mapping in UTF-8 * \param value to put for the mapping * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - bool val) __INTRODUCED_IN(__ANDROID_API_V__); + bool val) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put an int32_t associated with the provided key. @@ -198,10 +207,11 @@ void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const c * \param key for the mapping in UTF-8 * \param val value to put for the mapping * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int32_t val) __INTRODUCED_IN(__ANDROID_API_V__); + int32_t val) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put an int64_t associated with the provided key. @@ -211,10 +221,11 @@ void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* * \param key for the mapping in UTF-8 * \param val value to put for the mapping * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int64_t val) __INTRODUCED_IN(__ANDROID_API_V__); + int64_t val) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put a double associated with the provided key. @@ -224,10 +235,11 @@ void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char * \param key for the mapping in UTF-8 * \param val value to put for the mapping * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - double val) __INTRODUCED_IN(__ANDROID_API_V__); + double val) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put a string associated with the provided key. @@ -238,10 +250,11 @@ void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const ch * \param key for the mapping in UTF-8 * \param vec vector to put for the mapping * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); + const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put a boolean vector associated with the provided key. @@ -253,11 +266,12 @@ void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const ch * \param vec vector to put for the mapping * \param num number of elements in the vector * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const bool* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put an int32_t vector associated with the provided key. @@ -269,11 +283,11 @@ void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, * \param vec vector to put for the mapping * \param num number of elements in the vector * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const int32_t* _Nonnull vec, int32_t num) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Put an int64_t vector associated with the provided key. @@ -285,11 +299,12 @@ void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const * \param vec vector to put for the mapping * \param num number of elements in the vector * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const int64_t* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put a double vector associated with the provided key. @@ -301,11 +316,12 @@ void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, * \param vec vector to put for the mapping * \param num number of elements in the vector * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const double* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Put a string vector associated with the provided key. @@ -317,12 +333,12 @@ void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, * \param vec vector to put for the mapping * \param num number of elements in the vector * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const char* _Nullable const* _Nullable vec, int32_t num) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Put an APersistableBundle associated with the provided key. @@ -333,17 +349,17 @@ void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, * \param key for the mapping in UTF-8 * \param val value to put for the mapping * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. */ void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const APersistableBundle* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get a boolean associated with the provided key. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 @@ -353,12 +369,12 @@ void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundl */ bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, bool* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get an int32_t associated with the provided key. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 @@ -367,12 +383,13 @@ bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, * \return true if a value exists for the provided key */ bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); + int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get an int64_t associated with the provided key. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 @@ -382,12 +399,12 @@ bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const */ bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int64_t* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get a double associated with the provided key. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 @@ -397,13 +414,13 @@ bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, */ bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, double* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get a string associated with the provided key. * The caller is responsible for freeing the returned data. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 @@ -418,7 +435,8 @@ bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, char* _Nullable* _Nonnull val, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get a boolean vector associated with the provided key and place it in the @@ -445,7 +463,7 @@ int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, bool* _Nullable buffer, int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get an int32_t vector associated with the provided key and place it in the @@ -471,7 +489,8 @@ int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull p */ int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int32_t* _Nullable buffer, - int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__); + int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get an int64_t vector associated with the provided key and place it in the @@ -497,8 +516,8 @@ int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBund */ int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int64_t* _Nullable buffer, - int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__); + int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get a double vector associated with the provided key and place it in the @@ -525,7 +544,7 @@ int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBun int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, double* _Nullable buffer, int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get a string vector associated with the provided key and place it in the @@ -562,12 +581,12 @@ int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pB int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get an APersistableBundle* associated with the provided key. * - * Available since API level __ANDROID_API_V__. + * Available since API level 202404. * * \param pBundle to operate on * \param key for the mapping in UTF-8 @@ -581,7 +600,7 @@ int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pB bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, APersistableBundle* _Nullable* _Nonnull outBundle) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -614,7 +633,7 @@ int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBu int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -645,7 +664,8 @@ int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBu int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -676,7 +696,8 @@ int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -708,8 +729,8 @@ int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBun char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -741,8 +762,8 @@ int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBun char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -775,7 +796,7 @@ int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnu int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -808,7 +829,7 @@ int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull p int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -841,7 +862,7 @@ int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -873,7 +894,7 @@ int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnul int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -906,7 +927,7 @@ int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnul int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__); + __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); /** * Get all of the keys associated with this specific type and place it in the @@ -937,6 +958,6 @@ int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnul int32_t APersistableBundle_getPersistableBundleKeys( const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); __END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index a905dff4b1..52edae4a38 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -18,6 +18,7 @@ #include <android/binder_ibinder.h> #include <android/binder_status.h> +#include <android/llndk-versioning.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -252,9 +253,8 @@ void AServiceManager_getUpdatableApexName(const char* instance, void* context, * \return the result of dlopen of the specified HAL */ void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance, - int flag) - // TODO(b/302113279) use __INTRODUCED_LLNDK for vendor variants - __INTRODUCED_IN(__ANDROID_API_V__); + int flag) __INTRODUCED_IN(__ANDROID_API_V__) + __INTRODUCED_IN_LLNDK(202404); /** * Prevent lazy services without client from shutting down their process diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp index 3ee36cd8c3..ca927272f8 100644 --- a/libs/binder/ndk/tests/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -25,6 +25,7 @@ using ::android::wp; const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo"; const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die"; +const char* IFoo::kInstanceNameToDieFor2 = "libbinder_ndk-test-IFoo-to-die2"; const char* IFoo::kIFooDescriptor = "my-special-IFoo-class"; struct IFoo_Class_Data { diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h index 0a562f085d..0cdd50b37a 100644 --- a/libs/binder/ndk/tests/include/iface/iface.h +++ b/libs/binder/ndk/tests/include/iface/iface.h @@ -27,6 +27,7 @@ class IFoo : public virtual ::android::RefBase { public: static const char* kSomeInstanceName; static const char* kInstanceNameToDieFor; + static const char* kInstanceNameToDieFor2; static const char* kIFooDescriptor; static AIBinder_Class* kClass; diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 966ec959b6..ce63b828cb 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -536,6 +536,7 @@ TEST(NdkBinder, DeathRecipient) { bool deathReceived = false; std::function<void(void)> onDeath = [&] { + std::unique_lock<std::mutex> lockDeath(deathMutex); std::cerr << "Binder died (as requested)." << std::endl; deathReceived = true; deathCv.notify_one(); @@ -547,6 +548,7 @@ TEST(NdkBinder, DeathRecipient) { bool wasDeathReceivedFirst = false; std::function<void(void)> onUnlink = [&] { + std::unique_lock<std::mutex> lockUnlink(unlinkMutex); std::cerr << "Binder unlinked (as requested)." << std::endl; wasDeathReceivedFirst = deathReceived; unlinkReceived = true; @@ -560,7 +562,6 @@ TEST(NdkBinder, DeathRecipient) { EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie))); - // the binder driver should return this if the service dies during the transaction EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); foo = nullptr; @@ -579,6 +580,123 @@ TEST(NdkBinder, DeathRecipient) { binder = nullptr; } +TEST(NdkBinder, DeathRecipientDropBinderNoDeath) { + using namespace std::chrono_literals; + + std::mutex deathMutex; + std::condition_variable deathCv; + bool deathReceived = false; + + std::function<void(void)> onDeath = [&] { + std::unique_lock<std::mutex> lockDeath(deathMutex); + std::cerr << "Binder died (as requested)." << std::endl; + deathReceived = true; + deathCv.notify_one(); + }; + + std::mutex unlinkMutex; + std::condition_variable unlinkCv; + bool unlinkReceived = false; + bool wasDeathReceivedFirst = false; + + std::function<void(void)> onUnlink = [&] { + std::unique_lock<std::mutex> lockUnlink(unlinkMutex); + std::cerr << "Binder unlinked (as requested)." << std::endl; + wasDeathReceivedFirst = deathReceived; + unlinkReceived = true; + unlinkCv.notify_one(); + }; + + // keep the death recipient around + ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath)); + AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink); + + { + AIBinder* binder; + sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder); + ASSERT_NE(nullptr, foo.get()); + ASSERT_NE(nullptr, binder); + + DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink}; + + EXPECT_EQ(STATUS_OK, + AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie))); + // let the sp<IFoo> and AIBinder fall out of scope + AIBinder_decStrong(binder); + binder = nullptr; + } + + { + std::unique_lock<std::mutex> lockDeath(deathMutex); + EXPECT_FALSE(deathCv.wait_for(lockDeath, 100ms, [&] { return deathReceived; })); + EXPECT_FALSE(deathReceived); + } + + { + std::unique_lock<std::mutex> lockUnlink(unlinkMutex); + EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; })); + EXPECT_TRUE(unlinkReceived); + EXPECT_FALSE(wasDeathReceivedFirst); + } +} + +TEST(NdkBinder, DeathRecipientDropBinderOnDied) { + using namespace std::chrono_literals; + + std::mutex deathMutex; + std::condition_variable deathCv; + bool deathReceived = false; + + sp<IFoo> foo; + AIBinder* binder; + std::function<void(void)> onDeath = [&] { + std::unique_lock<std::mutex> lockDeath(deathMutex); + std::cerr << "Binder died (as requested)." << std::endl; + deathReceived = true; + AIBinder_decStrong(binder); + binder = nullptr; + deathCv.notify_one(); + }; + + std::mutex unlinkMutex; + std::condition_variable unlinkCv; + bool unlinkReceived = false; + bool wasDeathReceivedFirst = false; + + std::function<void(void)> onUnlink = [&] { + std::unique_lock<std::mutex> lockUnlink(unlinkMutex); + std::cerr << "Binder unlinked (as requested)." << std::endl; + wasDeathReceivedFirst = deathReceived; + unlinkReceived = true; + unlinkCv.notify_one(); + }; + + ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath)); + AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink); + + foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder); + ASSERT_NE(nullptr, foo.get()); + ASSERT_NE(nullptr, binder); + + DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink}; + EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie))); + + EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); + + { + std::unique_lock<std::mutex> lockDeath(deathMutex); + EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; })); + EXPECT_TRUE(deathReceived); + } + + { + std::unique_lock<std::mutex> lockUnlink(unlinkMutex); + EXPECT_TRUE(deathCv.wait_for(lockUnlink, 100ms, [&] { return unlinkReceived; })); + EXPECT_TRUE(unlinkReceived); + EXPECT_TRUE(wasDeathReceivedFirst); + } +} + TEST(NdkBinder, RetrieveNonNdkService) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -958,6 +1076,10 @@ int main(int argc, char* argv[]) { } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); + return manualThreadPoolService(IFoo::kInstanceNameToDieFor2); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); return manualPollingService(IFoo::kSomeInstanceName); } if (fork() == 0) { diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 54290cd629..0c8f3fa096 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -425,4 +425,8 @@ int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) { return iter->second; } +const sp<Looper> Choreographer::getLooper() { + return mLooper; +} + } // namespace android diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index 4e12fd330c..535a0218b6 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -100,27 +100,31 @@ bool LayerMetadata::has(uint32_t key) const { int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const { if (!has(key)) return fallback; const std::vector<uint8_t>& data = mMap.at(key); - if (data.size() < sizeof(uint32_t)) return fallback; - Parcel p; - p.setData(data.data(), data.size()); - return p.readInt32(); + + // TODO: should handle when not equal? + if (data.size() < sizeof(int32_t)) return fallback; + + int32_t result; + memcpy(&result, data.data(), sizeof(result)); + return result; } void LayerMetadata::setInt32(uint32_t key, int32_t value) { std::vector<uint8_t>& data = mMap[key]; - Parcel p; - p.writeInt32(value); - data.resize(p.dataSize()); - memcpy(data.data(), p.data(), p.dataSize()); + data.resize(sizeof(value)); + memcpy(data.data(), &value, sizeof(value)); } std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const { if (!has(key)) return std::nullopt; const std::vector<uint8_t>& data = mMap.at(key); + + // TODO: should handle when not equal? if (data.size() < sizeof(int64_t)) return std::nullopt; - Parcel p; - p.setData(data.data(), data.size()); - return p.readInt64(); + + int64_t result; + memcpy(&result, data.data(), sizeof(result)); + return result; } std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 1e0aacddab..0a2879975b 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -90,7 +90,6 @@ layer_state_t::layer_state_t() fixedTransformHint(ui::Transform::ROT_INVALID), autoRefresh(false), isTrustedOverlay(false), - borderEnabled(false), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), dropInputMode(gui::DropInputMode::NONE) { @@ -122,12 +121,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, transparentRegion); SAFE_PARCEL(output.writeUint32, bufferTransform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); - SAFE_PARCEL(output.writeBool, borderEnabled); - SAFE_PARCEL(output.writeFloat, borderWidth); - SAFE_PARCEL(output.writeFloat, borderColor.r); - SAFE_PARCEL(output.writeFloat, borderColor.g); - SAFE_PARCEL(output.writeFloat, borderColor.b); - SAFE_PARCEL(output.writeFloat, borderColor.a); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); SAFE_PARCEL(output.write, surfaceDamageRegion); @@ -238,17 +231,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, transparentRegion); SAFE_PARCEL(input.readUint32, &bufferTransform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); - SAFE_PARCEL(input.readBool, &borderEnabled); - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderWidth = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.r = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.g = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.b = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.a = tmpFloat; uint32_t tmpUint32 = 0; SAFE_PARCEL(input.readUint32, &tmpUint32); @@ -659,12 +641,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eShadowRadiusChanged; shadowRadius = other.shadowRadius; } - if (other.what & eRenderBorderChanged) { - what |= eRenderBorderChanged; - borderEnabled = other.borderEnabled; - borderWidth = other.borderWidth; - borderColor = other.borderColor; - } if (other.what & eDefaultFrameRateCompatibilityChanged) { what |= eDefaultFrameRateCompatibilityChanged; defaultFrameRateCompatibility = other.defaultFrameRateCompatibility; @@ -794,7 +770,6 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace); if (other.what & eMetadataChanged) diff |= eMetadataChanged; CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius); - CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor); CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility); CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority); CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility, diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 4f1356bebb..1d2ea3ea02 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2250,23 +2250,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::enableBorder( - const sp<SurfaceControl>& sc, bool shouldEnable, float width, const half4& color) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - - s->what |= layer_state_t::eRenderBorderChanged; - s->borderEnabled = shouldEnable; - s->borderWidth = width; - s->borderColor = color; - - registerSurfaceControlForCallback(sc); - return *this; -} - // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 86bf0ee745..ad0d99d11e 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -73,14 +73,6 @@ void WindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } -bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { - return touchableRegion.contains(x, y); -} - -bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frame.left && x < frame.right && y >= frame.top && y < frame.bottom; -} - bool WindowInfo::supportsSplitTouch() const { return !inputConfig.test(InputConfig::PREVENT_SPLITTING); } diff --git a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl index af138c7539..13962fee5d 100644 --- a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl +++ b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl @@ -42,6 +42,17 @@ parcelable DisplayModeSpecs { } /** + * Refers to the time after which the idle screen's refresh rate is to be reduced + */ + parcelable IdleScreenRefreshRateConfig { + + /** + * The timeout value in milli seconds + */ + int timeoutMillis; + } + + /** * Base mode ID. This is what system defaults to for all other settings, or * if the refresh rate range is not available. */ @@ -72,4 +83,13 @@ parcelable DisplayModeSpecs { * never smaller. */ RefreshRateRanges appRequestRanges; + + /** + * The config to represent the maximum time (in ms) for which the display can remain in an idle + * state before reducing the refresh rate to conserve power. + * Null value refers that the device is not configured to dynamically reduce the refresh rate + * based on external conditions. + * -1 refers to the current conditions requires no timeout + */ + @nullable IdleScreenRefreshRateConfig idleScreenRefreshRateConfig; } diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index fc79b03c23..2e5aa4a893 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -109,6 +109,7 @@ public: virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); int64_t getFrameInterval() const; bool inCallback() const; + const sp<Looper> getLooper(); private: Choreographer(const Choreographer&) = delete; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 0fedea7b9e..ca7acf9be4 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -179,7 +179,6 @@ struct layer_state_t { eCachingHintChanged = 0x00000200, eDimmingEnabledChanged = 0x00000400, eShadowRadiusChanged = 0x00000800, - eRenderBorderChanged = 0x00001000, eBufferCropChanged = 0x00002000, eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, @@ -258,8 +257,8 @@ struct layer_state_t { layer_state_t::eBlurRegionsChanged | layer_state_t::eColorChanged | layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged | - layer_state_t::eHdrMetadataChanged | layer_state_t::eRenderBorderChanged | - layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged; + layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged | + layer_state_t::eStretchChanged; // Changes which invalidates the layer's visible region in CE. static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES | @@ -388,11 +387,6 @@ struct layer_state_t { // should be trusted for input occlusion detection purposes bool isTrustedOverlay; - // Flag to indicate if border needs to be enabled on the layer - bool borderEnabled; - float borderWidth; - half4 borderColor; - // Stretch effect to be applied to this layer StretchEffect stretchEffect; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 288882695a..79224e6782 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -744,9 +744,6 @@ public: const Rect& destinationFrame); Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode); - Transaction& enableBorder(const sp<SurfaceControl>& sc, bool shouldEnable, float width, - const half4& color); - status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 32d60be612..2d1b51a418 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -254,10 +254,6 @@ struct WindowInfo : public Parcelable { void addTouchableRegion(const Rect& region); - bool touchableRegionContainsPoint(int32_t x, int32_t y) const; - - bool frameContainsPoint(int32_t x, int32_t y) const; - bool supportsSplitTouch() const; bool isSpy() const; diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 65e93a9a87..3278c23b6f 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -205,6 +205,7 @@ cc_library { "AccelerationCurve.cpp", "Input.cpp", "InputConsumer.cpp", + "InputConsumerNoResampling.cpp", "InputDevice.cpp", "InputEventLabels.cpp", "InputTransport.cpp", diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp new file mode 100644 index 0000000000..52acb51910 --- /dev/null +++ b/libs/input/InputConsumerNoResampling.cpp @@ -0,0 +1,540 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputTransport" +#define ATRACE_TAG ATRACE_TAG_INPUT + +#include <inttypes.h> + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <cutils/properties.h> +#include <ftl/enum.h> +#include <utils/Trace.h> + +#include <com_android_input_flags.h> +#include <input/InputConsumerNoResampling.h> +#include <input/PrintTools.h> +#include <input/TraceTools.h> + +namespace input_flags = com::android::input::flags; + +namespace android { + +namespace { + +/** + * Log debug messages relating to the consumer end of the transport channel. + * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) + */ +const bool DEBUG_TRANSPORT_CONSUMER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO); + +std::unique_ptr<KeyEvent> createKeyEvent(const InputMessage& msg) { + std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>(); + event->initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source, + msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action, + msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode, + msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime, + msg.body.key.eventTime); + return event; +} + +std::unique_ptr<FocusEvent> createFocusEvent(const InputMessage& msg) { + std::unique_ptr<FocusEvent> event = std::make_unique<FocusEvent>(); + event->initialize(msg.body.focus.eventId, msg.body.focus.hasFocus); + return event; +} + +std::unique_ptr<CaptureEvent> createCaptureEvent(const InputMessage& msg) { + std::unique_ptr<CaptureEvent> event = std::make_unique<CaptureEvent>(); + event->initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled); + return event; +} + +std::unique_ptr<DragEvent> createDragEvent(const InputMessage& msg) { + std::unique_ptr<DragEvent> event = std::make_unique<DragEvent>(); + event->initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y, + msg.body.drag.isExiting); + return event; +} + +std::unique_ptr<MotionEvent> createMotionEvent(const InputMessage& msg) { + std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>(); + const uint32_t pointerCount = msg.body.motion.pointerCount; + std::vector<PointerProperties> pointerProperties; + pointerProperties.reserve(pointerCount); + std::vector<PointerCoords> pointerCoords; + pointerCoords.reserve(pointerCount); + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties.push_back(msg.body.motion.pointers[i].properties); + pointerCoords.push_back(msg.body.motion.pointers[i].coords); + } + + ui::Transform transform; + transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx, + msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1}); + ui::Transform displayTransform; + displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw, + msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw, + 0, 0, 1}); + event->initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source, + msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action, + msg.body.motion.actionButton, msg.body.motion.flags, + msg.body.motion.edgeFlags, msg.body.motion.metaState, + msg.body.motion.buttonState, msg.body.motion.classification, transform, + msg.body.motion.xPrecision, msg.body.motion.yPrecision, + msg.body.motion.xCursorPosition, msg.body.motion.yCursorPosition, + displayTransform, msg.body.motion.downTime, msg.body.motion.eventTime, + pointerCount, pointerProperties.data(), pointerCoords.data()); + return event; +} + +void addSample(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + std::vector<PointerCoords> pointerCoords; + pointerCoords.reserve(pointerCount); + for (uint32_t i = 0; i < pointerCount; i++) { + pointerCoords.push_back(msg.body.motion.pointers[i].coords); + } + + // TODO(b/329770983): figure out if it's safe to combine events with mismatching metaState + event.setMetaState(event.getMetaState() | msg.body.motion.metaState); + event.addSample(msg.body.motion.eventTime, pointerCoords.data()); +} + +std::unique_ptr<TouchModeEvent> createTouchModeEvent(const InputMessage& msg) { + std::unique_ptr<TouchModeEvent> event = std::make_unique<TouchModeEvent>(); + event->initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode); + return event; +} + +std::string outboundMessageToString(const InputMessage& outboundMsg) { + switch (outboundMsg.header.type) { + case InputMessage::Type::FINISHED: { + return android::base::StringPrintf(" Finish: seq=%" PRIu32 " handled=%s", + outboundMsg.header.seq, + toString(outboundMsg.body.finished.handled)); + } + case InputMessage::Type::TIMELINE: { + return android::base:: + StringPrintf(" Timeline: inputEventId=%" PRId32 " gpuCompletedTime=%" PRId64 + ", presentTime=%" PRId64, + outboundMsg.body.timeline.eventId, + outboundMsg.body.timeline + .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], + outboundMsg.body.timeline + .graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); + } + default: { + LOG(FATAL) << "Outbound message must be FINISHED or TIMELINE, got " + << ftl::enum_string(outboundMsg.header.type); + return "Unreachable"; + } + } +} + +InputMessage createFinishedMessage(uint32_t seq, bool handled, nsecs_t consumeTime) { + InputMessage msg; + msg.header.type = InputMessage::Type::FINISHED; + msg.header.seq = seq; + msg.body.finished.handled = handled; + msg.body.finished.consumeTime = consumeTime; + return msg; +} + +InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTime, + nsecs_t presentTime) { + InputMessage msg; + msg.header.type = InputMessage::Type::TIMELINE; + msg.header.seq = 0; + msg.body.timeline.eventId = inputEventId; + msg.body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = gpuCompletedTime; + msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime; + return msg; +} + +} // namespace + +using android::base::Result; +using android::base::StringPrintf; + +// --- InputConsumerNoResampling --- + +InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, + sp<Looper> looper, + InputConsumerCallbacks& callbacks) + : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) { + LOG_ALWAYS_FATAL_IF(mLooper == nullptr); + mCallback = sp<LooperEventCallback>::make( + std::bind(&InputConsumerNoResampling::handleReceiveCallback, this, + std::placeholders::_1)); + // In the beginning, there are no pending outbounds events; we only care about receiving + // incoming data. + setFdEvents(ALOOPER_EVENT_INPUT); +} + +InputConsumerNoResampling::~InputConsumerNoResampling() { + ensureCalledOnLooperThread(__func__); + consumeBatchedInputEvents(std::nullopt); + while (!mOutboundQueue.empty()) { + processOutboundEvents(); + // This is our last chance to ack the events. If we don't ack them here, we will get an ANR, + // so keep trying to send the events as long as they are present in the queue. + } + setFdEvents(0); +} + +int InputConsumerNoResampling::handleReceiveCallback(int events) { + // Allowed return values of this function as documented in LooperCallback::handleEvent + constexpr int REMOVE_CALLBACK = 0; + constexpr int KEEP_CALLBACK = 1; + + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { + // This error typically occurs when the publisher has closed the input channel + // as part of removing a window or finishing an IME session, in which case + // the consumer will soon be disposed as well. + if (DEBUG_TRANSPORT_CONSUMER) { + LOG(INFO) << "The channel was hung up or an error occurred: " << mChannel->getName(); + } + return REMOVE_CALLBACK; + } + + int handledEvents = 0; + if (events & ALOOPER_EVENT_INPUT) { + std::vector<InputMessage> messages = readAllMessages(); + handleMessages(std::move(messages)); + handledEvents |= ALOOPER_EVENT_INPUT; + } + + if (events & ALOOPER_EVENT_OUTPUT) { + processOutboundEvents(); + handledEvents |= ALOOPER_EVENT_OUTPUT; + } + if (handledEvents != events) { + LOG(FATAL) << "Mismatch: handledEvents=" << handledEvents << ", events=" << events; + } + return KEEP_CALLBACK; +} + +void InputConsumerNoResampling::processOutboundEvents() { + while (!mOutboundQueue.empty()) { + const InputMessage& outboundMsg = mOutboundQueue.front(); + + const status_t result = mChannel->sendMessage(&outboundMsg); + if (result == OK) { + if (outboundMsg.header.type == InputMessage::Type::FINISHED) { + ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/outboundMsg.header.seq); + } + // Successful send. Erase the entry and keep trying to send more + mOutboundQueue.pop(); + continue; + } + + // Publisher is busy, try again later. Keep this entry (do not erase) + if (result == WOULD_BLOCK) { + setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT); + return; // try again later + } + + // Some other error. Give up + LOG(FATAL) << "Failed to send outbound event on channel '" << mChannel->getName() + << "'. status=" << statusToString(result) << "(" << result << ")"; + } + + // The queue is now empty. Tell looper there's no more output to expect. + setFdEvents(ALOOPER_EVENT_INPUT); +} + +void InputConsumerNoResampling::finishInputEvent(uint32_t seq, bool handled) { + ensureCalledOnLooperThread(__func__); + mOutboundQueue.push(createFinishedMessage(seq, handled, popConsumeTime(seq))); + // also produce finish events for all batches for this seq (if any) + const auto it = mBatchedSequenceNumbers.find(seq); + if (it != mBatchedSequenceNumbers.end()) { + for (uint32_t subSeq : it->second) { + mOutboundQueue.push(createFinishedMessage(subSeq, handled, popConsumeTime(subSeq))); + } + mBatchedSequenceNumbers.erase(it); + } + processOutboundEvents(); +} + +bool InputConsumerNoResampling::probablyHasInput() const { + // Ideally, this would only be allowed to run on the looper thread, and in production, it will. + // However, for testing, it's convenient to call this while the looper thread is blocked, so + // we do not call ensureCalledOnLooperThread here. + return (!mBatches.empty()) || mChannel->probablyHasInput(); +} + +void InputConsumerNoResampling::reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, + nsecs_t presentTime) { + ensureCalledOnLooperThread(__func__); + mOutboundQueue.push(createTimelineMessage(inputEventId, gpuCompletedTime, presentTime)); + processOutboundEvents(); +} + +nsecs_t InputConsumerNoResampling::popConsumeTime(uint32_t seq) { + auto it = mConsumeTimes.find(seq); + // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was + // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed. + LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32, + seq); + nsecs_t consumeTime = it->second; + mConsumeTimes.erase(it); + return consumeTime; +} + +void InputConsumerNoResampling::setFdEvents(int events) { + if (mFdEvents != events) { + mFdEvents = events; + if (events != 0) { + mLooper->addFd(mChannel->getFd(), 0, events, mCallback, nullptr); + } else { + mLooper->removeFd(mChannel->getFd()); + } + } +} + +void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messages) { + // TODO(b/297226446) : add resampling + for (const InputMessage& msg : messages) { + if (msg.header.type == InputMessage::Type::MOTION) { + const int32_t action = msg.body.motion.action; + const DeviceId deviceId = msg.body.motion.deviceId; + const int32_t source = msg.body.motion.source; + const bool batchableEvent = (action == AMOTION_EVENT_ACTION_MOVE || + action == AMOTION_EVENT_ACTION_HOVER_MOVE) && + (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER) || + isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK)); + if (batchableEvent) { + // add it to batch + mBatches[deviceId].emplace(msg); + } else { + // consume all pending batches for this event immediately + // TODO(b/329776327): figure out if this could be smarter by limiting the + // consumption only to the current device. + consumeBatchedInputEvents(std::nullopt); + handleMessage(msg); + } + } else { + // Non-motion events shouldn't force the consumption of pending batched events + handleMessage(msg); + } + } + // At the end of this, if we still have pending batches, notify the receiver about it. + + // We need to carefully notify the InputConsumerCallbacks about the pending batch. The receiver + // could choose to consume all events when notified about the batch. That means that the + // "mBatches" variable could change when 'InputConsumerCallbacks::onBatchedInputEventPending' is + // invoked. We also can't notify the InputConsumerCallbacks in a while loop until mBatches is + // empty, because the receiver could choose to not consume the batch immediately. + std::set<int32_t> pendingBatchSources; + for (const auto& [_, pendingMessages] : mBatches) { + // Assume that all messages for a given device has the same source. + pendingBatchSources.insert(pendingMessages.front().body.motion.source); + } + for (const int32_t source : pendingBatchSources) { + const bool sourceStillRemaining = + std::any_of(mBatches.begin(), mBatches.end(), [=](const auto& pair) { + return pair.second.front().body.motion.source == source; + }); + if (sourceStillRemaining) { + mCallbacks.onBatchedInputEventPending(source); + } + } +} + +std::vector<InputMessage> InputConsumerNoResampling::readAllMessages() { + std::vector<InputMessage> messages; + while (true) { + InputMessage msg; + status_t result = mChannel->receiveMessage(&msg); + switch (result) { + case OK: { + const auto [_, inserted] = + mConsumeTimes.emplace(msg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, + msg.header.seq); + + // Trace the event processing timeline - event was just read from the socket + // TODO(b/329777420): distinguish between multiple instances of InputConsumer + // in the same process. + ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/msg.header.seq); + messages.push_back(msg); + break; + } + case WOULD_BLOCK: { + return messages; + } + case DEAD_OBJECT: { + LOG(FATAL) << "Got a dead object for " << mChannel->getName(); + break; + } + case BAD_VALUE: { + LOG(FATAL) << "Got a bad value for " << mChannel->getName(); + break; + } + default: { + LOG(FATAL) << "Unexpected error: " << result; + break; + } + } + } +} + +void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const { + switch (msg.header.type) { + case InputMessage::Type::KEY: { + std::unique_ptr<KeyEvent> keyEvent = createKeyEvent(msg); + mCallbacks.onKeyEvent(std::move(keyEvent), msg.header.seq); + break; + } + + case InputMessage::Type::MOTION: { + std::unique_ptr<MotionEvent> motionEvent = createMotionEvent(msg); + mCallbacks.onMotionEvent(std::move(motionEvent), msg.header.seq); + break; + } + + case InputMessage::Type::FINISHED: + case InputMessage::Type::TIMELINE: { + LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " + "InputConsumer on %s", + ftl::enum_string(msg.header.type).c_str(), + mChannel->getName().c_str()); + break; + } + + case InputMessage::Type::FOCUS: { + std::unique_ptr<FocusEvent> focusEvent = createFocusEvent(msg); + mCallbacks.onFocusEvent(std::move(focusEvent), msg.header.seq); + break; + } + + case InputMessage::Type::CAPTURE: { + std::unique_ptr<CaptureEvent> captureEvent = createCaptureEvent(msg); + mCallbacks.onCaptureEvent(std::move(captureEvent), msg.header.seq); + break; + } + + case InputMessage::Type::DRAG: { + std::unique_ptr<DragEvent> dragEvent = createDragEvent(msg); + mCallbacks.onDragEvent(std::move(dragEvent), msg.header.seq); + break; + } + + case InputMessage::Type::TOUCH_MODE: { + std::unique_ptr<TouchModeEvent> touchModeEvent = createTouchModeEvent(msg); + mCallbacks.onTouchModeEvent(std::move(touchModeEvent), msg.header.seq); + break; + } + } +} + +bool InputConsumerNoResampling::consumeBatchedInputEvents( + std::optional<nsecs_t> requestedFrameTime) { + ensureCalledOnLooperThread(__func__); + // When batching is not enabled, we want to consume all events. That's equivalent to having an + // infinite frameTime. + const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); + bool producedEvents = false; + for (auto& [deviceId, messages] : mBatches) { + std::unique_ptr<MotionEvent> motion; + std::optional<uint32_t> firstSeqForBatch; + std::vector<uint32_t> sequences; + while (!messages.empty()) { + const InputMessage& msg = messages.front(); + if (msg.body.motion.eventTime > frameTime) { + break; + } + if (motion == nullptr) { + motion = createMotionEvent(msg); + firstSeqForBatch = msg.header.seq; + const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}}); + if (!inserted) { + LOG(FATAL) << "The sequence " << msg.header.seq << " was already present!"; + } + } else { + addSample(*motion, msg); + mBatchedSequenceNumbers[*firstSeqForBatch].push_back(msg.header.seq); + } + messages.pop(); + } + if (motion != nullptr) { + LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value()); + mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch); + producedEvents = true; + } else { + // This is OK, it just means that the frameTime is too old (all events that we have + // pending are in the future of the frametime). Maybe print a + // warning? If there are multiple devices active though, this might be normal and can + // just be ignored, unless none of them resulted in any consumption (in that case, this + // function would already return "false" so we could just leave it up to the caller). + } + } + std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); }); + return producedEvents; +} + +void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const { + sp<Looper> callingThreadLooper = Looper::getForThread(); + if (callingThreadLooper != mLooper) { + LOG(FATAL) << "The function " << func << " can only be called on the looper thread"; + } +} + +std::string InputConsumerNoResampling::dump() const { + ensureCalledOnLooperThread(__func__); + std::string out; + if (mOutboundQueue.empty()) { + out += "mOutboundQueue: <empty>\n"; + } else { + out += "mOutboundQueue:\n"; + // Make a copy of mOutboundQueue for printing destructively. Unfortunately std::queue + // doesn't provide a good way to iterate over the entire container. + std::queue<InputMessage> tmpQueue = mOutboundQueue; + while (!tmpQueue.empty()) { + out += std::string(" ") + outboundMessageToString(tmpQueue.front()) + "\n"; + tmpQueue.pop(); + } + } + + if (mBatches.empty()) { + out += "mBatches: <empty>\n"; + } else { + out += "mBatches:\n"; + for (const auto& [deviceId, messages] : mBatches) { + out += " Device id "; + out += std::to_string(deviceId); + out += ":\n"; + // Make a copy of mOutboundQueue for printing destructively. Unfortunately std::queue + // doesn't provide a good way to iterate over the entire container. + std::queue<InputMessage> tmpQueue = messages; + while (!tmpQueue.empty()) { + LOG_ALWAYS_FATAL_IF(tmpQueue.front().header.type != InputMessage::Type::MOTION); + std::unique_ptr<MotionEvent> motion = createMotionEvent(tmpQueue.front()); + out += std::string(" ") + streamableToString(*motion) + "\n"; + tmpQueue.pop(); + } + } + } + + return out; +} + +} // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 93af4c2066..e67a65a114 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -19,6 +19,7 @@ cc_test { "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", + "InputPublisherAndConsumerNoResampling_test.cpp", "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp new file mode 100644 index 0000000000..6593497763 --- /dev/null +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -0,0 +1,813 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <attestation/HmacKeyManager.h> +#include <ftl/enum.h> +#include <gtest/gtest.h> +#include <gui/constants.h> +#include <input/BlockingQueue.h> +#include <input/InputConsumerNoResampling.h> +#include <input/InputTransport.h> + +using android::base::Result; + +namespace android { + +namespace { + +static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; +static constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE; +static constexpr int32_t POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +static constexpr int32_t POINTER_2_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static auto constexpr TIMEOUT = 5s; + +struct Pointer { + int32_t id; + float x; + float y; + bool isResampled = false; +}; + +// A collection of arguments to be sent as publishMotionEvent(). The saved members of this struct +// allow to check the expectations against the event acquired from the InputConsumerCallbacks. To +// help simplify expectation checking it carries members not present in MotionEvent, like +// |rawXScale|. +struct PublishMotionArgs { + const int32_t action; + const nsecs_t downTime; + const uint32_t seq; + const int32_t eventId; + const int32_t deviceId = 1; + const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; + const int32_t displayId = ADISPLAY_ID_DEFAULT; + const int32_t actionButton = 0; + const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + const MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + const float xScale = 2; + const float yScale = 3; + const float xOffset = -10; + const float yOffset = -20; + const float rawXScale = 4; + const float rawYScale = -5; + const float rawXOffset = -11; + const float rawYOffset = 42; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const float xCursorPosition = 1.3; + const float yCursorPosition = 50.6; + std::array<uint8_t, 32> hmac; + int32_t flags; + ui::Transform transform; + ui::Transform rawTransform; + const nsecs_t eventTime; + size_t pointerCount; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; + + PublishMotionArgs(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers, + const uint32_t seq); +}; + +PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, + const std::vector<Pointer>& pointers, const uint32_t inSeq) + : action(inAction), + downTime(inDownTime), + seq(inSeq), + eventId(InputEvent::nextId()), + eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) { + hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + if (action == AMOTION_EVENT_ACTION_CANCEL) { + flags |= AMOTION_EVENT_FLAG_CANCELED; + } + pointerCount = pointers.size(); + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties.push_back({}); + pointerProperties[i].clear(); + pointerProperties[i].id = pointers[i].id; + pointerProperties[i].toolType = ToolType::FINGER; + + pointerCoords.push_back({}); + pointerCoords[i].clear(); + pointerCoords[i].isResampled = pointers[i].isResampled; + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); + } + transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); + rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); +} + +// Checks expectations against |motionEvent| acquired from an InputConsumer. Floating point +// comparisons limit precision to EPSILON. +void verifyArgsEqualToEvent(const PublishMotionArgs& args, const MotionEvent& motionEvent) { + EXPECT_EQ(args.eventId, motionEvent.getId()); + EXPECT_EQ(args.deviceId, motionEvent.getDeviceId()); + EXPECT_EQ(args.source, motionEvent.getSource()); + EXPECT_EQ(args.displayId, motionEvent.getDisplayId()); + EXPECT_EQ(args.hmac, motionEvent.getHmac()); + EXPECT_EQ(args.action, motionEvent.getAction()); + EXPECT_EQ(args.downTime, motionEvent.getDownTime()); + EXPECT_EQ(args.flags, motionEvent.getFlags()); + EXPECT_EQ(args.edgeFlags, motionEvent.getEdgeFlags()); + EXPECT_EQ(args.metaState, motionEvent.getMetaState()); + EXPECT_EQ(args.buttonState, motionEvent.getButtonState()); + EXPECT_EQ(args.classification, motionEvent.getClassification()); + EXPECT_EQ(args.transform, motionEvent.getTransform()); + EXPECT_NEAR((-args.rawXOffset / args.rawXScale) * args.xScale + args.xOffset, + motionEvent.getRawXOffset(), EPSILON); + EXPECT_NEAR((-args.rawYOffset / args.rawYScale) * args.yScale + args.yOffset, + motionEvent.getRawYOffset(), EPSILON); + EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision()); + EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision()); + EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON); + EXPECT_NEAR(args.yCursorPosition, motionEvent.getRawYCursorPosition(), EPSILON); + EXPECT_NEAR(args.xCursorPosition * args.xScale + args.xOffset, motionEvent.getXCursorPosition(), + EPSILON); + EXPECT_NEAR(args.yCursorPosition * args.yScale + args.yOffset, motionEvent.getYCursorPosition(), + EPSILON); + EXPECT_EQ(args.rawTransform, motionEvent.getRawTransform()); + EXPECT_EQ(args.eventTime, motionEvent.getEventTime()); + EXPECT_EQ(args.pointerCount, motionEvent.getPointerCount()); + EXPECT_EQ(0U, motionEvent.getHistorySize()); + + for (size_t i = 0; i < args.pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(args.pointerProperties[i].id, motionEvent.getPointerId(i)); + EXPECT_EQ(args.pointerProperties[i].toolType, motionEvent.getToolType(i)); + + const auto& pc = args.pointerCoords[i]; + EXPECT_EQ(pc, motionEvent.getSamplePointerCoords()[i]); + + EXPECT_NEAR(pc.getX() * args.rawXScale + args.rawXOffset, motionEvent.getRawX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.rawYScale + args.rawYOffset, motionEvent.getRawY(i), EPSILON); + EXPECT_NEAR(pc.getX() * args.xScale + args.xOffset, motionEvent.getX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.yScale + args.yOffset, motionEvent.getY(i), EPSILON); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent.getPressure(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent.getSize(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent.getTouchMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent.getTouchMinor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent.getToolMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent.getToolMinor(i)); + + // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is + // "up", and the positive y direction is "down". + const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + const float x = sinf(unscaledOrientation) * args.xScale; + const float y = -cosf(unscaledOrientation) * args.yScale; + EXPECT_EQ(atan2f(x, -y), motionEvent.getOrientation(i)); + } +} + +void publishMotionEvent(InputPublisher& publisher, const PublishMotionArgs& a) { + status_t status = + publisher.publishMotionEvent(a.seq, a.eventId, a.deviceId, a.source, a.displayId, + a.hmac, a.action, a.actionButton, a.flags, a.edgeFlags, + a.metaState, a.buttonState, a.classification, a.transform, + a.xPrecision, a.yPrecision, a.xCursorPosition, + a.yCursorPosition, a.rawTransform, a.downTime, a.eventTime, + a.pointerCount, a.pointerProperties.data(), + a.pointerCoords.data()); + ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; +} + +Result<InputPublisher::ConsumerResponse> receiveConsumerResponse( + InputPublisher& publisher, std::chrono::milliseconds timeout) { + const std::chrono::time_point start = std::chrono::steady_clock::now(); + + while (true) { + Result<InputPublisher::ConsumerResponse> result = publisher.receiveConsumerResponse(); + if (result.ok()) { + return result; + } + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; + if (waited > timeout) { + return result; + } + } +} + +void verifyFinishedSignal(InputPublisher& publisher, uint32_t seq, nsecs_t publishTime) { + Result<InputPublisher::ConsumerResponse> result = receiveConsumerResponse(publisher, TIMEOUT); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse returned " << result.error().message(); + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + +} // namespace + +class InputConsumerMessageHandler : public MessageHandler { +public: + InputConsumerMessageHandler(std::function<void(const Message&)> function) + : mFunction(function) {} + +private: + void handleMessage(const Message& message) override { mFunction(message); } + + std::function<void(const Message&)> mFunction; +}; + +class InputPublisherAndConsumerNoResamplingTest : public testing::Test, + public InputConsumerCallbacks { +protected: + std::unique_ptr<InputChannel> mClientChannel; + std::unique_ptr<InputPublisher> mPublisher; + std::unique_ptr<InputConsumerNoResampling> mConsumer; + + std::thread mLooperThread; + sp<Looper> mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false); + + // LOOPER CONTROL + // Set to false when you want the looper to exit + std::atomic<bool> mExitLooper = false; + std::mutex mLock; + + // Used by test to notify looper that the value of "mLooperMayProceed" has changed + std::condition_variable mNotifyLooperMayProceed; + bool mLooperMayProceed GUARDED_BY(mLock){true}; + // Used by looper to notify the test that it's about to block on "mLooperMayProceed" -> true + std::condition_variable mNotifyLooperWaiting; + bool mLooperIsBlocked GUARDED_BY(mLock){false}; + + std::condition_variable mNotifyConsumerDestroyed; + bool mConsumerDestroyed GUARDED_BY(mLock){false}; + + void runLooper() { + static constexpr int LOOP_INDEFINITELY = -1; + Looper::setForThread(mLooper); + // Loop forever -- this thread is dedicated to servicing the looper callbacks. + while (!mExitLooper) { + mLooper->pollOnce(/*timeoutMillis=*/LOOP_INDEFINITELY); + } + } + + void SetUp() override { + std::unique_ptr<InputChannel> serverChannel; + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, mClientChannel); + ASSERT_EQ(OK, result); + + mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel)); + mMessageHandler = sp<InputConsumerMessageHandler>::make( + [this](const Message& message) { handleMessage(message); }); + mLooperThread = std::thread([this] { runLooper(); }); + sendMessage(LooperMessage::CREATE_CONSUMER); + } + + void publishAndConsumeKeyEvent(); + void publishAndConsumeMotionStream(); + void publishAndConsumeMotionDown(nsecs_t downTime); + void publishAndConsumeBatchedMotionMove(nsecs_t downTime); + void publishAndConsumeFocusEvent(); + void publishAndConsumeCaptureEvent(); + void publishAndConsumeDragEvent(); + void publishAndConsumeTouchModeEvent(); + void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime, + const std::vector<Pointer>& pointers); + void TearDown() override { + // Destroy the consumer, flushing any of the pending ack's. + sendMessage(LooperMessage::DESTROY_CONSUMER); + { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + mNotifyConsumerDestroyed.wait(lock, [this] { return mConsumerDestroyed; }); + } + // Stop the looper thread so that we can destroy the object. + mExitLooper = true; + mLooper->wake(); + mLooperThread.join(); + } + +protected: + // Interaction with the looper thread + enum class LooperMessage : int { + CALL_PROBABLY_HAS_INPUT, + CREATE_CONSUMER, + DESTROY_CONSUMER, + CALL_REPORT_TIMELINE, + BLOCK_LOOPER, + }; + void sendMessage(LooperMessage message); + struct ReportTimelineArgs { + int32_t inputEventId; + nsecs_t gpuCompletedTime; + nsecs_t presentTime; + }; + // The input to the function "InputConsumer::reportTimeline". Populated on the test thread and + // accessed on the looper thread. + BlockingQueue<ReportTimelineArgs> mReportTimelineArgs; + // The output of calling "InputConsumer::probablyHasInput()". Populated on the looper thread and + // accessed on the test thread. + BlockingQueue<bool> mProbablyHasInputResponses; + +private: + sp<MessageHandler> mMessageHandler; + void handleMessage(const Message& message); + + static auto constexpr NO_EVENT_TIMEOUT = 10ms; + // The sequence number to use when publishing the next event + uint32_t mSeq = 1; + + BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents; + BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents; + BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents; + BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents; + BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents; + BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents; + + // InputConsumerCallbacks interface + void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override { + mKeyEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override { + mMotionEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onBatchedInputEventPending(int32_t pendingBatchSource) override { + if (!mConsumer->probablyHasInput()) { + ADD_FAILURE() << "should deterministically have input because there is a batch"; + } + mConsumer->consumeBatchedInputEvents(std::nullopt); + }; + void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { + mFocusEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override { + mCaptureEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override { + mDragEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override { + mTouchModeEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; +}; + +void InputPublisherAndConsumerNoResamplingTest::sendMessage(LooperMessage message) { + Message msg{ftl::to_underlying(message)}; + mLooper->sendMessage(mMessageHandler, msg); +} + +void InputPublisherAndConsumerNoResamplingTest::handleMessage(const Message& message) { + switch (static_cast<LooperMessage>(message.what)) { + case LooperMessage::CALL_PROBABLY_HAS_INPUT: { + mProbablyHasInputResponses.push(mConsumer->probablyHasInput()); + break; + } + case LooperMessage::CREATE_CONSUMER: { + mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), + mLooper, *this); + break; + } + case LooperMessage::DESTROY_CONSUMER: { + mConsumer = nullptr; + { + std::unique_lock lock(mLock); + mConsumerDestroyed = true; + } + mNotifyConsumerDestroyed.notify_all(); + break; + } + case LooperMessage::CALL_REPORT_TIMELINE: { + std::optional<ReportTimelineArgs> args = mReportTimelineArgs.pop(); + if (!args.has_value()) { + ADD_FAILURE() << "Couldn't get the 'reportTimeline' args in time"; + return; + } + mConsumer->reportTimeline(args->inputEventId, args->gpuCompletedTime, + args->presentTime); + break; + } + case LooperMessage::BLOCK_LOOPER: { + { + std::unique_lock lock(mLock); + mLooperIsBlocked = true; + } + mNotifyLooperWaiting.notify_all(); + + { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + mNotifyLooperMayProceed.wait(lock, [this] { return mLooperMayProceed; }); + } + + { + std::unique_lock lock(mLock); + mLooperIsBlocked = false; + } + mNotifyLooperWaiting.notify_all(); + break; + } + } +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeKeyEvent() { + status_t status; + + const uint32_t seq = mSeq++; + int32_t eventId = InputEvent::nextId(); + constexpr int32_t deviceId = 1; + constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; + constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; + constexpr int32_t keyCode = AKEYCODE_ENTER; + constexpr int32_t scanCode = 13; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t repeatCount = 1; + constexpr nsecs_t downTime = 3; + constexpr nsecs_t eventTime = 4; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action, + flags, keyCode, scanCode, metaState, repeatCount, downTime, + eventTime); + ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + + std::optional<std::unique_ptr<KeyEvent>> optKeyEvent = mKeyEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optKeyEvent.has_value()) << "consumer should have returned non-NULL event"; + std::unique_ptr<KeyEvent> keyEvent = std::move(*optKeyEvent); + + sendMessage(LooperMessage::CALL_PROBABLY_HAS_INPUT); + std::optional<bool> probablyHasInput = mProbablyHasInputResponses.popWithTimeout(TIMEOUT); + ASSERT_TRUE(probablyHasInput.has_value()); + ASSERT_FALSE(probablyHasInput.value()) << "no events should be waiting after being consumed"; + + EXPECT_EQ(eventId, keyEvent->getId()); + EXPECT_EQ(deviceId, keyEvent->getDeviceId()); + EXPECT_EQ(source, keyEvent->getSource()); + EXPECT_EQ(displayId, keyEvent->getDisplayId()); + EXPECT_EQ(hmac, keyEvent->getHmac()); + EXPECT_EQ(action, keyEvent->getAction()); + EXPECT_EQ(flags, keyEvent->getFlags()); + EXPECT_EQ(keyCode, keyEvent->getKeyCode()); + EXPECT_EQ(scanCode, keyEvent->getScanCode()); + EXPECT_EQ(metaState, keyEvent->getMetaState()); + EXPECT_EQ(repeatCount, keyEvent->getRepeatCount()); + EXPECT_EQ(downTime, keyEvent->getDownTime()); + EXPECT_EQ(eventTime, keyEvent->getEventTime()); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionStream() { + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}}); + + publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}, + Pointer{.id = 1, .x = 200, .y = 300}}); + + publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}, + Pointer{.id = 1, .x = 200, .y = 300}, + Pointer{.id = 2, .x = 300, .y = 400}}); + + // Provide a consistent input stream - cancel the gesture that was started above + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}, + Pointer{.id = 1, .x = 200, .y = 300}, + Pointer{.id = 2, .x = 300, .y = 400}}); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionDown(nsecs_t downTime) { + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}}); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove( + nsecs_t downTime) { + uint32_t seq = mSeq++; + const std::vector<Pointer> pointers = {Pointer{.id = 0, .x = 20, .y = 30}}; + PublishMotionArgs args(AMOTION_EVENT_ACTION_MOVE, downTime, pointers, seq); + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + // Block the looper thread, preventing it from being able to service any of the fd callbacks. + + { + std::scoped_lock lock(mLock); + mLooperMayProceed = false; + } + sendMessage(LooperMessage::BLOCK_LOOPER); + { + std::unique_lock lock(mLock); + mNotifyLooperWaiting.wait(lock, [this] { return mLooperIsBlocked; }); + } + + publishMotionEvent(*mPublisher, args); + + // Ensure no event arrives because the UI thread is blocked + std::optional<std::unique_ptr<MotionEvent>> noEvent = + mMotionEvents.popWithTimeout(NO_EVENT_TIMEOUT); + ASSERT_FALSE(noEvent.has_value()) << "Got unexpected event: " << *noEvent; + + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_FALSE(result.ok()); + ASSERT_EQ(WOULD_BLOCK, result.error().code()); + + // We shouldn't be calling mConsumer on the UI thread, but in this situation, the looper + // thread is locked, so this should be safe to do. + ASSERT_TRUE(mConsumer->probablyHasInput()) + << "should deterministically have input because there is a batch"; + + // Now, unblock the looper thread, so that the event can arrive. + { + std::scoped_lock lock(mLock); + mLooperMayProceed = true; + } + mNotifyLooperMayProceed.notify_all(); + + std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optMotion.has_value()); + std::unique_ptr<MotionEvent> motion = std::move(*optMotion); + ASSERT_EQ(ACTION_MOVE, motion->getAction()); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionEvent( + int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { + uint32_t seq = mSeq++; + PublishMotionArgs args(action, downTime, pointers, seq); + nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); + + std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optMotion.has_value()); + std::unique_ptr<MotionEvent> event = std::move(*optMotion); + + verifyArgsEqualToEvent(args, *event); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeFocusEvent() { + status_t status; + + constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); + constexpr bool hasFocus = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishFocusEvent(seq, eventId, hasFocus); + ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK"; + + std::optional<std::unique_ptr<FocusEvent>> optFocusEvent = mFocusEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optFocusEvent.has_value()) << "consumer should have returned non-NULL event"; + std::unique_ptr<FocusEvent> focusEvent = std::move(*optFocusEvent); + EXPECT_EQ(eventId, focusEvent->getId()); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeCaptureEvent() { + status_t status; + + constexpr uint32_t seq = 42; + int32_t eventId = InputEvent::nextId(); + constexpr bool captureEnabled = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled); + ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK"; + + std::optional<std::unique_ptr<CaptureEvent>> optEvent = mCaptureEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optEvent.has_value()) << "consumer should have returned non-NULL event"; + std::unique_ptr<CaptureEvent> event = std::move(*optEvent); + + const CaptureEvent& captureEvent = *event; + EXPECT_EQ(eventId, captureEvent.getId()); + EXPECT_EQ(captureEnabled, captureEvent.getPointerCaptureEnabled()); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeDragEvent() { + status_t status; + + constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); + constexpr bool isExiting = false; + constexpr float x = 10; + constexpr float y = 15; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishDragEvent(seq, eventId, x, y, isExiting); + ASSERT_EQ(OK, status) << "publisher publishDragEvent should return OK"; + + std::optional<std::unique_ptr<DragEvent>> optEvent = mDragEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optEvent.has_value()) << "consumer should have returned non-NULL event"; + std::unique_ptr<DragEvent> event = std::move(*optEvent); + + const DragEvent& dragEvent = *event; + EXPECT_EQ(eventId, dragEvent.getId()); + EXPECT_EQ(isExiting, dragEvent.isExiting()); + EXPECT_EQ(x, dragEvent.getX()); + EXPECT_EQ(y, dragEvent.getY()); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeTouchModeEvent() { + status_t status; + + constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); + constexpr bool touchModeEnabled = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled); + ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK"; + + std::optional<std::unique_ptr<TouchModeEvent>> optEvent = + mTouchModeEvents.popWithTimeout(TIMEOUT); + ASSERT_TRUE(optEvent.has_value()); + std::unique_ptr<TouchModeEvent> event = std::move(*optEvent); + + const TouchModeEvent& touchModeEvent = *event; + EXPECT_EQ(eventId, touchModeEvent.getId()); + EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode()); + + verifyFinishedSignal(*mPublisher, seq, publishTime); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, SendTimeline) { + const int32_t inputEventId = 20; + const nsecs_t gpuCompletedTime = 30; + const nsecs_t presentTime = 40; + + mReportTimelineArgs.emplace(inputEventId, gpuCompletedTime, presentTime); + sendMessage(LooperMessage::CALL_REPORT_TIMELINE); + + Result<InputPublisher::ConsumerResponse> result = receiveConsumerResponse(*mPublisher, TIMEOUT); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result)); + const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result); + ASSERT_EQ(inputEventId, timeline.inputEventId); + ASSERT_EQ(gpuCompletedTime, timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]); + ASSERT_EQ(presentTime, timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishKeyEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent()); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMotionEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream()); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMotionMoveEvent_EndToEnd) { + // Publish a DOWN event before MOVE to pass the InputVerifier checks. + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionDown(downTime)); + + // Publish the MOVE event and check expectations. + ASSERT_NO_FATAL_FAILURE(publishAndConsumeBatchedMotionMove(downTime)); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishFocusEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent()); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishCaptureEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent()); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishDragEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent()); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishTouchModeEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent()); +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, + PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { + status_t status; + const size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } + + ui::Transform identityTransform; + status = + mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, + PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { + status_t status; + const size_t pointerCount = 0; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + ui::Transform identityTransform; + status = + mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, + PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { + status_t status; + const size_t pointerCount = MAX_POINTERS + 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } + + ui::Transform identityTransform; + status = + mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMultipleEvents_EndToEnd) { + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}}); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent()); + publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}, + Pointer{.id = 1, .x = 200, .y = 300}}); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent()); + publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}, + Pointer{.id = 1, .x = 200, .y = 300}, + Pointer{.id = 2, .x = 200, .y = 300}}); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent()); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent()); + // Provide a consistent input stream - cancel the gesture that was started above + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}, + Pointer{.id = 1, .x = 200, .y = 300}, + Pointer{.id = 2, .x = 200, .y = 300}}); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent()); +} + +} // namespace android diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 39902b1635..0defc7e0d3 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -83,11 +83,13 @@ filegroup { "skia/AutoBackendTexture.cpp", "skia/Cache.cpp", "skia/ColorSpaces.cpp", + "skia/GaneshVkRenderEngine.cpp", "skia/GLExtensions.cpp", "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", "skia/SkiaVkRenderEngine.cpp", "skia/VulkanInterface.cpp", + "skia/compat/GaneshBackendTexture.cpp", "skia/compat/GaneshGpuContext.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", diff --git a/libs/renderengine/include/renderengine/BorderRenderInfo.h b/libs/renderengine/include/renderengine/BorderRenderInfo.h deleted file mode 100644 index 0ee6661f33..0000000000 --- a/libs/renderengine/include/renderengine/BorderRenderInfo.h +++ /dev/null @@ -1,36 +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 <math/mat4.h> -#include <ui/Region.h> - -namespace android { -namespace renderengine { - -struct BorderRenderInfo { - float width = 0; - half4 color; - Region combinedRegion; - - bool operator==(const BorderRenderInfo& rhs) const { - return (width == rhs.width && color == rhs.color && - combinedRegion.hasSameRects(rhs.combinedRegion)); - } -}; - -} // namespace renderengine -} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index 8d7c13cb18..deb62534a5 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -22,7 +22,6 @@ #include <math/mat4.h> #include <renderengine/PrintMatrix.h> -#include <renderengine/BorderRenderInfo.h> #include <ui/DisplayId.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> @@ -87,8 +86,6 @@ struct DisplaySettings { // Configures the rendering intent of the output display. This is used for tonemapping. aidl::android::hardware::graphics::composer3::RenderIntent renderIntent = aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC; - - std::vector<renderengine::BorderRenderInfo> borderInfoList; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { @@ -100,8 +97,7 @@ static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& lhs.deviceHandlesColorTransform == rhs.deviceHandlesColorTransform && lhs.orientation == rhs.orientation && lhs.targetLuminanceNits == rhs.targetLuminanceNits && - lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent && - lhs.borderInfoList == rhs.borderInfoList; + lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent; } static const char* orientation_to_string(uint32_t orientation) { diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index 02e7337053..8aeef9f4bc 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -20,71 +20,21 @@ #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <SkImage.h> -#include <android/hardware_buffer.h> -#include <include/gpu/ganesh/SkImageGanesh.h> -#include <include/gpu/ganesh/SkSurfaceGanesh.h> -#include <include/gpu/ganesh/gl/GrGLBackendSurface.h> -#include <include/gpu/ganesh/vk/GrVkBackendSurface.h> -#include <include/gpu/vk/GrVkTypes.h> -#include "ColorSpaces.h" -#include "log/log_main.h" -#include "utils/Trace.h" +#include <include/core/SkImage.h> +#include <include/core/SkSurface.h> + +#include "compat/SkiaBackendTexture.h" + +#include <log/log_main.h> +#include <utils/Trace.h> namespace android { namespace renderengine { namespace skia { -AutoBackendTexture::AutoBackendTexture(SkiaGpuContext* context, AHardwareBuffer* buffer, - bool isOutputBuffer, CleanupManager& cleanupMgr) - : mGrContext(context->grDirectContext()), - mCleanupMgr(cleanupMgr), - mIsOutputBuffer(isOutputBuffer) { - ATRACE_CALL(); - - AHardwareBuffer_Desc desc; - AHardwareBuffer_describe(buffer, &desc); - bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); - GrBackendFormat backendFormat; - - GrBackendApi backend = mGrContext->backend(); - if (backend == GrBackendApi::kOpenGL) { - backendFormat = - GrAHardwareBufferUtils::GetGLBackendFormat(mGrContext.get(), desc.format, false); - mBackendTexture = - GrAHardwareBufferUtils::MakeGLBackendTexture(mGrContext.get(), buffer, desc.width, - desc.height, &mDeleteProc, - &mUpdateProc, &mImageCtx, - createProtectedImage, backendFormat, - isOutputBuffer); - } else if (backend == GrBackendApi::kVulkan) { - backendFormat = GrAHardwareBufferUtils::GetVulkanBackendFormat(mGrContext.get(), buffer, - desc.format, false); - mBackendTexture = - GrAHardwareBufferUtils::MakeVulkanBackendTexture(mGrContext.get(), buffer, - desc.width, desc.height, - &mDeleteProc, &mUpdateProc, - &mImageCtx, createProtectedImage, - backendFormat, isOutputBuffer); - } else { - LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend)); - } - - mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); - if (!mBackendTexture.isValid() || !desc.width || !desc.height) { - LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d " - "isWriteable:%d format:%d", - this, desc.width, desc.height, createProtectedImage, isOutputBuffer, - desc.format); - } -} - -AutoBackendTexture::~AutoBackendTexture() { - if (mBackendTexture.isValid()) { - mDeleteProc(mImageCtx); - mBackendTexture = {}; - } -} +AutoBackendTexture::AutoBackendTexture(std::unique_ptr<SkiaBackendTexture> backendTexture, + CleanupManager& cleanupMgr) + : mCleanupMgr(cleanupMgr), mBackendTexture(std::move(backendTexture)) {} void AutoBackendTexture::unref(bool releaseLocalResources) { if (releaseLocalResources) { @@ -112,93 +62,32 @@ void AutoBackendTexture::releaseImageProc(SkImages::ReleaseContext releaseContex textureRelease->unref(false); } -void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace dataspace, - SkColorType colorType) { - switch (tex.backend()) { - case GrBackendApi::kOpenGL: { - GrGLTextureInfo textureInfo; - bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo); - LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d" - "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " - "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u" - " colorType %i", - msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(), - tex.height(), tex.hasMipmaps(), tex.isProtected(), - static_cast<int>(tex.textureType()), retrievedTextureInfo, - textureInfo.fTarget, textureInfo.fFormat, colorType); - break; - } - case GrBackendApi::kVulkan: { - GrVkImageInfo imageInfo; - bool retrievedImageInfo = GrBackendTextures::GetVkImageInfo(tex, &imageInfo); - LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d" - "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " - "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i " - "fSampleCount: %u fLevelCount: %u colorType %i", - msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(), - tex.height(), tex.hasMipmaps(), tex.isProtected(), - static_cast<int>(tex.textureType()), retrievedImageInfo, - imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount, - colorType); - break; - } - default: - LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg, static_cast<unsigned>(tex.backend())); - break; - } -} - sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) { ATRACE_CALL(); - if (mBackendTexture.isValid()) { - mUpdateProc(mImageCtx, mGrContext.get()); - } - - auto colorType = mColorType; - if (alphaType == kOpaque_SkAlphaType) { - if (colorType == kRGBA_8888_SkColorType) { - colorType = kRGB_888x_SkColorType; - } - } - - sk_sp<SkImage> image = - SkImages::BorrowTextureFrom(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin, - colorType, alphaType, toSkColorSpace(dataspace), - releaseImageProc, this); - if (image.get()) { - // The following ref will be counteracted by releaseProc, when SkImage is discarded. - ref(); - } + sk_sp<SkImage> image = mBackendTexture->makeImage(alphaType, dataspace, releaseImageProc, this); + // The following ref will be counteracted by releaseProc, when SkImage is discarded. + ref(); mImage = image; mDataspace = dataspace; - if (!mImage) { - logFatalTexture("Unable to generate SkImage.", mBackendTexture, dataspace, colorType); - } return mImage; } sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace) { ATRACE_CALL(); - LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture"); + LOG_ALWAYS_FATAL_IF(!mBackendTexture->isOutputBuffer(), + "You can't generate an SkSurface for a read-only texture"); if (!mSurface.get() || mDataspace != dataspace) { sk_sp<SkSurface> surface = - SkSurfaces::WrapBackendTexture(mGrContext.get(), mBackendTexture, - kTopLeft_GrSurfaceOrigin, 0, mColorType, - toSkColorSpace(dataspace), nullptr, - releaseSurfaceProc, this); - if (surface.get()) { - // The following ref will be counteracted by releaseProc, when SkSurface is discarded. - ref(); - } + mBackendTexture->makeSurface(dataspace, releaseSurfaceProc, this); + // The following ref will be counteracted by releaseProc, when SkSurface is discarded. + ref(); + mSurface = surface; } mDataspace = dataspace; - if (!mSurface) { - logFatalTexture("Unable to generate SkSurface.", mBackendTexture, dataspace, mColorType); - } return mSurface; } diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h index 1d5b5655db..74daf471fa 100644 --- a/libs/renderengine/skia/AutoBackendTexture.h +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -16,7 +16,6 @@ #pragma once -#include <GrAHardwareBufferUtils.h> #include <GrDirectContext.h> #include <SkImage.h> #include <SkSurface.h> @@ -24,9 +23,9 @@ #include <ui/GraphicTypes.h> #include "android-base/macros.h" -#include "compat/SkiaGpuContext.h" +#include "compat/SkiaBackendTexture.h" -#include <mutex> +#include <memory> #include <vector> namespace android { @@ -81,9 +80,8 @@ public: // of shared ownership with Skia objects, so we wrap it here instead. class LocalRef { public: - LocalRef(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, - CleanupManager& cleanupMgr) { - mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr); + LocalRef(std::unique_ptr<SkiaBackendTexture> backendTexture, CleanupManager& cleanupMgr) { + mTexture = new AutoBackendTexture(std::move(backendTexture), cleanupMgr); mTexture->ref(); } @@ -105,7 +103,7 @@ public: return mTexture->getOrCreateSurface(dataspace); } - SkColorType colorType() const { return mTexture->mColorType; } + SkColorType colorType() const { return mTexture->mBackendTexture->internalColorType(); } DISALLOW_COPY_AND_ASSIGN(LocalRef); @@ -116,12 +114,13 @@ public: private: DISALLOW_COPY_AND_ASSIGN(AutoBackendTexture); - // Creates a GrBackendTexture whose contents come from the provided buffer. - AutoBackendTexture(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, + // Creates an AutoBackendTexture to manage the lifecycle of a given SkiaBackendTexture, which is + // in turn backed by an underlying backend-specific texture type. + AutoBackendTexture(std::unique_ptr<SkiaBackendTexture> backendTexture, CleanupManager& cleanupMgr); // The only way to invoke dtor is with unref, when mUsageCount is 0. - ~AutoBackendTexture(); + ~AutoBackendTexture() = default; void ref() { mUsageCount++; } @@ -137,24 +136,16 @@ private: // Makes a new SkSurface from the texture content, if needed. sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace); - GrBackendTexture mBackendTexture; - GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; - GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; - GrAHardwareBufferUtils::TexImageCtx mImageCtx; - - // TODO: b/293371537 - Graphite abstractions for ABT. - const sk_sp<GrDirectContext> mGrContext = nullptr; CleanupManager& mCleanupMgr; static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext); static void releaseImageProc(SkImages::ReleaseContext releaseContext); + std::unique_ptr<SkiaBackendTexture> mBackendTexture; int mUsageCount = 0; - const bool mIsOutputBuffer; sk_sp<SkImage> mImage = nullptr; sk_sp<SkSurface> mSurface = nullptr; ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN; - SkColorType mColorType = kUnknown_SkColorType; }; } // namespace skia diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp new file mode 100644 index 0000000000..aa18713295 --- /dev/null +++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GaneshVkRenderEngine.h" + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" + +#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> + +#include <log/log_main.h> +#include <sync/sync.h> +#include <utils/Trace.h> + +namespace android::renderengine::skia { + +// Ganesh-specific function signature for fFinishedProc callback. +static void unref_semaphore(void* semaphore) { + SkiaVkRenderEngine::DestroySemaphoreInfo* info = + reinterpret_cast<SkiaVkRenderEngine::DestroySemaphoreInfo*>(semaphore); + info->unref(); +} + +std::unique_ptr<SkiaGpuContext> GaneshVkRenderEngine::createContext( + VulkanInterface& vulkanInterface) { + return SkiaGpuContext::MakeVulkan_Ganesh(vulkanInterface.getGaneshBackendContext(), + mSkSLCacheMonitor); +} + +void GaneshVkRenderEngine::waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) { + if (fenceFd.get() < 0) return; + + const int dupedFd = dup(fenceFd.get()); + if (dupedFd < 0) { + ALOGE("failed to create duplicate fence fd: %d", dupedFd); + sync_wait(fenceFd.get(), -1); + return; + } + + base::unique_fd fenceDup(dupedFd); + VkSemaphore waitSemaphore = + getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); + GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore); + constexpr bool kDeleteAfterWait = true; + context->grDirectContext()->wait(1, &beSemaphore, kDeleteAfterWait); +} + +base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, + sk_sp<SkSurface> dstSurface) { + sk_sp<GrDirectContext> grContext = context->grDirectContext(); + { + ATRACE_NAME("flush surface"); + // TODO: Investigate feasibility of combining this "surface flush" into the "context flush" + // below. + context->grDirectContext()->flush(dstSurface.get()); + } + + VulkanInterface& vi = getVulkanInterface(isProtected()); + VkSemaphore semaphore = vi.createExportableSemaphore(); + GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore); + + GrFlushInfo flushInfo; + DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; + if (semaphore != VK_NULL_HANDLE) { + destroySemaphoreInfo = new DestroySemaphoreInfo(vi, semaphore); + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &backendSemaphore; + flushInfo.fFinishedProc = unref_semaphore; + flushInfo.fFinishedContext = destroySemaphoreInfo; + } + GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); + grContext->submit(GrSyncCpu::kNo); + int drawFenceFd = -1; + if (semaphore != VK_NULL_HANDLE) { + if (GrSemaphoresSubmitted::kYes == submitted) { + drawFenceFd = vi.exportSemaphoreSyncFd(semaphore); + } + // Now that drawFenceFd has been created, we can delete our reference to this semaphore + flushInfo.fFinishedProc(destroySemaphoreInfo); + } + base::unique_fd res(drawFenceFd); + return res; +} + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h new file mode 100644 index 0000000000..5940d04f51 --- /dev/null +++ b/libs/renderengine/skia/GaneshVkRenderEngine.h @@ -0,0 +1,35 @@ +/* + * Copyright 2024 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 "skia/SkiaVkRenderEngine.h" + +namespace android::renderengine::skia { + +class GaneshVkRenderEngine : public SkiaVkRenderEngine { + friend std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( + const RenderEngineCreationArgs& args); + +protected: + GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} + + std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override; + void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; + base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override; +}; + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index f10c98d6e2..61369ae764 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -337,8 +337,14 @@ void SkiaGLRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) { } } -base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context) { - base::unique_fd drawFence = flush(); +base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context, + sk_sp<SkSurface> dstSurface) { + sk_sp<GrDirectContext> grContext = context->grDirectContext(); + { + ATRACE_NAME("flush surface"); + grContext->flush(dstSurface.get()); + } + base::unique_fd drawFence = flushGL(); bool requireSync = drawFence.get() < 0; if (requireSync) { @@ -346,8 +352,7 @@ base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context) { } else { ATRACE_BEGIN("Submit(sync=false)"); } - bool success = - context->grDirectContext()->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo); + bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo); ATRACE_END(); if (!success) { ALOGE("Failed to flush RenderEngine commands"); @@ -394,7 +399,7 @@ bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { return true; } -base::unique_fd SkiaGLRenderEngine::flush() { +base::unique_fd SkiaGLRenderEngine::flushGL() { ATRACE_CALL(); if (!GLExtensions::getInstance().hasNativeFenceSync()) { return base::unique_fd(); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 2e22478274..bd177e60ba 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -63,7 +63,7 @@ protected: bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; - base::unique_fd flushAndSubmit(SkiaGpuContext* context) override; + base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override; void appendBackendSpecificInfoToDump(std::string& result) override; private: @@ -71,7 +71,7 @@ private: EGLSurface placeholder, EGLContext protectedContext, EGLSurface protectedPlaceholder); bool waitGpuFence(base::borrowed_fd fenceFd); - base::unique_fd flush(); + base::unique_fd flushGL(); static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); static EGLContext createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 27daeba092..24846509bc 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -80,6 +80,7 @@ #include "filters/KawaseBlurFilter.h" #include "filters/LinearEffect.h" #include "log/log_main.h" +#include "skia/compat/SkiaBackendTexture.h" #include "skia/debug/SkiaCapture.h" #include "skia/debug/SkiaMemoryReporter.h" #include "skia/filters/StretchShaderFactory.h" @@ -89,7 +90,7 @@ namespace { // Debugging settings static const bool kPrintLayerSettings = false; -static const bool kFlushAfterEveryLayer = kPrintLayerSettings; +static const bool kGaneshFlushAfterEveryLayer = kPrintLayerSettings; static constexpr bool kEnableLayerBrightening = true; } // namespace @@ -417,9 +418,11 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, if (FlagManager::getInstance().renderable_buffer_usage()) { isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER; } - std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = - std::make_shared<AutoBackendTexture::LocalRef>(context, buffer->toAHardwareBuffer(), - isRenderable, mTextureCleanupMgr); + std::unique_ptr<SkiaBackendTexture> backendTexture = + context->makeBackendTexture(buffer->toAHardwareBuffer(), isRenderable); + auto imageTextureRef = + std::make_shared<AutoBackendTexture::LocalRef>(std::move(backendTexture), + mTextureCleanupMgr); cache.insert({buffer->getId(), imageTextureRef}); } } @@ -470,9 +473,10 @@ std::shared_ptr<AutoBackendTexture::LocalRef> SkiaRenderEngine::getOrCreateBacke return it->second; } } - return std::make_shared<AutoBackendTexture::LocalRef>(getActiveContext(), - buffer->toAHardwareBuffer(), - isOutputBuffer, mTextureCleanupMgr); + std::unique_ptr<SkiaBackendTexture> backendTexture = + getActiveContext()->makeBackendTexture(buffer->toAHardwareBuffer(), isOutputBuffer); + return std::make_shared<AutoBackendTexture::LocalRef>(std::move(backendTexture), + mTextureCleanupMgr); } bool SkiaRenderEngine::canSkipPostRenderCleanup() const { @@ -661,8 +665,8 @@ void SkiaRenderEngine::drawLayersInternal( validateOutputBufferUsage(buffer->getBuffer()); auto context = getActiveContext(); - LOG_ALWAYS_FATAL_IF(context->isAbandoned(), "Context is abandoned/device lost at start of %s", - __func__); + LOG_ALWAYS_FATAL_IF(context->isAbandonedOrDeviceLost(), + "Context is abandoned/device lost at start of %s", __func__); // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called DeferTextureCleanup dtc(mTextureCleanupMgr); @@ -1118,40 +1122,21 @@ void SkiaRenderEngine::drawLayersInternal( } else { canvas->drawRect(bounds.rect(), paint); } - if (kFlushAfterEveryLayer) { + if (kGaneshFlushAfterEveryLayer) { ATRACE_NAME("flush surface"); + // No-op in Graphite. If "flushing" Skia's drawing commands after each layer is desired + // in Graphite, then a graphite::Recording would need to be snapped and tracked for each + // layer, which is likely possible but adds non-trivial complexity (in both bookkeeping + // and refactoring). skgpu::ganesh::Flush(activeSurface); } } - for (const auto& borderRenderInfo : display.borderInfoList) { - SkPaint p; - p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g, - borderRenderInfo.color.b, borderRenderInfo.color.a}); - p.setAntiAlias(true); - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(borderRenderInfo.width); - SkRegion sk_region; - SkPath path; - - // Construct a final SkRegion using Regions - for (const auto& r : borderRenderInfo.combinedRegion) { - sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op); - } - - sk_region.getBoundaryPath(&path); - canvas->drawPath(path, p); - path.close(); - } surfaceAutoSaveRestore.restore(); mCapture->endCapture(); - { - ATRACE_NAME("flush surface"); - LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); - skgpu::ganesh::Flush(activeSurface); - } - auto drawFence = sp<Fence>::make(flushAndSubmit(context)); + LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); + auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); if (ATRACE_ENABLED()) { static gui::FenceMonitor sMonitor("RE Completion"); diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 9f892bd0f7..d7b491083d 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -89,11 +89,13 @@ protected: virtual bool supportsProtectedContentImpl() const = 0; virtual bool useProtectedContextImpl(GrProtected isProtected) = 0; virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) = 0; - virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context) = 0; + virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context, + sk_sp<SkSurface> dstSurface) = 0; virtual void appendBackendSpecificInfoToDump(std::string& result) = 0; size_t getMaxTextureSize() const override final; size_t getMaxViewportDims() const override final; + // TODO: b/293371537 - Return reference instead of pointer? (Cleanup) SkiaGpuContext* getActiveContext(); bool isProtected() const { return mInProtectedContext; } diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index d854929960..3715859e8a 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -21,6 +21,7 @@ #include "SkiaVkRenderEngine.h" +#include "GaneshVkRenderEngine.h" #include "compat/SkiaGpuContext.h" #include <GrBackendSemaphore.h> @@ -84,7 +85,8 @@ using base::StringAppendF; std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( const RenderEngineCreationArgs& args) { - std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); + // TODO: b/293371537 - Ganesh vs. Graphite subclass based on flag in RenderEngineCreationArgs + std::unique_ptr<SkiaVkRenderEngine> engine(new GaneshVkRenderEngine(args)); engine->ensureContextsCreated(); if (sVulkanInterface.isInitialized()) { @@ -110,12 +112,9 @@ SkiaRenderEngine::Contexts SkiaVkRenderEngine::createContexts() { sSetupVulkanInterface(); SkiaRenderEngine::Contexts contexts; - contexts.first = SkiaGpuContext::MakeVulkan_Ganesh(sVulkanInterface.getGaneshBackendContext(), - mSkSLCacheMonitor); + contexts.first = createContext(sVulkanInterface); if (supportsProtectedContentImpl()) { - contexts.second = SkiaGpuContext::MakeVulkan_Ganesh(sProtectedContentVulkanInterface - .getGaneshBackendContext(), - mSkSLCacheMonitor); + contexts.second = createContext(sProtectedContentVulkanInterface); } return contexts; @@ -129,66 +128,13 @@ bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) { return true; } -static void unref_semaphore(void* semaphore) { - SkiaVkRenderEngine::DestroySemaphoreInfo* info = - reinterpret_cast<SkiaVkRenderEngine::DestroySemaphoreInfo*>(semaphore); - info->unref(); -} - -static VulkanInterface& getVulkanInterface(bool protectedContext) { +VulkanInterface& SkiaVkRenderEngine::getVulkanInterface(bool protectedContext) { if (protectedContext) { return sProtectedContentVulkanInterface; } return sVulkanInterface; } -void SkiaVkRenderEngine::waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) { - if (fenceFd.get() < 0) return; - - int dupedFd = dup(fenceFd.get()); - if (dupedFd < 0) { - ALOGE("failed to create duplicate fence fd: %d", dupedFd); - sync_wait(fenceFd.get(), -1); - return; - } - - base::unique_fd fenceDup(dupedFd); - VkSemaphore waitSemaphore = - getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); - GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore); - context->grDirectContext()->wait(1, &beSemaphore, true /* delete after wait */); -} - -base::unique_fd SkiaVkRenderEngine::flushAndSubmit(SkiaGpuContext* context) { - sk_sp<GrDirectContext> grContext = context->grDirectContext(); - VulkanInterface& vi = getVulkanInterface(isProtected()); - VkSemaphore semaphore = vi.createExportableSemaphore(); - - GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore); - - GrFlushInfo flushInfo; - DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; - if (semaphore != VK_NULL_HANDLE) { - destroySemaphoreInfo = new DestroySemaphoreInfo(vi, semaphore); - flushInfo.fNumSemaphores = 1; - flushInfo.fSignalSemaphores = &backendSemaphore; - flushInfo.fFinishedProc = unref_semaphore; - flushInfo.fFinishedContext = destroySemaphoreInfo; - } - GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); - grContext->submit(GrSyncCpu::kNo); - int drawFenceFd = -1; - if (semaphore != VK_NULL_HANDLE) { - if (GrSemaphoresSubmitted::kYes == submitted) { - drawFenceFd = vi.exportSemaphoreSyncFd(semaphore); - } - // Now that drawFenceFd has been created, we can delete our reference to this semaphore - flushInfo.fFinishedProc(destroySemaphoreInfo); - } - base::unique_fd res(drawFenceFd); - return res; -} - int SkiaVkRenderEngine::getContextPriority() { // EGL_CONTEXT_PRIORITY_REALTIME_NV constexpr int kRealtimePriority = 0x3357; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 28b7595d41..371b812897 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -71,20 +71,23 @@ public: }; protected: + virtual std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) = 0; + // Redeclare parent functions that Ganesh vs. Graphite subclasses must implement. + virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override = 0; + virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context, + sk_sp<SkSurface> dstSurface) override = 0; + + SkiaVkRenderEngine(const RenderEngineCreationArgs& args); + // Implementations of abstract SkiaRenderEngine functions specific to - // rendering backend + // Vulkan, but shareable between Ganesh and Graphite. SkiaRenderEngine::Contexts createContexts() override; bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; - void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; - base::unique_fd flushAndSubmit(SkiaGpuContext* context) override; void appendBackendSpecificInfoToDump(std::string& result) override; -private: - SkiaVkRenderEngine(const RenderEngineCreationArgs& args); - base::unique_fd flush(); - - GrVkBackendContext mBackendContext; + // TODO: b/300533018 - refactor this to be non-static + static VulkanInterface& getVulkanInterface(bool protectedContext); }; } // namespace skia diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp new file mode 100644 index 0000000000..d246466965 --- /dev/null +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GaneshBackendTexture.h" + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <include/core/SkImage.h> +#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/SkImageGanesh.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <include/gpu/ganesh/gl/GrGLBackendSurface.h> +#include <include/gpu/ganesh/vk/GrVkBackendSurface.h> +#include <include/gpu/vk/GrVkTypes.h> + +#include "skia/ColorSpaces.h" +#include "skia/compat/SkiaBackendTexture.h" + +#include <android/hardware_buffer.h> +#include <log/log_main.h> +#include <utils/Trace.h> + +namespace android::renderengine::skia { + +GaneshBackendTexture::GaneshBackendTexture(sk_sp<GrDirectContext> grContext, + AHardwareBuffer* buffer, bool isOutputBuffer) + : SkiaBackendTexture(buffer, isOutputBuffer), mGrContext(grContext) { + ATRACE_CALL(); + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); + + GrBackendFormat backendFormat; + const GrBackendApi graphicsApi = grContext->backend(); + if (graphicsApi == GrBackendApi::kOpenGL) { + backendFormat = + GrAHardwareBufferUtils::GetGLBackendFormat(grContext.get(), desc.format, false); + mBackendTexture = + GrAHardwareBufferUtils::MakeGLBackendTexture(grContext.get(), buffer, desc.width, + desc.height, &mDeleteProc, + &mUpdateProc, &mImageCtx, + createProtectedImage, backendFormat, + isOutputBuffer); + } else if (graphicsApi == GrBackendApi::kVulkan) { + backendFormat = GrAHardwareBufferUtils::GetVulkanBackendFormat(grContext.get(), buffer, + desc.format, false); + mBackendTexture = + GrAHardwareBufferUtils::MakeVulkanBackendTexture(grContext.get(), buffer, + desc.width, desc.height, + &mDeleteProc, &mUpdateProc, + &mImageCtx, createProtectedImage, + backendFormat, isOutputBuffer); + } else { + LOG_ALWAYS_FATAL("Unexpected graphics API %u", static_cast<unsigned>(graphicsApi)); + } + + if (!mBackendTexture.isValid() || !desc.width || !desc.height) { + LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d " + "isWriteable:%d format:%d", + this, desc.width, desc.height, createProtectedImage, isOutputBuffer, + desc.format); + } +} + +GaneshBackendTexture::~GaneshBackendTexture() { + if (mBackendTexture.isValid()) { + mDeleteProc(mImageCtx); + mBackendTexture = {}; + } +} + +sk_sp<SkImage> GaneshBackendTexture::makeImage(SkAlphaType alphaType, ui::Dataspace dataspace, + TextureReleaseProc releaseImageProc, + ReleaseContext releaseContext) { + if (mBackendTexture.isValid()) { + mUpdateProc(mImageCtx, mGrContext.get()); + } + + const SkColorType colorType = colorTypeForImage(alphaType); + sk_sp<SkImage> image = + SkImages::BorrowTextureFrom(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin, + colorType, alphaType, toSkColorSpace(dataspace), + releaseImageProc, releaseContext); + if (!image) { + logFatalTexture("Unable to generate SkImage.", dataspace, colorType); + } + return image; +} + +sk_sp<SkSurface> GaneshBackendTexture::makeSurface(ui::Dataspace dataspace, + TextureReleaseProc releaseSurfaceProc, + ReleaseContext releaseContext) { + const SkColorType colorType = internalColorType(); + sk_sp<SkSurface> surface = + SkSurfaces::WrapBackendTexture(mGrContext.get(), mBackendTexture, + kTopLeft_GrSurfaceOrigin, 0, colorType, + toSkColorSpace(dataspace), nullptr, releaseSurfaceProc, + releaseContext); + if (!surface) { + logFatalTexture("Unable to generate SkSurface.", dataspace, colorType); + } + return surface; +} + +void GaneshBackendTexture::logFatalTexture(const char* msg, ui::Dataspace dataspace, + SkColorType colorType) { + switch (mBackendTexture.backend()) { + case GrBackendApi::kOpenGL: { + GrGLTextureInfo textureInfo; + bool retrievedTextureInfo = + GrBackendTextures::GetGLTextureInfo(mBackendTexture, &textureInfo); + LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d" + "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " + "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u" + " colorType %i", + msg, mBackendTexture.isValid(), static_cast<int32_t>(dataspace), + mBackendTexture.width(), mBackendTexture.height(), + mBackendTexture.hasMipmaps(), mBackendTexture.isProtected(), + static_cast<int>(mBackendTexture.textureType()), retrievedTextureInfo, + textureInfo.fTarget, textureInfo.fFormat, colorType); + break; + } + case GrBackendApi::kVulkan: { + GrVkImageInfo imageInfo; + bool retrievedImageInfo = + GrBackendTextures::GetVkImageInfo(mBackendTexture, &imageInfo); + LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d" + "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " + "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i " + "fSampleCount: %u fLevelCount: %u colorType %i", + msg, mBackendTexture.isValid(), static_cast<int32_t>(dataspace), + mBackendTexture.width(), mBackendTexture.height(), + mBackendTexture.hasMipmaps(), mBackendTexture.isProtected(), + static_cast<int>(mBackendTexture.textureType()), retrievedImageInfo, + imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount, + colorType); + break; + } + default: + LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg, + static_cast<unsigned>(mBackendTexture.backend())); + break; + } +} + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h new file mode 100644 index 0000000000..5cf8647801 --- /dev/null +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h @@ -0,0 +1,57 @@ +/* + * Copyright 2024 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 "SkiaBackendTexture.h" +#include "ui/GraphicTypes.h" + +#include <include/android/GrAHardwareBufferUtils.h> +#include <include/core/SkColorSpace.h> +#include <include/gpu/GrDirectContext.h> + +#include <android-base/macros.h> + +namespace android::renderengine::skia { + +class GaneshBackendTexture : public SkiaBackendTexture { +public: + // Creates an internal GrBackendTexture whose contents come from the provided buffer. + GaneshBackendTexture(sk_sp<GrDirectContext> grContext, AHardwareBuffer* buffer, + bool isOutputBuffer); + + ~GaneshBackendTexture() override; + + sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace, + TextureReleaseProc releaseImageProc, + ReleaseContext releaseContext) override; + + sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace, TextureReleaseProc releaseSurfaceProc, + ReleaseContext releaseContext) override; + +private: + DISALLOW_COPY_AND_ASSIGN(GaneshBackendTexture); + + void logFatalTexture(const char* msg, ui::Dataspace dataspace, SkColorType colorType); + + const sk_sp<GrDirectContext> mGrContext; + GrBackendTexture mBackendTexture; + GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; + GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; + GrAHardwareBufferUtils::TexImageCtx mImageCtx; +}; + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp index 51c6a6cd1c..e3fec19f9b 100644 --- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp +++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp @@ -27,8 +27,13 @@ #include <include/gpu/gl/GrGLInterface.h> #include <include/gpu/vk/GrVkBackendContext.h> +#include "../AutoBackendTexture.h" +#include "GaneshBackendTexture.h" +#include "skia/compat/SkiaBackendTexture.h" + #include <android-base/macros.h> #include <log/log_main.h> +#include <memory> namespace android::renderengine::skia { @@ -66,6 +71,11 @@ sk_sp<GrDirectContext> GaneshGpuContext::grDirectContext() { return mGrContext; } +std::unique_ptr<SkiaBackendTexture> GaneshGpuContext::makeBackendTexture(AHardwareBuffer* buffer, + bool isOutputBuffer) { + return std::make_unique<GaneshBackendTexture>(mGrContext, buffer, isOutputBuffer); +} + sk_sp<SkSurface> GaneshGpuContext::createRenderTarget(SkImageInfo imageInfo) { constexpr int kSampleCount = 1; // enable AA constexpr SkSurfaceProps* kProps = nullptr; @@ -83,7 +93,7 @@ size_t GaneshGpuContext::getMaxTextureSize() const { return mGrContext->maxTextureSize(); }; -bool GaneshGpuContext::isAbandoned() { +bool GaneshGpuContext::isAbandonedOrDeviceLost() { return mGrContext->abandoned(); } diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.h b/libs/renderengine/skia/compat/GaneshGpuContext.h index 59001eccc2..d815d15e16 100644 --- a/libs/renderengine/skia/compat/GaneshGpuContext.h +++ b/libs/renderengine/skia/compat/GaneshGpuContext.h @@ -29,11 +29,14 @@ public: sk_sp<GrDirectContext> grDirectContext() override; + std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer, + bool isOutputBuffer) override; + sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) override; size_t getMaxRenderTargetSize() const override; size_t getMaxTextureSize() const override; - bool isAbandoned() override; + bool isAbandonedOrDeviceLost() override; void setResourceCacheLimit(size_t maxResourceBytes) override; void finishRenderingAndAbandonContext() override; diff --git a/libs/renderengine/skia/compat/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h new file mode 100644 index 0000000000..09877a5ede --- /dev/null +++ b/libs/renderengine/skia/compat/SkiaBackendTexture.h @@ -0,0 +1,85 @@ +/* + * Copyright 2024 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 <include/android/GrAHardwareBufferUtils.h> +#include <include/core/SkColorSpace.h> +#include <include/gpu/GrDirectContext.h> + +#include <android/hardware_buffer.h> +#include <ui/GraphicTypes.h> + +namespace android::renderengine::skia { + +/** + * Abstraction over a Skia backend-specific texture type. + * + * This class does not do any lifecycle management, and should typically be wrapped in an + * AutoBackendTexture::LocalRef. Typically created via SkiaGpuContext::makeBackendTexture(...). + */ +class SkiaBackendTexture { +public: + SkiaBackendTexture(AHardwareBuffer* buffer, bool isOutputBuffer) + : mIsOutputBuffer(isOutputBuffer) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + + mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); + } + virtual ~SkiaBackendTexture() = default; + + // These two definitions mirror Skia's own types used for texture release callbacks, which are + // re-declared multiple times between context-specific implementation headers for Ganesh vs. + // Graphite, and within the context of SkImages vs. SkSurfaces. Our own re-declaration allows us + // to not pull in any implementation-specific headers here. + using ReleaseContext = void*; + using TextureReleaseProc = void (*)(ReleaseContext); + + // Guaranteed to be non-null (crashes otherwise). An opaque alphaType may coerce the internal + // color type to RBGX. + virtual sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace, + TextureReleaseProc releaseImageProc, + ReleaseContext releaseContext) = 0; + + // Guaranteed to be non-null (crashes otherwise). + virtual sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace, + TextureReleaseProc releaseSurfaceProc, + ReleaseContext releaseContext) = 0; + + bool isOutputBuffer() const { return mIsOutputBuffer; } + + SkColorType internalColorType() const { return mColorType; } + +protected: + // Strip alpha channel from rawColorType if alphaType is opaque (note: only works for RGBA_8888) + SkColorType colorTypeForImage(SkAlphaType alphaType) const { + if (alphaType == kOpaque_SkAlphaType) { + // TODO: b/40043126 - Support RGBX SkColorType for F16 and support it and 101010x as a + // source + if (internalColorType() == kRGBA_8888_SkColorType) { + return kRGB_888x_SkColorType; + } + } + return internalColorType(); + } + +private: + const bool mIsOutputBuffer; + SkColorType mColorType = kUnknown_SkColorType; +}; + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h index ba167f352d..8222e6281d 100644 --- a/libs/renderengine/skia/compat/SkiaGpuContext.h +++ b/libs/renderengine/skia/compat/SkiaGpuContext.h @@ -24,8 +24,12 @@ #include <include/gpu/gl/GrGLInterface.h> #include <include/gpu/vk/GrVkBackendContext.h> +#include "SkiaBackendTexture.h" + #include <log/log.h> +#include <memory> + namespace android::renderengine::skia { /** @@ -52,6 +56,9 @@ public: LOG_ALWAYS_FATAL("grDirectContext() called on a non-Ganesh instance of SkiaGpuContext!"); } + virtual std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer, + bool isOutputBuffer) = 0; + /** * Notes: * - The surface doesn't count against Skia's caching budgets. @@ -61,7 +68,7 @@ public: */ virtual sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) = 0; - virtual bool isAbandoned() = 0; + virtual bool isAbandonedOrDeviceLost() = 0; virtual size_t getMaxRenderTargetSize() const = 0; virtual size_t getMaxTextureSize() const = 0; virtual void setResourceCacheLimit(size_t maxResourceBytes) = 0; diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 7b8eb8470f..3a07205ea6 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -2490,51 +2490,6 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { expectBufferColor(rect, 0, 128, 0, 128); } -TEST_P(RenderEngineTest, testBorder) { - if (!GetParam()->apiSupported()) { - GTEST_SKIP(); - } - - initializeRenderEngine(); - - const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB; - - const auto displayRect = Rect(1080, 2280); - renderengine::DisplaySettings display{ - .physicalDisplay = displayRect, - .clip = displayRect, - .outputDataspace = dataspace, - }; - display.borderInfoList.clear(); - renderengine::BorderRenderInfo info; - info.combinedRegion = Region(Rect(99, 99, 199, 199)); - info.width = 20.0f; - info.color = half4{1.0f, 128.0f / 255.0f, 0.0f, 1.0f}; - display.borderInfoList.emplace_back(info); - - const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255)); - const renderengine::LayerSettings greenLayer{ - .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f), - .source = - renderengine::PixelSource{ - .buffer = - renderengine::Buffer{ - .buffer = greenBuffer, - .usePremultipliedAlpha = true, - }, - }, - .alpha = 1.0f, - .sourceDataspace = dataspace, - .whitePointNits = 200.f, - }; - - std::vector<renderengine::LayerSettings> layers; - layers.emplace_back(greenLayer); - invokeDraw(display, layers); - - expectBufferColor(Rect(99, 99, 101, 101), 255, 128, 0, 255, 1); -} - TEST_P(RenderEngineTest, testDimming) { if (!GetParam()->apiSupported()) { GTEST_SKIP(); diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS index 90c233030e..7347ac74e6 100644 --- a/libs/sensor/OWNERS +++ b/libs/sensor/OWNERS @@ -1,3 +1 @@ -arthuri@google.com bduddie@google.com -stange@google.com diff --git a/libs/tracing_perfetto/TEST_MAPPING b/libs/tracing_perfetto/TEST_MAPPING new file mode 100644 index 0000000000..1805e1819d --- /dev/null +++ b/libs/tracing_perfetto/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "libtracing_perfetto_tests" + } + ] +} diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h index 4e3c83fca3..2c1c2a49e7 100644 --- a/libs/tracing_perfetto/include/tracing_perfetto.h +++ b/libs/tracing_perfetto/include/tracing_perfetto.h @@ -46,7 +46,7 @@ Result traceInstantForTrack(uint64_t category, const char* trackName, Result traceCounter(uint64_t category, const char* name, int64_t value); -uint64_t getEnabledCategories(); +bool isTagEnabled(uint64_t category); } // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp index 19d1eb639e..6f716eea9a 100644 --- a/libs/tracing_perfetto/tracing_perfetto.cpp +++ b/libs/tracing_perfetto/tracing_perfetto.cpp @@ -130,12 +130,13 @@ Result traceCounter(uint64_t category, const char* name, int64_t value) { } } -uint64_t getEnabledCategories() { - if (internal::isPerfettoRegistered()) { - // TODO(b/303199244): Return only enabled categories and not all registered ones - return internal::getDefaultCategories(); +bool isTagEnabled(uint64_t category) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return true; } else { - return atrace_get_enabled_tags(); + return (atrace_get_enabled_tags() & category) != 0; } } diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index 11064ae7fc..f14a5cf1a2 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -23,6 +23,7 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_manager.h> #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> +#include <android/llndk-versioning.h> #include <binder/IPCThreadState.h> #include <dlfcn.h> #include <ui/FatVector.h> @@ -91,8 +92,7 @@ static void *loadIMapperLibrary() { void* so = nullptr; // TODO(b/322384429) switch this to __ANDROID_API_V__ when V is finalized - // TODO(b/302113279) use __ANDROID_VENDOR_API__ for vendor variant - if (__builtin_available(android __ANDROID_API_FUTURE__, *)) { + if API_LEVEL_AT_LEAST(__ANDROID_API_FUTURE__, 202404) { so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(), RTLD_LOCAL | RTLD_NOW); } else { diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index e487cbc54d..af0bcffc1f 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -67,7 +67,6 @@ static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl"; static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl"; static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform"; static const char* ANGLE_SUFFIX_VALUE = "angle"; -static const char* VENDOR_ANGLE_BUILD = "ro.gfx.angle.supported"; static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = { PERSIST_DRIVER_SUFFIX_PROPERTY, @@ -494,14 +493,9 @@ static void* load_system_driver(const char* kind, const char* suffix, const bool void* dso = nullptr; - const bool AngleInVendor = property_get_bool(VENDOR_ANGLE_BUILD, false); const bool isSuffixAngle = suffix != nullptr && strcmp(suffix, ANGLE_SUFFIX_VALUE) == 0; - // Only use sphal namespace when system ANGLE binaries are not the default drivers. - const bool useSphalNamespace = !isSuffixAngle || AngleInVendor; - const std::string absolutePath = - findLibrary(libraryName, useSphalNamespace ? VENDOR_LIB_EGL_DIR : SYSTEM_LIB_PATH, - exact); + findLibrary(libraryName, isSuffixAngle ? SYSTEM_LIB_PATH : VENDOR_LIB_EGL_DIR, exact); if (absolutePath.empty()) { // this happens often, we don't want to log an error return nullptr; @@ -509,8 +503,9 @@ static void* load_system_driver(const char* kind, const char* suffix, const bool const char* const driverAbsolutePath = absolutePath.c_str(); // Currently the default driver is unlikely to be ANGLE on most devices, - // hence put this first. - if (useSphalNamespace) { + // hence put this first. Only use sphal namespace when system ANGLE binaries + // are not the default drivers. + if (!isSuffixAngle) { // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to // the original routine when the namespace does not exist. // See /system/linkerconfig/contents/namespace for the configuration of the diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index c333814078..1aa1077d54 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -37,6 +37,11 @@ bool isFromTouchpad(const NotifyMotionArgs& args) { args.pointerProperties[0].toolType == ToolType::FINGER; } +bool isFromDrawingTablet(const NotifyMotionArgs& args) { + return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) && + isStylusToolType(args.pointerProperties[0].toolType); +} + bool isHoverAction(int32_t action) { return action == AMOTION_EVENT_ACTION_HOVER_ENTER || action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT; @@ -46,6 +51,13 @@ bool isStylusHoverEvent(const NotifyMotionArgs& args) { return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action); } +bool isMouseOrTouchpad(uint32_t sources) { + // Check if this is a mouse or touchpad, but not a drawing tablet. + return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) || + (isFromSource(sources, AINPUT_SOURCE_MOUSE) && + !isFromSource(sources, AINPUT_SOURCE_STYLUS)); +} + inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPoint>> change, PointerChoreographerPolicyInterface& policy) { if (!change) { @@ -55,6 +67,18 @@ inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPo policy.notifyPointerDisplayIdChanged(displayId, cursorPosition); } +void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon, + PointerControllerInterface& controller) { + if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) { + if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) { + LOG(FATAL) << "SpriteIcon should not be null"; + } + controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon)); + } else { + controller.updatePointerIcon(std::get<PointerIconStyle>(icon)); + } +} + } // namespace // --- PointerChoreographer --- @@ -107,6 +131,8 @@ NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& arg return processMouseEventLocked(args); } else if (isFromTouchpad(args)) { return processTouchpadEventLocked(args); + } else if (isFromDrawingTablet(args)) { + processDrawingTabletEventLocked(args); } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) { processStylusHoverEventLocked(args); } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) { @@ -189,6 +215,36 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo return newArgs; } +void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) { + if (args.displayId == ADISPLAY_ID_NONE) { + return; + } + + if (args.getPointerCount() != 1) { + LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: " + << args.dump(); + } + + // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist. + auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId, + getMouseControllerConstructor( + args.displayId)); + + PointerControllerInterface& pc = *it->second; + + const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); + const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); + pc.setPosition(x, y); + if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { + // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed + // immediately by a DOWN event. + pc.fade(PointerControllerInterface::Transition::IMMEDIATE); + pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); + } else if (canUnfadeOnDisplay(args.displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } +} + /** * When screen is touched, fade the mouse pointer on that display. We only call fade for * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the @@ -255,6 +311,8 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); pc.setPosition(x, y); if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { + // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed + // immediately by a DOWN event. pc.fade(PointerControllerInterface::Transition::IMMEDIATE); pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); } else if (canUnfadeOnDisplay(args.displayId)) { @@ -284,6 +342,7 @@ void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) std::scoped_lock _l(mLock); mTouchPointersByDevice.erase(args.deviceId); mStylusPointersByDevice.erase(args.deviceId); + mDrawingTabletPointersByDevice.erase(args.deviceId); } void PointerChoreographer::notifyPointerCaptureChanged( @@ -320,6 +379,11 @@ void PointerChoreographer::dump(std::string& dump) { std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT); dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump; } + dump += INDENT "DrawingTabletControllers:\n"; + for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) { + std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT); + dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump; + } dump += "\n"; } @@ -361,13 +425,13 @@ PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerCo std::set<int32_t /*displayId*/> mouseDisplaysToKeep; std::set<DeviceId> touchDevicesToKeep; std::set<DeviceId> stylusDevicesToKeep; + std::set<DeviceId> drawingTabletDevicesToKeep; // Mark the displayIds or deviceIds of PointerControllers currently needed, and create // new PointerControllers if necessary. for (const auto& info : mInputDeviceInfos) { const uint32_t sources = info.getSources(); - if (isFromSource(sources, AINPUT_SOURCE_MOUSE) || - isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) { + if (isMouseOrTouchpad(sources)) { const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId()); mouseDisplaysToKeep.insert(displayId); // For mice, show the cursor immediately when the device is first connected or @@ -388,6 +452,10 @@ PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerCo info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) { stylusDevicesToKeep.insert(info.getId()); } + if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) && + info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) { + drawingTabletDevicesToKeep.insert(info.getId()); + } } // Remove PointerControllers no longer needed. @@ -400,6 +468,9 @@ PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerCo std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) { return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end(); }); + std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) { + return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end(); + }); std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) { return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(), [id](const auto& info) { return info.getId() == id; }) == @@ -460,6 +531,12 @@ void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport stylusPointerController->setDisplayViewport(viewport); } } + for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) { + const InputDeviceInfo* info = findInputDeviceLocked(deviceId); + if (info && info->getAssociatedDisplayId() == displayId) { + drawingTabletController->setDisplayViewport(viewport); + } + } } mViewports = viewports; pointerDisplayChange = calculatePointerDisplayChangeToNotify(); @@ -533,42 +610,35 @@ bool PointerChoreographer::setPointerIcon( return false; } const uint32_t sources = info->getSources(); - const auto stylusPointerIt = mStylusPointersByDevice.find(deviceId); - if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && - stylusPointerIt != mStylusPointersByDevice.end()) { - if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) { - if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) { - LOG(FATAL) << "SpriteIcon should not be null"; - } - stylusPointerIt->second->setCustomPointerIcon( - *std::get<std::unique_ptr<SpriteIcon>>(icon)); - } else { - stylusPointerIt->second->updatePointerIcon(std::get<PointerIconStyle>(icon)); + if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) { + auto it = mDrawingTabletPointersByDevice.find(deviceId); + if (it != mDrawingTabletPointersByDevice.end()) { + setIconForController(icon, *it->second); + return true; } - } else if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) { - if (const auto mousePointerIt = mMousePointersByDisplay.find(displayId); - mousePointerIt != mMousePointersByDisplay.end()) { - if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) { - if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) { - LOG(FATAL) << "SpriteIcon should not be null"; - } - mousePointerIt->second->setCustomPointerIcon( - *std::get<std::unique_ptr<SpriteIcon>>(icon)); - } else { - mousePointerIt->second->updatePointerIcon(std::get<PointerIconStyle>(icon)); - } + } + if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) { + auto it = mStylusPointersByDevice.find(deviceId); + if (it != mStylusPointersByDevice.end()) { + setIconForController(icon, *it->second); + return true; + } + } + if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) { + auto it = mMousePointersByDisplay.find(displayId); + if (it != mMousePointersByDisplay.end()) { + setIconForController(icon, *it->second); + return true; } else { LOG(WARNING) << "No mouse pointer controller found for display " << displayId << ", device " << deviceId << "."; return false; } - } else { - LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " - << deviceId << "."; - return false; } - return true; + LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId + << "."; + return false; } void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) { diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index db1488b546..a3c210e696 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -123,6 +123,7 @@ private: NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); void processDeviceReset(const NotifyDeviceResetArgs& args); @@ -144,6 +145,8 @@ private: GUARDED_BY(mLock); std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice GUARDED_BY(mLock); + std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice + GUARDED_BY(mLock); int32_t mDefaultMouseDisplayId GUARDED_BY(mLock); int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index d857723649..e331e8d0a3 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -599,6 +599,18 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float return true; } +// Returns true if the given window's frame can occlude pointer events at the given display +// location. +bool windowOccludesTouchAt(const WindowInfo& windowInfo, int displayId, float x, float y, + const ui::Transform& displayTransform) { + if (windowInfo.displayId != displayId) { + return false; + } + const auto frame = displayTransform.transform(windowInfo.frame); + const auto p = floor(displayTransform.transform(x, y)); + return p.x >= frame.left && p.x < frame.right && p.y >= frame.top && p.y < frame.bottom; +} + bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(entry.pointerProperties[pointerIndex].toolType); @@ -3108,7 +3120,7 @@ static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle, * If neither of those is true, then it means the touch can be allowed. */ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked( - const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const { + const sp<WindowInfoHandle>& windowHandle, float x, float y) const { const WindowInfo* windowInfo = windowHandle->getInfo(); int32_t displayId = windowInfo->displayId; const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); @@ -3122,7 +3134,8 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo break; // All future windows are below us. Exit early. } const WindowInfo* otherInfo = otherHandle->getInfo(); - if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) && + if (canBeObscuredBy(windowHandle, otherHandle) && + windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) && !haveSameApplicationToken(windowInfo, otherInfo)) { if (DEBUG_TOUCH_OCCLUSION) { info.debugInfo.push_back( @@ -3192,7 +3205,7 @@ bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionIn } bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle, - int32_t x, int32_t y) const { + float x, float y) const { int32_t displayId = windowHandle->getInfo()->displayId; const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { @@ -3201,7 +3214,7 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& } const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && - otherInfo->frameContainsPoint(x, y)) { + windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) { return true; } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index f884685e19..3579a67c96 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -568,11 +568,11 @@ private: }; TouchOcclusionInfo computeTouchOcclusionInfoLocked( - const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const + const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const REQUIRES(mLock); bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock); bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, - int32_t x, int32_t y) const REQUIRES(mLock); + float x, float y) const REQUIRES(mLock); bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info, diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp index 7e13047b61..77d09cbb4b 100644 --- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp +++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp @@ -38,10 +38,10 @@ struct Visitor : V... { template <typename Backend> ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend) - : mTracerThread( + : mBackend(std::move(innerBackend)), + mTracerThread( "InputTracer", [this]() { threadLoop(); }, - [this]() { mThreadWakeCondition.notify_all(); }), - mBackend(std::move(innerBackend)) {} + [this]() { mThreadWakeCondition.notify_all(); }) {} template <typename Backend> ThreadedBackend<Backend>::~ThreadedBackend() { diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h index f4659a83a8..650a87e452 100644 --- a/services/inputflinger/dispatcher/trace/ThreadedBackend.h +++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h @@ -44,7 +44,6 @@ public: private: std::mutex mLock; - InputThread mTracerThread; bool mThreadExit GUARDED_BY(mLock){false}; std::condition_variable mThreadWakeCondition; Backend mBackend; @@ -53,6 +52,11 @@ private: TracedEventMetadata>; std::vector<TraceEntry> mQueue GUARDED_BY(mLock); + // InputThread stops when its destructor is called. Initialize it last so that it is the + // first thing to be destructed. This will guarantee the thread will not access other + // members that have already been destructed. + InputThread mTracerThread; + void threadLoop(); }; diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 2baf576903..782c84a266 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -507,15 +507,7 @@ std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers( } // Touchscreens and touchpad devices. - static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY = - sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true); - // TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or - // at least load this setting from the IDC file. - const InputDeviceIdentifier identifier = contextPtr.getDeviceIdentifier(); - const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c && - (identifier.product == 0x05c4 || identifier.product == 0x09cc); - if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) && - classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) { + if (classes.test(InputDeviceClass::TOUCHPAD) && classes.test(InputDeviceClass::TOUCH_MT)) { mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig)); } else if (classes.test(InputDeviceClass::TOUCH_MT)) { mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig)); diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 99f9e24169..721cdfdfb0 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -198,6 +198,7 @@ private: } void produceAtomsLocked(AStatsEventList& outEventList) const REQUIRES(mLock) { + ALOGI("Producing touchpad usage atoms for %zu counters", mCounters.size()); for (auto& [id, counters] : mCounters) { auto [busId, vendorId, productId, versionId] = id; addAStatsEvent(&outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index fcb5ab0183..9e3a4f10ef 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -5369,6 +5369,94 @@ TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) window->assertNoEvents(); } +// This test verifies the occlusion detection for all rotations of the display by tapping +// in different locations on the display, specifically points close to the four corners of a +// window. +TEST_P(InputDispatcherDisplayOrientationFixture, BlockUntrustClickInDifferentOrientations) { + constexpr static int32_t displayWidth = 400; + constexpr static int32_t displayHeight = 800; + + std::shared_ptr<FakeApplicationHandle> untrustedWindowApplication = + std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + const auto rotation = GetParam(); + + // Set up the display with the specified rotation. + const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270; + const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth; + const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight; + const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation), + logicalDisplayWidth, logicalDisplayHeight); + addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform); + + // Create a window that not trusted. + const Rect untrustedWindowFrameInLogicalDisplay(100, 100, 200, 300); + + const Rect untrustedWindowFrameInDisplay = + displayTransform.inverse().transform(untrustedWindowFrameInLogicalDisplay); + + sp<FakeWindowHandle> untrustedWindow = + sp<FakeWindowHandle>::make(untrustedWindowApplication, mDispatcher, "UntrustedWindow", + ADISPLAY_ID_DEFAULT); + untrustedWindow->setFrame(untrustedWindowFrameInDisplay, displayTransform); + untrustedWindow->setTrustedOverlay(false); + untrustedWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); + untrustedWindow->setTouchable(false); + untrustedWindow->setAlpha(1.0f); + untrustedWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101}); + addWindow(untrustedWindow); + + // Create a simple app window below the untrusted window. + const Rect simpleAppWindowFrameInLogicalDisplay(0, 0, 300, 600); + const Rect simpleAppWindowFrameInDisplay = + displayTransform.inverse().transform(simpleAppWindowFrameInLogicalDisplay); + + sp<FakeWindowHandle> simpleAppWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "SimpleAppWindow", + ADISPLAY_ID_DEFAULT); + simpleAppWindow->setFrame(simpleAppWindowFrameInDisplay, displayTransform); + simpleAppWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202}); + addWindow(simpleAppWindow); + + // The following points in logical display space should be inside the untrusted window, so + // the simple window could not receive events that coordinate is these point. + static const std::array<vec2, 4> untrustedPoints{ + {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}}; + + for (const auto untrustedPoint : untrustedPoints) { + const vec2 p = displayTransform.inverse().transform(untrustedPoint); + const PointF pointInDisplaySpace{p.x, p.y}; + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + } + untrustedWindow->assertNoEvents(); + simpleAppWindow->assertNoEvents(); + // The following points in logical display space should be outside the untrusted window, so + // the simple window should receive events that coordinate is these point. + static const std::array<vec2, 5> trustedPoints{ + {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}}; + for (const auto trustedPoint : trustedPoints) { + const vec2 p = displayTransform.inverse().transform(trustedPoint); + const PointF pointInDisplaySpace{p.x, p.y}; + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + simpleAppWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + simpleAppWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + } + untrustedWindow->assertNoEvents(); +} + // Run the precision tests for all rotations. INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests, InputDispatcherDisplayOrientationFixture, diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 8ddb672cfe..3b2565e973 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -46,6 +46,7 @@ constexpr int32_t DISPLAY_ID = 5; constexpr int32_t ANOTHER_DISPLAY_ID = 10; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; +constexpr auto DRAWING_TABLET_SOURCE = AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS; const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE) .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10) @@ -759,12 +760,28 @@ TEST_F(PointerChoreographerTest, WhenTouchDeviceIsResetClearsSpots) { assertPointerControllerRemoved(pc); } -TEST_F(PointerChoreographerTest, - WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) { +using StylusFixtureParam = + std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>; + +class StylusTestFixture : public PointerChoreographerTest, + public ::testing::WithParamInterface<StylusFixtureParam> {}; + +INSTANTIATE_TEST_SUITE_P(PointerChoreographerTest, StylusTestFixture, + ::testing::Values(std::make_tuple("DirectStylus", AINPUT_SOURCE_STYLUS, + ControllerType::STYLUS), + std::make_tuple("DrawingTablet", DRAWING_TABLET_SOURCE, + ControllerType::MOUSE)), + [](const testing::TestParamInfo<StylusFixtureParam>& p) { + return std::string{std::get<0>(p.param)}; + }); + +TEST_P(StylusTestFixture, WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) { + const auto& [name, source, controllerType] = GetParam(); + // Disable stylus pointer icon and add a stylus device. mChoreographer.setStylusPointerIconEnabled(false); mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); assertPointerControllerNotCreated(); // Enable stylus pointer icon. PointerController still should not be created. @@ -772,25 +789,25 @@ TEST_F(PointerChoreographerTest, assertPointerControllerNotCreated(); } -TEST_F(PointerChoreographerTest, WhenStylusHoverEventOccursCreatesPointerController) { +TEST_P(StylusTestFixture, WhenStylusHoverEventOccursCreatesPointerController) { + const auto& [name, source, controllerType] = GetParam(); + // Add a stylus device and enable stylus pointer icon. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); assertPointerControllerNotCreated(); // Emit hover event. Now PointerController should be created. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + assertPointerControllerCreated(controllerType); } -TEST_F(PointerChoreographerTest, - WhenStylusPointerIconDisabledAndHoverEventOccursDoesNotCreatePointerController) { +TEST_F(PointerChoreographerTest, StylusHoverEventWhenStylusPointerIconDisabled) { // Add a stylus device and disable stylus pointer icon. mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); @@ -807,25 +824,43 @@ TEST_F(PointerChoreographerTest, assertPointerControllerNotCreated(); } -TEST_F(PointerChoreographerTest, WhenStylusDeviceIsRemovedRemovesPointerController) { - // Make sure the PointerController is created. +TEST_F(PointerChoreographerTest, DrawingTabletHoverEventWhenStylusPointerIconDisabled) { + // Add a drawing tablet and disable stylus pointer icon. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); - mChoreographer.setStylusPointerIconEnabled(true); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, DISPLAY_ID)}}); + mChoreographer.setStylusPointerIconEnabled(false); + assertPointerControllerNotCreated(); + + // Emit hover event. Drawing tablets are not affected by "stylus pointer icon" setting. mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, DRAWING_TABLET_SOURCE) .pointer(STYLUS_POINTER) .deviceId(DEVICE_ID) .displayId(DISPLAY_ID) .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + assertPointerControllerCreated(ControllerType::MOUSE); +} + +TEST_P(StylusTestFixture, WhenStylusDeviceIsRemovedRemovesPointerController) { + const auto& [name, source, controllerType] = GetParam(); + + // Make sure the PointerController is created. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); + mChoreographer.setStylusPointerIconEnabled(true); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); // Remove the device. mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); assertPointerControllerRemoved(pc); } -TEST_F(PointerChoreographerTest, WhenStylusPointerIconDisabledRemovesPointerController) { +TEST_F(PointerChoreographerTest, StylusPointerIconDisabledRemovesPointerController) { // Make sure the PointerController is created. mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); @@ -843,38 +878,59 @@ TEST_F(PointerChoreographerTest, WhenStylusPointerIconDisabledRemovesPointerCont assertPointerControllerRemoved(pc); } -TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) { - // Set viewport. - mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - +TEST_F(PointerChoreographerTest, + StylusPointerIconDisabledDoesNotRemoveDrawingTabletPointerController) { // Make sure the PointerController is created. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, DRAWING_TABLET_SOURCE) .pointer(STYLUS_POINTER) .deviceId(DEVICE_ID) .displayId(DISPLAY_ID) .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + + // Disable stylus pointer icon. This should not affect drawing tablets. + mChoreographer.setStylusPointerIconEnabled(false); + assertPointerControllerNotRemoved(pc); +} + +TEST_P(StylusTestFixture, SetsViewportForStylusPointerController) { + const auto& [name, source, controllerType] = GetParam(); + + // Set viewport. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Make sure the PointerController is created. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); + mChoreographer.setStylusPointerIconEnabled(true); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); // Check that viewport is set for the PointerController. pc->assertViewportSet(DISPLAY_ID); } -TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) { +TEST_P(StylusTestFixture, WhenViewportIsSetLaterSetsViewportForStylusPointerController) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure the PointerController is created. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); // Check that viewport is unset. pc->assertViewportNotSet(); @@ -886,19 +942,19 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPoin pc->assertViewportSet(DISPLAY_ID); } -TEST_F(PointerChoreographerTest, - WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) { +TEST_P(StylusTestFixture, WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure the PointerController is created. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); // Check that viewport is unset. pc->assertViewportNotSet(); @@ -910,24 +966,25 @@ TEST_F(PointerChoreographerTest, pc->assertViewportNotSet(); } -TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) { +TEST_P(StylusTestFixture, StylusHoverManipulatesPointer) { + const auto& [name, source, controllerType] = GetParam(); + mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); // Emit hover enter event. This is for creating PointerController. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); // Emit hover move event. After bounds are set, PointerController will update the position. mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, source) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250)) .deviceId(DEVICE_ID) .displayId(DISPLAY_ID) @@ -937,7 +994,7 @@ TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) { // Emit hover exit event. mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, source) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250)) .deviceId(DEVICE_ID) .displayId(DISPLAY_ID) @@ -946,38 +1003,38 @@ TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) { ASSERT_FALSE(pc->isPointerShown()); } -TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) { +TEST_P(StylusTestFixture, StylusHoverManipulatesPointerForTwoDisplays) { + const auto& [name, source, controllerType] = GetParam(); + mChoreographer.setStylusPointerIconEnabled(true); // Add two stylus devices associated to different displays. mChoreographer.notifyInputDevicesChanged( {/*id=*/0, - {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID), - generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, ANOTHER_DISPLAY_ID)}}); + {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID), + generateTestDeviceInfo(SECOND_DEVICE_ID, source, ANOTHER_DISPLAY_ID)}}); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); // Emit hover event with first device. This is for creating PointerController. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto firstDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto firstDisplayPc = assertPointerControllerCreated(controllerType); // Emit hover event with second device. This is for creating PointerController. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(SECOND_DEVICE_ID) - .displayId(ANOTHER_DISPLAY_ID) - .build()); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); // There should be another PointerController created. - auto secondDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS); + auto secondDisplayPc = assertPointerControllerCreated(controllerType); // Emit hover event with first device. mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, source) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250)) .deviceId(DEVICE_ID) .displayId(DISPLAY_ID) @@ -989,7 +1046,7 @@ TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) { // Emit hover event with second device. mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, source) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(250).y(350)) .deviceId(SECOND_DEVICE_ID) .displayId(ANOTHER_DISPLAY_ID) @@ -1004,19 +1061,20 @@ TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) { ASSERT_TRUE(firstDisplayPc->isPointerShown()); } -TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) { +TEST_P(StylusTestFixture, WhenStylusDeviceIsResetRemovesPointer) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure the PointerController is created and there is a pointer. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); ASSERT_TRUE(pc->isPointerShown()); // Reset the device and see the pointer controller was removed. @@ -1460,19 +1518,20 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) { firstMousePc->assertPointerIconNotSet(); } -TEST_F(PointerChoreographerTest, SetsPointerIconForStylus) { +TEST_P(StylusTestFixture, SetsPointerIconForStylus) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure there is a PointerController. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); pc->assertPointerIconNotSet(); // Set pointer icon for the device. @@ -1485,28 +1544,28 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForStylus) { pc->assertPointerIconNotSet(); // The stylus stops hovering. This should cause the icon to be reset. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); pc->assertPointerIconSet(PointerIconStyle::TYPE_NOT_SPECIFIED); } -TEST_F(PointerChoreographerTest, SetsCustomPointerIconForStylus) { +TEST_P(StylusTestFixture, SetsCustomPointerIconForStylus) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure there is a PointerController. mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(controllerType); pc->assertCustomPointerIconNotSet(); // Set custom pointer icon for the device. @@ -1522,28 +1581,28 @@ TEST_F(PointerChoreographerTest, SetsCustomPointerIconForStylus) { pc->assertCustomPointerIconNotSet(); } -TEST_F(PointerChoreographerTest, SetsPointerIconForTwoStyluses) { +TEST_P(StylusTestFixture, SetsPointerIconForTwoStyluses) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure there are two StylusPointerControllers. They can be on a same display. mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, - {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID), - generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID), + generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto firstStylusPc = assertPointerControllerCreated(ControllerType::STYLUS); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(SECOND_DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto secondStylusPc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto firstStylusPc = assertPointerControllerCreated(controllerType); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto secondStylusPc = assertPointerControllerCreated(controllerType); // Set pointer icon for one stylus. ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); @@ -1557,14 +1616,16 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForTwoStyluses) { firstStylusPc->assertPointerIconNotSet(); } -TEST_F(PointerChoreographerTest, SetsPointerIconForMouseAndStylus) { +TEST_P(StylusTestFixture, SetsPointerIconForMouseAndStylus) { + const auto& [name, source, controllerType] = GetParam(); + // Make sure there are PointerControllers for a mouse and a stylus. mChoreographer.setStylusPointerIconEnabled(true); mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), - generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}}); mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) @@ -1573,13 +1634,12 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseAndStylus) { .displayId(ADISPLAY_ID_NONE) .build()); auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(SECOND_DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); - auto stylusPc = assertPointerControllerCreated(ControllerType::STYLUS); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto stylusPc = assertPointerControllerCreated(controllerType); // Set pointer icon for the mouse. ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); @@ -1688,7 +1748,9 @@ TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad ASSERT_FALSE(touchpadPc->isPointerShown()); } -TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForStylus) { +TEST_P(StylusTestFixture, SetPointerIconVisibilityHidesPointerForStylus) { + const auto& [name, source, controllerType] = GetParam(); + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); mChoreographer.setStylusPointerIconEnabled(true); @@ -1696,15 +1758,14 @@ TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForStylus) mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); mChoreographer.notifyInputDevicesChanged( - {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) - .pointer(STYLUS_POINTER) - .deviceId(DEVICE_ID) - .displayId(DISPLAY_ID) - .build()); + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}}); + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); - auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + auto pc = assertPointerControllerCreated(controllerType); pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); // The pointer should not be visible. diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 3ea08febfd..4f65e77462 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -20,7 +20,6 @@ cc_library_shared { "PowerHintSessionWrapper.cpp", "PowerSaveState.cpp", "Temperature.cpp", - "WorkDuration.cpp", "WorkSource.cpp", ":libpowermanager_aidl", ], @@ -52,6 +51,10 @@ cc_library_shared { "android.hardware.power@1.3", ], + whole_static_libs: [ + "android.os.hintmanager_aidl-ndk", + ], + cflags: [ "-Wall", "-Werror", diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp deleted file mode 100644 index bd2b10a149..0000000000 --- a/services/powermanager/WorkDuration.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (C) 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. - */ - -#define LOG_TAG "WorkDuration" - -#include <android/WorkDuration.h> -#include <android/performance_hint.h> -#include <binder/Parcel.h> -#include <utils/Log.h> - -namespace android::os { - -WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos, - int64_t cpuDurationNanos, int64_t gpuDurationNanos) - : timestampNanos(0), - actualTotalDurationNanos(totalDurationNanos), - workPeriodStartTimestampNanos(startTimestampNanos), - actualCpuDurationNanos(cpuDurationNanos), - actualGpuDurationNanos(gpuDurationNanos) {} - -status_t WorkDuration::writeToParcel(Parcel* parcel) const { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - - parcel->writeInt64(workPeriodStartTimestampNanos); - parcel->writeInt64(actualTotalDurationNanos); - parcel->writeInt64(actualCpuDurationNanos); - parcel->writeInt64(actualGpuDurationNanos); - parcel->writeInt64(timestampNanos); - return OK; -} - -status_t WorkDuration::readFromParcel(const Parcel*) { - return INVALID_OPERATION; -} - -} // namespace android::os diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h deleted file mode 100644 index 26a575f834..0000000000 --- a/services/powermanager/include/android/WorkDuration.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 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 <binder/Parcelable.h> -#include <math.h> - -struct AWorkDuration {}; - -namespace android::os { - -/** - * C++ Parcelable version of {@link PerformanceHintManager.WorkDuration} that can be used in - * binder calls. - * This file needs to be kept in sync with the WorkDuration in - * frameworks/base/core/java/android/os/WorkDuration.java - */ -struct WorkDuration : AWorkDuration, android::Parcelable { - WorkDuration() = default; - ~WorkDuration() = default; - - WorkDuration(int64_t workPeriodStartTimestampNanos, int64_t actualTotalDurationNanos, - int64_t actualCpuDurationNanos, int64_t actualGpuDurationNanos); - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - inline bool equalsWithoutTimestamp(const WorkDuration& other) const { - return workPeriodStartTimestampNanos == other.workPeriodStartTimestampNanos && - actualTotalDurationNanos == other.actualTotalDurationNanos && - actualCpuDurationNanos == other.actualCpuDurationNanos && - actualGpuDurationNanos == other.actualGpuDurationNanos; - } - - bool operator==(const WorkDuration& other) const { - return timestampNanos == other.timestampNanos && equalsWithoutTimestamp(other); - } - - bool operator!=(const WorkDuration& other) const { return !(*this == other); } - - friend std::ostream& operator<<(std::ostream& os, const WorkDuration& workDuration) { - os << "{" - << "workPeriodStartTimestampNanos: " << workDuration.workPeriodStartTimestampNanos - << ", actualTotalDurationNanos: " << workDuration.actualTotalDurationNanos - << ", actualCpuDurationNanos: " << workDuration.actualCpuDurationNanos - << ", actualGpuDurationNanos: " << workDuration.actualGpuDurationNanos - << ", timestampNanos: " << workDuration.timestampNanos << "}"; - return os; - } - - int64_t timestampNanos; - int64_t actualTotalDurationNanos; - int64_t workPeriodStartTimestampNanos; - int64_t actualCpuDurationNanos; - int64_t actualGpuDurationNanos; -}; - -} // namespace android::os diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h index 7c10fa57ec..e32cc02974 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h @@ -73,6 +73,9 @@ public: // TODO(b/121291683): These will become private/internal virtual void preComposition(CompositionRefreshArgs&) = 0; + // Resolves any unfulfilled promises for release fences + virtual void postComposition(CompositionRefreshArgs&) = 0; + virtual FeatureFlags getFeatureFlags() const = 0; // Debugging diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 843b5c5c82..dd0f985e1c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -34,12 +34,6 @@ namespace android::compositionengine { using Layers = std::vector<sp<compositionengine::LayerFE>>; using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>; -struct BorderRenderInfo { - float width = 0; - half4 color; - std::vector<int32_t> layerIds; -}; - // Interface of composition engine power hint callback. struct ICEPowerCallback { virtual void notifyCpuLoadUp() = 0; @@ -101,8 +95,6 @@ struct CompositionRefreshArgs { // TODO (b/255601557): Calculate per display. std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime; - std::vector<BorderRenderInfo> borderInfoList; - bool hasTrustedPresentationListener = false; ICEPowerCallback* powerCallback = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index a499928dd0..4e080b356b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -133,6 +133,15 @@ public: uint64_t frameNumber = 0; }; + // Describes the states of the release fence. Checking the states allows checks + // to ensure that set_value() is not called on the same promise multiple times, + // and can indicate if the promise has been fulfilled. + enum class ReleaseFencePromiseStatus { + UNINITIALIZED, // Promise not created + INITIALIZED, // Promise created, fence has not been set + FULFILLED // Promise fulfilled, fence is set + }; + // Returns the LayerSettings to pass to RenderEngine::drawLayers. The state may contain shadows // casted by the layer or the content of the layer itself. If the layer does not render then an // empty optional will be returned. @@ -142,6 +151,19 @@ public: // Called after the layer is displayed to update the presentation fence virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0; + // Initializes a promise for a buffer release fence and provides the future for that + // fence. This should only be called when a promise has not yet been created, or + // after the previous promise has already been fulfilled. Attempting to call this + // when an existing promise is INITIALIZED will fail because the promise has not + // yet been fulfilled. + virtual ftl::Future<FenceResult> createReleaseFenceFuture() = 0; + + // Sets promise with its buffer's release fence + virtual void setReleaseFence(const FenceResult& releaseFence) = 0; + + // Checks if the buffer's release fence has been set + virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0; + // Gets some kind of identifier for the layer for debug purposes. virtual const char* getDebugName() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index f1d6f52eb8..e7d0afc9e2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -321,7 +321,9 @@ protected: const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0; virtual void setExpensiveRenderingExpected(bool enabled) = 0; + virtual void setHintSessionGpuStart(TimePoint startTime) = 0; virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0; + virtual void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) = 0; virtual bool isPowerHintSessionEnabled() = 0; virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0; virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h index c6995576a1..45208dd3c7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h @@ -48,6 +48,8 @@ public: void preComposition(CompositionRefreshArgs&) override; + void postComposition(CompositionRefreshArgs&) override; + FeatureFlags getFeatureFlags() const override; // Debugging diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 2dc9a1a49a..eaffa9e03a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -93,7 +93,9 @@ public: private: bool isPowerHintSessionEnabled() override; + void setHintSessionGpuStart(TimePoint startTime) override; void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; + void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override; DisplayId mId; bool mIsDisconnected = false; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 911d67b5ed..3671f15818 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -144,7 +144,9 @@ protected: std::vector<LayerFE*>& outLayerFEs) override; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; + void setHintSessionGpuStart(TimePoint startTime) override; void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; + void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override; bool isPowerHintSessionEnabled() override; void dumpBase(std::string&) const; @@ -162,7 +164,6 @@ protected: private: void dirtyEntireOutput(); - void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&); compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const; void finishPrepareFrame(); ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index 6b1c318d1c..f8ffde1e51 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -34,7 +34,6 @@ #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/ProjectionSpace.h> -#include <renderengine/BorderRenderInfo.h> #include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -166,8 +165,6 @@ struct OutputCompositionState { bool treat170mAsSrgb = false; - std::vector<renderengine::BorderRenderInfo> borderInfoList; - uint64_t lastOutputLayerHash = 0; uint64_t outputLayerHash = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h index 9b2387b966..a1b728232c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h @@ -52,6 +52,7 @@ public: MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&)); MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&)); + MOCK_METHOD1(postComposition, void(CompositionRefreshArgs&)); MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index 1b8cc2758f..05a5d3838c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -20,6 +20,7 @@ #include <compositionengine/LayerFECompositionState.h> #include <gmock/gmock.h> #include <ui/Fence.h> +#include "ui/FenceResult.h" namespace android::compositionengine::mock { @@ -52,6 +53,9 @@ public: MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack), (override)); + MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>()); + MOCK_METHOD1(setReleaseFence, void(const FenceResult&)); + MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus()); MOCK_CONST_METHOD0(getDebugName, const char*()); MOCK_CONST_METHOD0(getSequence, int32_t()); MOCK_CONST_METHOD0(hasRoundedCorners, bool()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 95ea3a4ed7..019a0583a3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -134,7 +134,9 @@ public: MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&)); MOCK_METHOD1(setPredictCompositionStrategy, void(bool)); MOCK_METHOD1(setTreat170mAsSrgb, void(bool)); + MOCK_METHOD(void, setHintSessionGpuStart, (TimePoint startTime)); MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence)); + MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine)); MOCK_METHOD(bool, isPowerHintSessionEnabled, ()); }; diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index b4702085b6..4c776879f0 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -162,6 +162,7 @@ void CompositionEngine::present(CompositionRefreshArgs& args) { future.get(); } } + postComposition(args); } void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) { @@ -192,6 +193,34 @@ void CompositionEngine::preComposition(CompositionRefreshArgs& args) { mNeedsAnotherUpdate = needsAnotherUpdate; } +// If a buffer is latched but the layer is not presented, such as when +// obscured by another layer, the previous buffer needs to be released. We find +// these buffers and fire a NO_FENCE to release it. This ensures that all +// promises for buffer releases are fulfilled at the end of composition. +void CompositionEngine::postComposition(CompositionRefreshArgs& args) { + if (FlagManager::getInstance().ce_fence_promise()) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + for (auto& layerFE : args.layers) { + if (layerFE->getReleaseFencePromiseStatus() == + LayerFE::ReleaseFencePromiseStatus::INITIALIZED) { + layerFE->setReleaseFence(Fence::NO_FENCE); + } + } + + // List of layersWithQueuedFrames does not necessarily overlap with + // list of layers, so those layersWithQueuedFrames also need any + // unfulfilled promises to be resolved for completeness. + for (auto& layerFE : args.layersWithQueuedFrames) { + if (layerFE->getReleaseFencePromiseStatus() == + LayerFE::ReleaseFencePromiseStatus::INITIALIZED) { + layerFE->setReleaseFence(Fence::NO_FENCE); + } + } + } +} + FeatureFlags CompositionEngine::getFeatureFlags() const { return {}; } diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 6428d089a7..3d35704a12 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -252,10 +252,6 @@ bool Display::chooseCompositionStrategy( auto& hwc = getCompositionEngine().getHwComposer(); const bool requiresClientComposition = anyLayersRequireClientComposition(); - if (isPowerHintSessionEnabled()) { - mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); - } - const TimePoint hwcValidateStartTime = TimePoint::now(); if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition, @@ -416,10 +412,20 @@ bool Display::isPowerHintSessionEnabled() { return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession(); } +// For ADPF GPU v0 this is expected to set start time to when the GPU commands are submitted with +// fence returned, i.e. when RenderEngine flushes the commands and returns the draw fence. +void Display::setHintSessionGpuStart(TimePoint startTime) { + mPowerAdvisor->setGpuStartTime(mId, startTime); +} + void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) { mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence)); } +void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) { + mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine); +} + void Display::finishFrame(GpuCompositionResult&& result) { // We only need to actually compose the display if: // 1) It is being handled by hardware composer, which may need this to diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 921e05dfb2..1f01b5754b 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -16,6 +16,7 @@ #include <SurfaceFlingerProperties.sysprop.h> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayColorProfile.h> @@ -463,6 +464,10 @@ ftl::Future<std::monostate> Output::present( setColorTransform(refreshArgs); beginFrame(); + if (isPowerHintSessionEnabled()) { + // always reset the flag before the composition prediction + setHintSessionRequiresRenderEngine(false); + } GpuCompositionResult result; const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs); if (predictCompositionStrategy) { @@ -818,44 +823,6 @@ void Output::updateCompositionState(const compositionengine::CompositionRefreshA forceClientComposition = false; } } - - updateCompositionStateForBorder(refreshArgs); -} - -void Output::updateCompositionStateForBorder( - const compositionengine::CompositionRefreshArgs& refreshArgs) { - std::unordered_map<int32_t, const Region*> layerVisibleRegionMap; - // Store a map of layerId to their computed visible region. - for (auto* layer : getOutputLayersOrderedByZ()) { - int layerId = (layer->getLayerFE()).getSequence(); - layerVisibleRegionMap[layerId] = &((layer->getState()).visibleRegion); - } - OutputCompositionState& outputCompositionState = editState(); - outputCompositionState.borderInfoList.clear(); - bool clientComposeTopLayer = false; - for (const auto& borderInfo : refreshArgs.borderInfoList) { - renderengine::BorderRenderInfo info; - for (const auto& id : borderInfo.layerIds) { - info.combinedRegion.orSelf(*(layerVisibleRegionMap[id])); - } - - if (!info.combinedRegion.isEmpty()) { - info.width = borderInfo.width; - info.color = borderInfo.color; - outputCompositionState.borderInfoList.emplace_back(std::move(info)); - clientComposeTopLayer = true; - } - } - - // In this situation we must client compose the top layer instead of using hwc - // because we want to draw the border above all else. - // This could potentially cause a bit of a performance regression if the top - // layer would have been rendered using hwc originally. - // TODO(b/227656283): Measure system's performance before enabling the border feature - if (clientComposeTopLayer) { - auto topLayer = getOutputLayerOrderedByZByIndex(getOutputLayerCount() - 1); - (topLayer->editState()).forceClientComposition = true; - } } void Output::planComposition() { @@ -1247,8 +1214,7 @@ void Output::finishFrame(GpuCompositionResult&& result) { if (!optReadyFence) { return; } - - if (isPowerHintSessionEnabled()) { + if (isPowerHintSessionEnabled() && !FlagManager::getInstance().adpf_gpu_sf()) { // get fence end time to know when gpu is complete in display setHintSessionGpuFence( std::make_unique<FenceTime>(sp<Fence>::make(dup(optReadyFence->get())))); @@ -1392,8 +1358,20 @@ std::optional<base::unique_fd> Output::composeSurfaces( // If rendering was not successful, remove the request from the cache. mClientCompositionRequestCache->remove(tex->getBuffer()->getId()); } - const auto fence = std::move(fenceResult).value_or(Fence::NO_FENCE); + if (isPowerHintSessionEnabled()) { + if (fence != Fence::NO_FENCE && fence->isValid() && + !outputCompositionState.reusedClientComposition) { + setHintSessionRequiresRenderEngine(true); + if (FlagManager::getInstance().adpf_gpu_sf()) { + // the order of the two calls here matters as we should check if the previously + // tracked fence has signaled first and archive the previous start time + setHintSessionGpuStart(TimePoint::now()); + setHintSessionGpuFence( + std::make_unique<FenceTime>(sp<Fence>::make(dup(fence->get())))); + } + } + } if (auto timeStats = getCompositionEngine().getTimeStats()) { if (fence->isValid()) { @@ -1443,13 +1421,6 @@ renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings( // Compute the global color transform matrix. clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; - for (auto& info : outputState.borderInfoList) { - renderengine::BorderRenderInfo borderInfo; - borderInfo.width = info.width; - borderInfo.color = info.color; - borderInfo.combinedRegion = info.combinedRegion; - clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo)); - } clientCompositionDisplay.deviceHandlesColorTransform = outputState.usesDeviceComposition || getSkipColorTransform(); return clientCompositionDisplay; @@ -1576,10 +1547,18 @@ void Output::setExpensiveRenderingExpected(bool) { // The base class does nothing with this call. } +void Output::setHintSessionGpuStart(TimePoint) { + // The base class does nothing with this call. +} + void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) { // The base class does nothing with this call. } +void Output::setHintSessionRequiresRenderEngine(bool) { + // The base class does nothing with this call. +} + bool Output::isPowerHintSessionEnabled() { return false; } @@ -1621,9 +1600,13 @@ void Output::presentFrameAndReleaseLayers() { releaseFence = Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence); } - layer->getLayerFE() - .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(), - outputState.layerFilter.layerStack); + if (FlagManager::getInstance().ce_fence_promise()) { + layer->getLayerFE().setReleaseFence(releaseFence); + } else { + layer->getLayerFE() + .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(), + outputState.layerFilter.layerStack); + } } // We've got a list of layers needing fences, that are disjoint with @@ -1631,8 +1614,12 @@ void Output::presentFrameAndReleaseLayers() { // supply them with the present fence. for (auto& weakLayer : mReleasedLayers) { if (const auto layer = weakLayer.promote()) { - layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(), - outputState.layerFilter.layerStack); + if (FlagManager::getInstance().ce_fence_promise()) { + layer->setReleaseFence(frame.presentFence); + } else { + layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(), + outputState.layerFilter.layerStack); + } } } diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 39cf67165a..6683e67029 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -67,11 +67,6 @@ void OutputCompositionState::dump(std::string& out) const { out.append("\n "); dumpVal(out, "treat170mAsSrgb", treat170mAsSrgb); - - out.append("\n"); - for (const auto& borderRenderInfo : borderInfoList) { - dumpVal(out, "borderRegion", borderRenderInfo.combinedRegion); - } out.append("\n"); } diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index 042010edd5..639164d8c9 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -28,6 +28,7 @@ #include "MockHWComposer.h" #include "TimeStats/TimeStats.h" +#include "gmock/gmock.h" #include <variant> @@ -90,14 +91,16 @@ struct CompositionEnginePresentTest : public CompositionEngineTest { // These are the overridable functions CompositionEngine::present() may // call, and have separate test coverage. MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&)); + MOCK_METHOD1(postComposition, void(CompositionRefreshArgs&)); }; StrictMock<CompositionEnginePartialMock> mEngine; }; TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) { - // present() always calls preComposition() + // present() always calls preComposition() and postComposition() EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs))); + EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs))); mEngine.present(mRefreshArgs); } @@ -126,6 +129,9 @@ TEST_F(CompositionEnginePresentTest, worksAsExpected) { EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs))) .WillOnce(Return(ftl::yield<std::monostate>({}))); + // present() always calls postComposition() + EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs))); + mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3}; mEngine.present(mRefreshArgs); } @@ -481,5 +487,29 @@ TEST_F(CompositionEngineOffloadTest, disabledDisplaysDoNotPreventOthersFromOfflo mEngine.present(mRefreshArgs); } +struct CompositionEnginePostCompositionTest : public CompositionEngineTest { + sp<StrictMock<mock::LayerFE>> mLayer1FE = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> mLayer2FE = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> mLayer3FE = sp<StrictMock<mock::LayerFE>>::make(); +}; + +TEST_F(CompositionEnginePostCompositionTest, postCompositionReleasesAllFences) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true); + ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise()); + + EXPECT_CALL(*mLayer1FE, getReleaseFencePromiseStatus) + .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED)); + EXPECT_CALL(*mLayer2FE, getReleaseFencePromiseStatus) + .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED)); + EXPECT_CALL(*mLayer3FE, getReleaseFencePromiseStatus) + .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::INITIALIZED)); + mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE}; + + EXPECT_CALL(*mLayer1FE, setReleaseFence(_)).Times(0); + EXPECT_CALL(*mLayer2FE, setReleaseFence(_)).Times(0); + EXPECT_CALL(*mLayer3FE, setReleaseFence(_)).Times(1); + + mEngine.postComposition(mRefreshArgs); +} } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index 7253354e0f..d0843a20de 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -42,6 +42,7 @@ public: MOCK_METHOD(void, reportActualWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override)); + MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override)); MOCK_METHOD(void, setGpuFenceTime, (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); MOCK_METHOD(void, setHwcValidateTiming, @@ -51,8 +52,8 @@ public: (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime), (override)); MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); - MOCK_METHOD(void, setRequiresClientComposition, - (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine), + (override)); MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override)); MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime), (override)); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 799c7edebd..6be245eb7d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -2017,8 +2017,15 @@ struct OutputPresentTest : public testing::Test { MOCK_METHOD0(presentFrameAndReleaseLayers, void()); MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&)); + MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine), + (override)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override)); }; + OutputPresentTest() { + EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true)); + } + StrictMock<OutputPartialMock> mOutput; }; @@ -2032,6 +2039,7 @@ TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) { EXPECT_CALL(mOutput, writeCompositionState(Ref(args))); EXPECT_CALL(mOutput, setColorTransform(Ref(args))); EXPECT_CALL(mOutput, beginFrame()); + EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(false)); EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false)); EXPECT_CALL(mOutput, prepareFrame()); EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args))); @@ -2052,6 +2060,7 @@ TEST_F(OutputPresentTest, predictingCompositionStrategyInvokesPrepareFrameAsync) EXPECT_CALL(mOutput, writeCompositionState(Ref(args))); EXPECT_CALL(mOutput, setColorTransform(Ref(args))); EXPECT_CALL(mOutput, beginFrame()); + EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(false)); EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true)); EXPECT_CALL(mOutput, prepareFrameAsync()); EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args))); @@ -2989,6 +2998,9 @@ struct OutputFinishFrameTest : public testing::Test { MOCK_METHOD0(updateProtectedContentState, void()); MOCK_METHOD2(dequeueRenderBuffer, bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence), + (override)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override)); }; OutputFinishFrameTest() { @@ -2997,6 +3009,7 @@ struct OutputFinishFrameTest : public testing::Test { mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine)); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); + EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true)); } StrictMock<OutputPartialMock> mOutput; @@ -3014,6 +3027,7 @@ TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) { } TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, true); mOutput.mState.isEnabled = true; EXPECT_CALL(mOutput, updateProtectedContentState()); EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true)); @@ -3023,7 +3037,24 @@ TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) { mOutput.finishFrame(std::move(result)); } +TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFenceWithAdpfGpuOff) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, false); + mOutput.mState.isEnabled = true; + + InSequence seq; + EXPECT_CALL(mOutput, updateProtectedContentState()); + EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true)); + EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _)) + .WillOnce(Return(ByMove(base::unique_fd()))); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)); + EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f)); + + impl::GpuCompositionResult result; + mOutput.finishFrame(std::move(result)); +} + TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, true); mOutput.mState.isEnabled = true; InSequence seq; @@ -3031,6 +3062,7 @@ TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) { EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true)); EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _)) .WillOnce(Return(ByMove(base::unique_fd()))); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0); EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f)); impl::GpuCompositionResult result; @@ -3039,6 +3071,7 @@ TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) { TEST_F(OutputFinishFrameTest, queuesBufferWithHdrSdrRatio) { SET_FLAG_FOR_TEST(flags::fp16_client_target, true); + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, true); mOutput.mState.isEnabled = true; InSequence seq; @@ -3058,6 +3091,7 @@ TEST_F(OutputFinishFrameTest, queuesBufferWithHdrSdrRatio) { .WillOnce(DoAll(SetArgPointee<1>(texture), Return(true))); EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _)) .WillOnce(Return(ByMove(base::unique_fd()))); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0); EXPECT_CALL(*mRenderSurface, queueBuffer(_, 2.f)); impl::GpuCompositionResult result; @@ -3065,9 +3099,11 @@ TEST_F(OutputFinishFrameTest, queuesBufferWithHdrSdrRatio) { } TEST_F(OutputFinishFrameTest, predictionSucceeded) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, true); mOutput.mState.isEnabled = true; mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::SUCCESS; InSequence seq; + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0); EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f)); impl::GpuCompositionResult result; @@ -3075,6 +3111,7 @@ TEST_F(OutputFinishFrameTest, predictionSucceeded) { } TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, true); mOutput.mState.isEnabled = true; mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::FAIL; @@ -3090,6 +3127,7 @@ TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) { composeSurfaces(RegionEq(Region::INVALID_REGION), result.buffer, Eq(ByRef(result.fence)))) .WillOnce(Return(ByMove(base::unique_fd()))); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0); EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f)); mOutput.finishFrame(std::move(result)); } @@ -3164,6 +3202,8 @@ TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCom } TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false); + ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise()); // Simulate getting release fences from each layer, and ensure they are passed to the // front-end layer interface for each layer correctly. @@ -3205,7 +3245,51 @@ TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) { mOutput.presentFrameAndReleaseLayers(); } +TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true); + ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise()); + // Simulate getting release fences from each layer, and ensure they are passed to the + // front-end layer interface for each layer correctly. + + mOutput.mState.isEnabled = true; + + // Create three unique fence instances + sp<Fence> layer1Fence = sp<Fence>::make(); + sp<Fence> layer2Fence = sp<Fence>::make(); + sp<Fence> layer3Fence = sp<Fence>::make(); + + Output::FrameFences frameFences; + frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence); + frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence); + frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence); + + EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences)); + EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); + + // Compare the pointers values of each fence to make sure the correct ones + // are passed. This happens to work with the current implementation, but + // would not survive certain calls like Fence::merge() which would return a + // new instance. + EXPECT_CALL(*mLayer1.layerFE, setReleaseFence(_)) + .WillOnce([&layer1Fence](FenceResult releaseFence) { + EXPECT_EQ(FenceResult(layer1Fence), releaseFence); + }); + EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_)) + .WillOnce([&layer2Fence](FenceResult releaseFence) { + EXPECT_EQ(FenceResult(layer2Fence), releaseFence); + }); + EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_)) + .WillOnce([&layer3Fence](FenceResult releaseFence) { + EXPECT_EQ(FenceResult(layer3Fence), releaseFence); + }); + + mOutput.presentFrameAndReleaseLayers(); +} + TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false); + ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise()); + mOutput.mState.isEnabled = true; mOutput.mState.usesClientComposition = true; @@ -3228,7 +3312,35 @@ TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) mOutput.presentFrameAndReleaseLayers(); } +TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true); + ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise()); + + mOutput.mState.isEnabled = true; + mOutput.mState.usesClientComposition = true; + + Output::FrameFences frameFences; + frameFences.clientTargetAcquireFence = sp<Fence>::make(); + frameFences.layerFences.emplace(&mLayer1.hwc2Layer, sp<Fence>::make()); + frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make()); + frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make()); + + EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences)); + EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); + + // Fence::merge is called, and since none of the fences are actually valid, + // Fence::NO_FENCE is returned and passed to each setReleaseFence() call. + // This is the best we can do without creating a real kernel fence object. + EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return()); + EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return()); + EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return()); + mOutput.presentFrameAndReleaseLayers(); +} + TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false); + ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise()); + mOutput.mState.isEnabled = true; mOutput.mState.usesClientComposition = true; @@ -3276,6 +3388,54 @@ TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty()); } +TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true); + ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise()); + + mOutput.mState.isEnabled = true; + mOutput.mState.usesClientComposition = true; + + // This should happen even if there are no (current) output layers. + EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); + + // Load up the released layers with some mock instances + sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make(); + Output::ReleasedLayers layers; + layers.push_back(releasedLayer1); + layers.push_back(releasedLayer2); + layers.push_back(releasedLayer3); + mOutput.setReleasedLayers(std::move(layers)); + + // Set up a fake present fence + sp<Fence> presentFence = sp<Fence>::make(); + Output::FrameFences frameFences; + frameFences.presentFence = presentFence; + + EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences)); + EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); + + // Each released layer should be given the presentFence. + EXPECT_CALL(*releasedLayer1, setReleaseFence(_)) + .WillOnce([&presentFence](FenceResult fenceResult) { + EXPECT_EQ(FenceResult(presentFence), fenceResult); + }); + EXPECT_CALL(*releasedLayer2, setReleaseFence(_)) + .WillOnce([&presentFence](FenceResult fenceResult) { + EXPECT_EQ(FenceResult(presentFence), fenceResult); + }); + EXPECT_CALL(*releasedLayer3, setReleaseFence(_)) + .WillOnce([&presentFence](FenceResult fenceResult) { + EXPECT_EQ(FenceResult(presentFence), fenceResult); + }); + + mOutput.presentFrameAndReleaseLayers(); + + // After the call the list of released layers should have been cleared. + EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty()); +} + /* * Output::composeSurfaces() */ @@ -3293,8 +3453,10 @@ struct OutputComposeSurfacesTest : public testing::Test { MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); + MOCK_METHOD(void, setHintSessionGpuStart, (TimePoint startTime), (override)); MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence), (override)); + MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool), (override)); MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override)); }; @@ -3325,6 +3487,7 @@ struct OutputComposeSurfacesTest : public testing::Test { EXPECT_CALL(mCompositionEngine, getTimeStats()).WillRepeatedly(Return(mTimeStats.get())); EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()) .WillRepeatedly(ReturnRef(kHdrCapabilities)); + EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true)); } struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> { @@ -3590,11 +3753,59 @@ TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) { EXPECT_FALSE(mOutput.mState.reusedClientComposition); // We do not expect another call to draw layers. + EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(_)).Times(0); + EXPECT_CALL(mOutput, setHintSessionGpuStart(_)).Times(0); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0); verify().execute().expectAFenceWasReturned(); EXPECT_TRUE(mOutput.mState.reusedClientComposition); } +TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChangesWithAdpfGpuOff) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, false); + LayerFE::LayerSettings r1; + LayerFE::LayerSettings r2; + + r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; + r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; + + EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); + EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _)) + .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); + EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) + .WillRepeatedly(Return()); + + const auto otherOutputBuffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)) + .WillOnce(Return(mOutputBuffer)) + .WillOnce(Return(otherOutputBuffer)); + base::unique_fd fd(open("/dev/null", O_RDONLY)); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) + .WillRepeatedly([&](const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, + base::unique_fd&&) -> ftl::Future<FenceResult> { + return ftl::yield<FenceResult>(sp<Fence>::make(std::move(fd))); + }); + + EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(true)); + EXPECT_CALL(mOutput, setHintSessionGpuStart(_)).Times(0); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0); + verify().execute().expectAFenceWasReturned(); + EXPECT_FALSE(mOutput.mState.reusedClientComposition); + + verify().execute().expectAFenceWasReturned(); + EXPECT_FALSE(mOutput.mState.reusedClientComposition); +} + TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { + SET_FLAG_FOR_TEST(flags::adpf_gpu_sf, true); LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; @@ -3618,14 +3829,18 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)) .WillOnce(Return(mOutputBuffer)) .WillOnce(Return(otherOutputBuffer)); + base::unique_fd fd(open("/dev/null", O_RDONLY)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { - return ftl::yield<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(sp<Fence>::make(std::move(fd))); }); + EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(true)); + EXPECT_CALL(mOutput, setHintSessionGpuStart(_)); + EXPECT_CALL(mOutput, setHintSessionGpuFence(_)); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index faa51972e9..96cf84c945 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -270,27 +270,30 @@ void PowerAdvisor::reportActualWorkDuration() { return; } ATRACE_CALL(); - std::optional<Duration> actualDuration = estimateWorkDuration(); - if (!actualDuration.has_value() || actualDuration < 0ns) { + std::optional<WorkDuration> actualDuration = estimateWorkDuration(); + if (!actualDuration.has_value() || actualDuration->durationNanos < 0) { ALOGV("Failed to send actual work duration, skipping"); return; } - actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin); - mActualDuration = actualDuration; - + actualDuration->durationNanos += sTargetSafetyMargin.ns(); if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", actualDuration->ns()); - ATRACE_INT64("Target error term", Duration{*actualDuration - mTargetDuration}.ns()); - ATRACE_INT64("Reported duration", actualDuration->ns()); + ATRACE_INT64("Measured duration", actualDuration->durationNanos); + ATRACE_INT64("Target error term", actualDuration->durationNanos - mTargetDuration.ns()); + ATRACE_INT64("Reported duration", actualDuration->durationNanos); + if (FlagManager::getInstance().adpf_gpu_sf()) { + ATRACE_INT64("Reported cpu duration", actualDuration->cpuDurationNanos); + ATRACE_INT64("Reported gpu duration", actualDuration->gpuDurationNanos); + } ATRACE_INT64("Reported target", mLastTargetDurationSent.ns()); ATRACE_INT64("Reported target error term", - Duration{*actualDuration - mLastTargetDurationSent}.ns()); + actualDuration->durationNanos - mLastTargetDurationSent.ns()); } - ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 - " with error: %" PRId64, - actualDuration->ns(), mLastTargetDurationSent.ns(), - Duration{*actualDuration - mLastTargetDurationSent}.ns()); + ALOGV("Sending actual work duration of: %" PRId64 " with cpu: %" PRId64 " and gpu: %" PRId64 + " on reported target: %" PRId64 " with error: %" PRId64, + actualDuration->durationNanos, actualDuration->cpuDurationNanos, + actualDuration->gpuDurationNanos, mLastTargetDurationSent.ns(), + actualDuration->durationNanos - mLastTargetDurationSent.ns()); if (mTimingTestingMode) { mDelayReportActualMutexAcquisitonPromise.get_future().wait(); @@ -303,17 +306,7 @@ void PowerAdvisor::reportActualWorkDuration() { ALOGV("Hint session not running and could not be started, skipping"); return; } - - WorkDuration duration{ - .timeStampNanos = TimePoint::now().ns(), - // TODO(b/284324521): Correctly calculate total duration. - .durationNanos = actualDuration->ns(), - .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(), - .cpuDurationNanos = actualDuration->ns(), - // TODO(b/284324521): Calculate RenderEngine GPU time. - .gpuDurationNanos = 0, - }; - mHintSessionQueue.push_back(duration); + mHintSessionQueue.push_back(*actualDuration); auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue); if (!ret.isOk()) { @@ -348,11 +341,36 @@ bool PowerAdvisor::startPowerHintSession(std::vector<int32_t>&& threadIds) { return ensurePowerHintSessionRunning(); } -void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) { +void PowerAdvisor::setGpuStartTime(DisplayId displayId, TimePoint startTime) { DisplayTimingData& displayData = mDisplayTimingData[displayId]; if (displayData.gpuEndFenceTime) { nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime(); if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) { + displayData.lastValidGpuStartTime = displayData.gpuStartTime; + displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime); + for (auto&& [_, otherDisplayData] : mDisplayTimingData) { + if (!otherDisplayData.lastValidGpuStartTime.has_value() || + !otherDisplayData.lastValidGpuEndTime.has_value()) + continue; + if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) && + (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) { + displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime; + break; + } + } + } + displayData.gpuEndFenceTime = nullptr; + } + displayData.gpuStartTime = startTime; +} + +void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + if (displayData.gpuEndFenceTime && !FlagManager::getInstance().adpf_gpu_sf()) { + nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime(); + if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) { + displayData.lastValidGpuStartTime = displayData.gpuStartTime; + displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime); for (auto&& [_, otherDisplayData] : mDisplayTimingData) { // If the previous display started before us but ended after we should have // started, then it likely delayed our start time and we must compensate for that. @@ -365,12 +383,12 @@ void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTim break; } } - displayData.lastValidGpuStartTime = displayData.gpuStartTime; - displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime); } } displayData.gpuEndFenceTime = std::move(fenceTime); - displayData.gpuStartTime = TimePoint::now(); + if (!FlagManager::getInstance().adpf_gpu_sf()) { + displayData.gpuStartTime = TimePoint::now(); + } } void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime, @@ -391,9 +409,8 @@ void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) { mDisplayTimingData[displayId].skippedValidate = skipped; } -void PowerAdvisor::setRequiresClientComposition(DisplayId displayId, - bool requiresClientComposition) { - mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition; +void PowerAdvisor::setRequiresRenderEngine(DisplayId displayId, bool requiresRenderEngine) { + mDisplayTimingData[displayId].requiresRenderEngine = requiresRenderEngine; } void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) { @@ -401,8 +418,8 @@ void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) { } void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) { - mLastSfPresentEndTime = presentEndTime; mLastPresentFenceTime = presentFenceTime; + mLastSfPresentEndTime = presentEndTime; } void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) { @@ -443,7 +460,7 @@ std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( return sortedDisplays; } -std::optional<Duration> PowerAdvisor::estimateWorkDuration() { +std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() { if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) { return std::nullopt; } @@ -462,11 +479,10 @@ std::optional<Duration> PowerAdvisor::estimateWorkDuration() { // used to accumulate gpu time as we iterate over the active displays std::optional<TimePoint> estimatedGpuEndTime; - // The timing info for the previously calculated display, if there was one - std::optional<DisplayTimeline> previousDisplayTiming; std::vector<DisplayId>&& displayIds = getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime); DisplayTimeline displayTiming; + std::optional<GpuTimeline> firstGpuTimeline; // Iterate over the displays that use hwc in the same order they are presented for (DisplayId displayId : displayIds) { @@ -478,14 +494,6 @@ std::optional<Duration> PowerAdvisor::estimateWorkDuration() { displayTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime); - // If this is the first display, include the duration before hwc present starts - if (!previousDisplayTiming.has_value()) { - estimatedHwcEndTime += displayTiming.hwcPresentStartTime - mCommitStartTimes[0]; - } else { // Otherwise add the time since last display's hwc present finished - estimatedHwcEndTime += - displayTiming.hwcPresentStartTime - previousDisplayTiming->hwcPresentEndTime; - } - // Update predicted present finish time with this display's present time estimatedHwcEndTime = displayTiming.hwcPresentEndTime; @@ -500,6 +508,9 @@ std::optional<Duration> PowerAdvisor::estimateWorkDuration() { // Estimate the reference frame's gpu timing auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime); if (gpuTiming.has_value()) { + if (!firstGpuTimeline.has_value()) { + firstGpuTimeline = gpuTiming; + } previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration; // Estimate the prediction frame's gpu end time from the reference frame @@ -507,9 +518,7 @@ std::optional<Duration> PowerAdvisor::estimateWorkDuration() { estimatedGpuEndTime.value_or(TimePoint{0ns})) + gpuTiming->duration; } - previousDisplayTiming = displayTiming; } - ATRACE_INT64("Idle duration", idleDuration.ns()); TimePoint estimatedFlingerEndTime = mLastSfPresentEndTime; @@ -522,15 +531,34 @@ std::optional<Duration> PowerAdvisor::estimateWorkDuration() { Duration totalDuration = mFrameDelayDuration + std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) - mCommitStartTimes[0]; + Duration totalDurationWithoutGpu = + mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes[0]; // We finish SurfaceFlinger when post-composition finishes, so add that in here Duration flingerDuration = estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0]; + Duration estimatedGpuDuration = firstGpuTimeline.has_value() + ? estimatedGpuEndTime.value_or(TimePoint{0ns}) - firstGpuTimeline->startTime + : Duration::fromNs(0); // Combine the two timings into a single normalized one Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); - - return std::make_optional(combinedDuration); + Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration); + + WorkDuration duration{ + .timeStampNanos = TimePoint::now().ns(), + .durationNanos = combinedDuration.ns(), + .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(), + .cpuDurationNanos = FlagManager::getInstance().adpf_gpu_sf() ? cpuDuration.ns() : 0, + .gpuDurationNanos = + FlagManager::getInstance().adpf_gpu_sf() ? estimatedGpuDuration.ns() : 0, + }; + if (sTraceHintSessionData) { + ATRACE_INT64("Idle duration", idleDuration.ns()); + ATRACE_INT64("Total duration", totalDuration.ns()); + ATRACE_INT64("Flinger duration", flingerDuration.ns()); + } + return std::make_optional(duration); } Duration PowerAdvisor::combineTimingEstimates(Duration totalDuration, Duration flingerDuration) { @@ -581,7 +609,7 @@ PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayT std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming( std::optional<TimePoint> previousEndTime) { - if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { + if (!(requiresRenderEngine && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { return std::nullopt; } const TimePoint latestGpuStartTime = diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 13e1263369..60967b0509 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -68,6 +68,8 @@ public: virtual void enablePowerHintSession(bool enabled) = 0; // Initializes the power hint session virtual bool startPowerHintSession(std::vector<int32_t>&& threadIds) = 0; + // Provides PowerAdvisor with gpu start time + virtual void setGpuStartTime(DisplayId displayId, TimePoint startTime) = 0; // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0; // Reports the start and end times of a hwc validate call this frame for a given display @@ -80,9 +82,8 @@ public: virtual void setExpectedPresentTime(TimePoint expectedPresentTime) = 0; // Reports the most recent present fence time and end time once known virtual void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) = 0; - // Reports whether a display used client composition this frame - virtual void setRequiresClientComposition(DisplayId displayId, - bool requiresClientComposition) = 0; + // Reports whether a display requires RenderEngine to draw + virtual void setRequiresRenderEngine(DisplayId displayId, bool requiresRenderEngine) = 0; // Reports whether a given display skipped validation this frame virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0; // Reports when a hwc present is delayed, and the time that it will resume @@ -125,13 +126,14 @@ public: void reportActualWorkDuration() override; void enablePowerHintSession(bool enabled) override; bool startPowerHintSession(std::vector<int32_t>&& threadIds) override; + void setGpuStartTime(DisplayId displayId, TimePoint startTime) override; void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) override; void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime) override; void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime) override; void setSkippedValidate(DisplayId displayId, bool skipped) override; - void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override; + void setRequiresRenderEngine(DisplayId displayId, bool requiresRenderEngine); void setExpectedPresentTime(TimePoint expectedPresentTime) override; void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override; void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override; @@ -192,7 +194,7 @@ private: std::optional<TimePoint> hwcValidateStartTime; std::optional<TimePoint> hwcValidateEndTime; std::optional<TimePoint> hwcPresentDelayedTime; - bool usedClientComposition = false; + bool requiresRenderEngine = false; bool skippedValidate = false; // Calculate high-level timing milestones from more granular display timing data DisplayTimeline calculateDisplayTimeline(TimePoint fenceTime); @@ -224,8 +226,8 @@ private: // Filter and sort the display ids by a given property std::vector<DisplayId> getOrderedDisplayIds( std::optional<TimePoint> DisplayTimingData::*sortBy); - // Estimates a frame's total work duration including gpu time. - std::optional<Duration> estimateWorkDuration(); + // Estimates a frame's total work duration including gpu and gpu time. + std::optional<aidl::android::hardware::power::WorkDuration> estimateWorkDuration(); // There are two different targets and actual work durations we care about, // this normalizes them together and takes the max of the two Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration); @@ -235,7 +237,6 @@ private: bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex); std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData; - // Current frame's delay Duration mFrameDelayDuration{0ns}; // Last frame's post-composition duration @@ -272,7 +273,6 @@ private: std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue; // The latest values we have received for target and actual Duration mTargetDuration = kDefaultTargetDuration; - std::optional<Duration> mActualDuration; // The list of thread ids, stored so we can restart the session from this class if needed std::vector<int32_t> mHintSessionThreadIds; Duration mLastTargetDurationSent = kDefaultTargetDuration; diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp index 178c531a2a..ca8cdc3c0a 100644 --- a/services/surfaceflinger/FrameTracker.cpp +++ b/services/surfaceflinger/FrameTracker.cpp @@ -18,9 +18,6 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -// This is needed for stdint.h to define INT64_MAX in C++ -#define __STDC_LIMIT_MACROS - #include <inttypes.h> #include <android-base/stringprintf.h> diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 736fec6fb2..cd2120e990 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -15,6 +15,7 @@ */ // TODO(b/129481165): remove the #pragma below and fix conversion issues +#include "TransactionCallbackInvoker.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -150,7 +151,6 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mWindowType(static_cast<WindowInfo::Type>( args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))), mLayerCreationFlags(args.flags), - mBorderEnabled(false), mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) { ALOGV("Creating Layer %s", getDebugName()); @@ -1235,28 +1235,6 @@ StretchEffect Layer::getStretchEffect() const { return StretchEffect{}; } -bool Layer::enableBorder(bool shouldEnable, float width, const half4& color) { - if (mBorderEnabled == shouldEnable && mBorderWidth == width && mBorderColor == color) { - return false; - } - mBorderEnabled = shouldEnable; - mBorderWidth = width; - mBorderColor = color; - return true; -} - -bool Layer::isBorderEnabled() { - return mBorderEnabled; -} - -float Layer::getBorderWidth() { - return mBorderWidth; -} - -const half4& Layer::getBorderColor() { - return mBorderColor; -} - bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren, bool* transactionNeeded) { // Gets the frame rate to propagate to children. @@ -2912,9 +2890,7 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l currentMaxAcquiredBufferCount); } -void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, - ui::LayerStack layerStack, - std::function<FenceResult(FenceResult)>&& continuation) { +sp<CallbackHandle> Layer::findCallbackHandle() { // If we are displayed on multiple displays in a single composition cycle then we would // need to do careful tracking to enable the use of the mLastClientCompositionFence. // For example we can only use it if all the displays are client comp, and we need @@ -2949,6 +2925,53 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, break; } } + return ch; +} + +void Layer::prepareReleaseCallbacks(ftl::Future<FenceResult> futureFenceResult, + ui::LayerStack layerStack) { + sp<CallbackHandle> ch = findCallbackHandle(); + + if (ch != nullptr) { + ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; + ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); + ch->name = mName; + } else { + // If we didn't get a release callback yet, e.g. some scenarios when capturing + // screenshots asynchronously, then make sure we don't drop the fence. + mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult)); + std::vector<ftl::Future<FenceResult>> mergedFences; + sp<Fence> prevFence = nullptr; + // For a layer that's frequently screenshotted, try to merge fences to make sure we + // don't grow unbounded. + for (auto& futureReleaseFence : mAdditionalPreviousReleaseFences) { + auto result = futureReleaseFence.wait_for(0s); + if (result != std::future_status::ready) { + mergedFences.emplace_back(std::move(futureReleaseFence)); + continue; + } + mergeFence(getDebugName(), futureReleaseFence.get().value_or(Fence::NO_FENCE), + prevFence); + } + if (prevFence != nullptr) { + mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence)))); + } + mAdditionalPreviousReleaseFences.swap(mergedFences); + } + + if (mBufferInfo.mBuffer) { + mPreviouslyPresentedLayerStacks.push_back(layerStack); + } + + if (mDrawingState.frameNumber > 0) { + mDrawingState.previousFrameNumber = mDrawingState.frameNumber; + } +} + +void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, + ui::LayerStack layerStack, + std::function<FenceResult(FenceResult)>&& continuation) { + sp<CallbackHandle> ch = findCallbackHandle(); if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) { futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share(); @@ -2956,32 +2979,32 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, if (ch != nullptr) { ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; - ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); + ch->previousSharedReleaseFences.emplace_back(std::move(futureFenceResult)); ch->name = mName; } else if (FlagManager::getInstance().screenshot_fence_preservation()) { // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots // asynchronously, then make sure we don't drop the fence. - mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult), - std::move(continuation)); + mPreviousReleaseFenceAndContinuations.emplace_back(std::move(futureFenceResult), + std::move(continuation)); std::vector<FenceAndContinuation> mergedFences; sp<Fence> prevFence = nullptr; // For a layer that's frequently screenshotted, try to merge fences to make sure we don't // grow unbounded. - for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) { - auto result = futureAndContinution.future.wait_for(0s); + for (const auto& futureAndContinuation : mPreviousReleaseFenceAndContinuations) { + auto result = futureAndContinuation.future.wait_for(0s); if (result != std::future_status::ready) { - mergedFences.emplace_back(futureAndContinution); + mergedFences.emplace_back(futureAndContinuation); continue; } - mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE), - prevFence); + mergeFence(getDebugName(), + futureAndContinuation.chain().get().value_or(Fence::NO_FENCE), prevFence); } if (prevFence != nullptr) { mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share()); } - mAdditionalPreviousReleaseFences.swap(mergedFences); + mPreviousReleaseFenceAndContinuations.swap(mergedFences); } if (mBufferInfo.mBuffer) { @@ -3481,16 +3504,23 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; handle->frameNumber = mDrawingState.frameNumber; handle->previousFrameNumber = mDrawingState.previousFrameNumber; - if (FlagManager::getInstance().screenshot_fence_preservation() && + if (FlagManager::getInstance().ce_fence_promise() && mPreviousReleaseBufferEndpoint == handle->listener) { // Add fences from previous screenshots now so that they can be dispatched to the // client. - for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) { - handle->previousReleaseFences.emplace_back(futureAndContinution.chain()); + for (auto& futureReleaseFence : mAdditionalPreviousReleaseFences) { + handle->previousReleaseFences.emplace_back(std::move(futureReleaseFence)); } mAdditionalPreviousReleaseFences.clear(); + } else if (FlagManager::getInstance().screenshot_fence_preservation() && + mPreviousReleaseBufferEndpoint == handle->listener) { + // Add fences from previous screenshots now so that they can be dispatched to the + // client. + for (const auto& futureAndContinution : mPreviousReleaseFenceAndContinuations) { + handle->previousSharedReleaseFences.emplace_back(futureAndContinution.chain()); + } + mPreviousReleaseFenceAndContinuations.clear(); } - // Store so latched time and release fence can be set mDrawingState.callbackHandles.push_back(handle); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 0ceecec7ec..d283d6fda9 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -559,6 +559,14 @@ public: void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack, std::function<FenceResult(FenceResult)>&& continuation = nullptr); + // Tracks mLastClientCompositionFence and gets the callback handle for this layer. + sp<CallbackHandle> findCallbackHandle(); + + // Adds the future release fence to a list of fences that are used to release the + // last presented buffer. Also keeps track of the layerstack in a list of previous + // layerstacks that have been presented. + void prepareReleaseCallbacks(ftl::Future<FenceResult>, ui::LayerStack layerStack); + void setWasClientComposed(const sp<Fence>& fence) { mLastClientCompositionFence = fence; mClearClientCompositionFenceOnLayerDisplayed = false; @@ -874,10 +882,6 @@ public: bool setStretchEffect(const StretchEffect& effect); StretchEffect getStretchEffect() const; - bool enableBorder(bool shouldEnable, float width, const half4& color); - bool isBorderEnabled(); - float getBorderWidth(); - const half4& getBorderColor(); bool setBufferCrop(const Rect& /* bufferCrop */); bool setDestinationFrame(const Rect& /* destinationFrame */); @@ -934,6 +938,7 @@ public: // the release fences from the correct displays when we release the last buffer // from the layer. std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks; + struct FenceAndContinuation { ftl::SharedFuture<FenceResult> future; std::function<FenceResult(FenceResult)> continuation; @@ -946,7 +951,16 @@ public: } } }; - std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences; + std::vector<FenceAndContinuation> mPreviousReleaseFenceAndContinuations; + + // Release fences for buffers that have not yet received a release + // callback. A release callback may not be given when capturing + // screenshots asynchronously. There may be no buffer update for the + // layer, but the layer will still be composited on the screen in every + // frame. Kepping track of these fences ensures that they are not dropped + // and can be dispatched to the client at a later time. + std::vector<ftl::Future<FenceResult>> mAdditionalPreviousReleaseFences; + // Exposed so SurfaceFlinger can assert that it's held const sp<SurfaceFlinger> mFlinger; @@ -1238,10 +1252,6 @@ private: bool findInHierarchy(const sp<Layer>&); - bool mBorderEnabled = false; - float mBorderWidth; - half4 mBorderColor; - void setTransformHintLegacy(ui::Transform::RotationFlags); void releasePreviousBuffer(); void resetDrawingStateBufferInfo(); diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index 43a4397899..620edca4fb 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -27,6 +27,8 @@ #include "LayerFE.h" #include "SurfaceFlinger.h" +#include "ui/FenceResult.h" +#include "ui/LayerStack.h" namespace android { @@ -387,4 +389,29 @@ const sp<GraphicBuffer> LayerFE::getBuffer() const { return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr; } +void LayerFE::setReleaseFence(const FenceResult& releaseFence) { + // Promises should not be fulfilled more than once. This case can occur if virtual + // displays with the same layerstack ID are being created and destroyed in quick + // succession, such as in tests. This would result in a race condition in which + // multiple displays have the same layerstack ID within the same vsync interval. + if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) { + return; + } + mReleaseFence.set_value(releaseFence); + mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED; +} + +// LayerFEs are reused and a new fence needs to be created whevever a buffer is latched. +ftl::Future<FenceResult> LayerFE::createReleaseFenceFuture() { + if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) { + LOG_ALWAYS_FATAL("Attempting to create a new promise while one is still unfulfilled."); + } + mReleaseFence = std::promise<FenceResult>(); + mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::INITIALIZED; + return mReleaseFence.get_future(); +} + +LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() { + return mReleaseFencePromiseStatus; +} } // namespace android diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index 66cb88b20e..019fa22737 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -22,6 +22,9 @@ #include "compositionengine/LayerFE.h" #include "compositionengine/LayerFECompositionState.h" #include "renderengine/LayerSettings.h" +#include "ui/LayerStack.h" + +#include <ftl/future.h> namespace android { @@ -47,6 +50,9 @@ public: std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( compositionengine::LayerFE::ClientCompositionTargetSettings&) const; CompositionResult&& stealCompositionResult(); + ftl::Future<FenceResult> createReleaseFenceFuture() override; + void setReleaseFence(const FenceResult& releaseFence) override; + LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override; std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot; @@ -76,6 +82,8 @@ private: CompositionResult mCompositionResult; std::string mName; + std::promise<FenceResult> mReleaseFence; + ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index b8d5e76358..974c837139 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -40,14 +40,15 @@ namespace android::scheduler { namespace { -bool isLayerActive(const LayerInfo& info, nsecs_t threshold) { +bool isLayerActive(const LayerInfo& info, nsecs_t threshold, bool isVrrDevice) { if (FlagManager::getInstance().misc1() && !info.isVisible()) { return false; } // Layers with an explicit frame rate or frame rate category are kept active, // but ignore NoVote. - if (info.getSetFrameRateVote().isValid() && !info.getSetFrameRateVote().isNoVote()) { + const auto frameRate = info.getSetFrameRateVote(); + if (frameRate.isValid() && !frameRate.isNoVote() && frameRate.isVoteValidForMrr(isVrrDevice)) { return true; } @@ -194,7 +195,7 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - std::lock_guard lock(mLock); - partitionLayers(now); + partitionLayers(now, selector.isVrrDevice()); for (const auto& [key, value] : mActiveLayerInfos) { auto& info = value.second; @@ -236,7 +237,7 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - return summary; } -void LayerHistory::partitionLayers(nsecs_t now) { +void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { ATRACE_CALL(); const nsecs_t threshold = getActiveLayerThreshold(now); @@ -244,7 +245,7 @@ void LayerHistory::partitionLayers(nsecs_t now) { LayerInfos::iterator it = mInactiveLayerInfos.begin(); while (it != mInactiveLayerInfos.end()) { auto& [layerUnsafe, info] = it->second; - if (isLayerActive(*info, threshold)) { + if (isLayerActive(*info, threshold, isVrrDevice)) { // move this to the active map mActiveLayerInfos.insert({it->first, std::move(it->second)}); @@ -262,7 +263,7 @@ void LayerHistory::partitionLayers(nsecs_t now) { it = mActiveLayerInfos.begin(); while (it != mActiveLayerInfos.end()) { auto& [layerUnsafe, info] = it->second; - if (isLayerActive(*info, threshold)) { + if (isLayerActive(*info, threshold, isVrrDevice)) { // Set layer vote if set const auto frameRate = info->getSetFrameRateVote(); @@ -305,7 +306,7 @@ void LayerHistory::partitionLayers(nsecs_t now) { trace(*info, gameFrameRateOverrideVoteType, gameModeFrameRateOverride.getIntValue()); } - } else if (frameRate.isValid()) { + } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) { info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate, frameRate.vote.seamlessness, frameRate.category}); if (CC_UNLIKELY(mTraceEnabled)) { @@ -321,14 +322,30 @@ void LayerHistory::partitionLayers(nsecs_t now) { gameDefaultFrameRateOverride.getIntValue()); } } else { + if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) { + ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " + "%s %s", + info->getName().c_str(), + ftl::enum_string(frameRate.vote.type).c_str(), + to_string(frameRate.vote.rate).c_str(), + ftl::enum_string(frameRate.category).c_str()); + } info->resetLayerVote(); } } else { - if (frameRate.isValid()) { + if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) { const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote; info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness, frameRate.category}); } else { + if (!frameRate.isVoteValidForMrr(isVrrDevice)) { + ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " + "%s %s", + info->getName().c_str(), + ftl::enum_string(frameRate.vote.type).c_str(), + to_string(frameRate.vote.rate).c_str(), + ftl::enum_string(frameRate.category).c_str()); + } info->resetLayerVote(); } } diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index a6f1b56bf2..c09f148a9b 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -111,9 +111,12 @@ private: std::string dumpGameFrameRateOverridesLocked() const REQUIRES(mLock); // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive - // layers to mInactiveLayerInfos. + // layers to mInactiveLayerInfos. Layer's active state is determined by multiple factors + // such as update activity, visibility, and frame rate vote. // worst case time complexity is O(2 * inactive + active) - void partitionLayers(nsecs_t now) REQUIRES(mLock); + // now: the current time (system time) when calling the method + // isVrrDevice: true if the device has DisplayMode with VrrConfig specified. + void partitionLayers(nsecs_t now, bool isVrrDevice) REQUIRES(mLock); enum class LayerStatus { NotFound, diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 9745452e89..1bc4ac2f11 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -566,6 +566,18 @@ bool LayerInfo::FrameRate::isValid() const { return isNoVote() || vote.rate.isValid() || category != FrameRateCategory::Default; } +bool LayerInfo::FrameRate::isVoteValidForMrr(bool isVrrDevice) const { + if (isVrrDevice || FlagManager::getInstance().frame_rate_category_mrr()) { + return true; + } + + if (category == FrameRateCategory::Default && vote.type != FrameRateCompatibility::Gte) { + return true; + } + + return false; +} + std::ostream& operator<<(std::ostream& stream, const LayerInfo::FrameRate& rate) { return stream << "{rate=" << rate.vote.rate << " type=" << ftl::enum_string(rate.vote.type) << " seamlessness=" << ftl::enum_string(rate.vote.seamlessness) << '}'; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 326e444815..40903ed7c8 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -146,6 +146,10 @@ public: // selection. bool isNoVote() const; + // Checks whether the given FrameRate's vote specifications is valid for MRR devices + // given the current flagging. + bool isVoteValidForMrr(bool isVrrDevice) const; + private: static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) { if (!rate.isValid()) { diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp index cd45bfdab3..7e61dc02aa 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp +++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp @@ -115,9 +115,24 @@ void OneShotTimer::loop() { break; } - auto triggerTime = mClock->now() + mInterval; + auto triggerTime = mClock->now() + mInterval.load(); state = TimerState::WAITING; while (true) { + if (mPaused) { + mWaiting = true; + int result = sem_wait(&mSemaphore); + if (result && errno != EINTR) { + std::stringstream ss; + ss << "sem_wait failed (" << errno << ")"; + LOG_ALWAYS_FATAL("%s", ss.str().c_str()); + } + + mWaiting = false; + state = checkForResetAndStop(state); + if (state == TimerState::STOPPED) { + break; + } + } // Wait until triggerTime time to check if we need to reset or drop into the idle state. if (const auto triggerInterval = triggerTime - mClock->now(); triggerInterval > 0ns) { mWaiting = true; @@ -137,14 +152,14 @@ void OneShotTimer::loop() { break; } - if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= 0ns) { + if (!mPaused && state == TimerState::WAITING && (triggerTime - mClock->now()) <= 0ns) { triggerTimeout = true; state = TimerState::IDLE; break; } if (state == TimerState::RESET) { - triggerTime = mLastResetTime.load() + mInterval; + triggerTime = mLastResetTime.load() + mInterval.load(); state = TimerState::WAITING; } } @@ -179,5 +194,15 @@ void OneShotTimer::reset() { } } +void OneShotTimer::pause() { + mPaused = true; +} + +void OneShotTimer::resume() { + if (mPaused.exchange(false)) { + LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed"); + } +} + } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index 02e8719c08..4e1e2eef00 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -43,7 +43,8 @@ public: std::unique_ptr<android::Clock> clock = std::make_unique<SteadyClock>()); ~OneShotTimer(); - Duration interval() const { return mInterval; } + Duration interval() const { return mInterval.load(); } + void setInterval(Interval value) { mInterval = value; } // Initializes and turns on the idle timer. void start(); @@ -51,6 +52,10 @@ public: void stop(); // Resets the wakeup time and fires the reset callback. void reset(); + // Pauses the timer. reset calls will get ignored. + void pause(); + // Resumes the timer. + void resume(); private: // Enum to track in what state is the timer. @@ -91,7 +96,7 @@ private: std::string mName; // Interval after which timer expires. - const Interval mInterval; + std::atomic<Interval> mInterval; // Callback that happens when timer resets. const ResetCallback mResetCallback; @@ -105,6 +110,7 @@ private: std::atomic<bool> mResetTriggered = false; std::atomic<bool> mStopTriggered = false; std::atomic<bool> mWaiting = false; + std::atomic<bool> mPaused = false; std::atomic<std::chrono::steady_clock::time_point> mLastResetTime; }; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index ad59f1a574..a37fb96b5b 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -285,11 +285,12 @@ struct RefreshRateSelector::RefreshRateScoreComparator { std::string RefreshRateSelector::Policy::toString() const { return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s" - ", primaryRanges=%s, appRequestRanges=%s}", + ", primaryRanges=%s, appRequestRanges=%s idleScreenConfig=%s}", ftl::to_underlying(defaultMode), allowGroupSwitching ? "true" : "false", - to_string(primaryRanges).c_str(), - to_string(appRequestRanges).c_str()); + to_string(primaryRanges).c_str(), to_string(appRequestRanges).c_str(), + idleScreenConfigOpt ? idleScreenConfigOpt->toString().c_str() + : "nullptr"); } std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod, @@ -474,21 +475,23 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay } auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const -> RankedFrameRates { + GlobalSignals signals, Fps pacesetterFps) const + -> RankedFrameRates { + GetRankedFrameRatesCache cache{layers, signals, pacesetterFps}; + std::lock_guard lock(mLock); - if (mGetRankedFrameRatesCache && - mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) { + if (mGetRankedFrameRatesCache && mGetRankedFrameRatesCache->matches(cache)) { return mGetRankedFrameRatesCache->result; } - const auto result = getRankedFrameRatesLocked(layers, signals); - mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result}; - return result; + cache.result = getRankedFrameRatesLocked(layers, signals, pacesetterFps); + mGetRankedFrameRatesCache = std::move(cache); + return mGetRankedFrameRatesCache->result; } auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const + GlobalSignals signals, Fps pacesetterFps) const -> RankedFrameRates { using namespace fps_approx_ops; ATRACE_CALL(); @@ -496,6 +499,24 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const auto& activeMode = *getActiveModeLocked().modePtr; + if (pacesetterFps.isValid()) { + ALOGV("Follower display"); + + const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending, + std::nullopt, [&](FrameRateMode mode) { + return mode.modePtr->getPeakFps() == pacesetterFps; + }); + + if (!ranking.empty()) { + ATRACE_FORMAT_INSTANT("%s (Follower display)", + to_string(ranking.front().frameRateMode.fps).c_str()); + + return {ranking, kNoSignals, pacesetterFps}; + } + + ALOGW("Follower display cannot follow the pacesetter"); + } + // Keep the display at max frame rate for the duration of powering on the display. if (signals.powerOnImminent) { ALOGV("Power On Imminent"); @@ -506,6 +527,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi } int noVoteLayers = 0; + // Layers that prefer the same mode ("no-op"). + int noPreferenceLayers = 0; int minVoteLayers = 0; int maxVoteLayers = 0; int explicitDefaultVoteLayers = 0; @@ -549,10 +572,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi explicitCategoryVoteLayers++; } if (layer.frameRateCategory == FrameRateCategory::NoPreference) { - // Count this layer for Min vote as well. The explicit vote avoids - // touch boost and idle for choosing a category, while Min vote is for correct - // behavior when all layers are Min or no vote. - minVoteLayers++; + noPreferenceLayers++; } break; case LayerVoteType::Heuristic: @@ -612,6 +632,16 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, kNoSignals}; } + // If all layers are category NoPreference, use the current config. + if (noPreferenceLayers + noVoteLayers == layers.size()) { + ALOGV("All layers NoPreference"); + const auto ascendingWithPreferred = + rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId()); + ATRACE_FORMAT_INSTANT("%s (All layers NoPreference)", + to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str()); + return {ascendingWithPreferred, kNoSignals}; + } + const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0; const DisplayModeId activeModeId = activeMode.getId(); @@ -643,6 +673,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi ftl::enum_string(layer.frameRateCategory).c_str()); if (layer.isNoVote() || layer.frameRateCategory == FrameRateCategory::NoPreference || layer.vote == LayerVoteType::Min) { + ALOGV("%s scoring skipped due to vote", formatLayerInfo(layer, layer.weight).c_str()); continue; } @@ -831,7 +862,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit // vote we should not change it if we get a touch event. Only apply touch boost if it will // actually increase the refresh rate over the normal selection. - const bool touchBoostForExplicitExact = [&] { + const auto isTouchBoostForExplicitExact = [&]() -> bool { if (supportsAppFrameRateOverrideByContent()) { // Enable touch boost if there are other layers besides exact return explicitExact + noVoteLayers + explicitGteLayers != layers.size(); @@ -839,13 +870,11 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi // Enable touch boost if there are no exact layers return explicitExact == 0; } - }(); - - const bool touchBoostForCategory = - explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size(); + }; - const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); - using fps_approx_ops::operator<; + const auto isTouchBoostForCategory = [&]() -> bool { + return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size(); + }; // A method for UI Toolkit to send the touch signal via "HighHint" category vote, // which will touch boost when there are no ExplicitDefault layer votes. This is an @@ -853,13 +882,17 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi // compatibility to limit the frame rate, which should not have touch boost. const bool hasInteraction = signals.touch || interactiveLayers > 0; - if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && - touchBoostForCategory && - scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { - ALOGV("Touch Boost"); - ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", - to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); - return {touchRefreshRates, GlobalSignals{.touch = true}}; + if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() && + isTouchBoostForCategory()) { + const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); + using fps_approx_ops::operator<; + + if (scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { + ALOGV("Touch Boost"); + ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", + to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); + return {touchRefreshRates, GlobalSignals{.touch = true}}; + } } // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the @@ -873,8 +906,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ascendingWithPreferred, kNoSignals}; } - ALOGV("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str()); - ATRACE_FORMAT_INSTANT("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str()); + ALOGV("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str()); + ATRACE_FORMAT_INSTANT("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals}; } @@ -1216,19 +1249,21 @@ void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRat LOG_ALWAYS_FATAL_IF(!activeModeOpt); mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())}); + mIsVrrDevice = FlagManager::getInstance().vrr_config() && + activeModeOpt->get()->getVrrConfig().has_value(); } RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId, Config config) : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) { - initializeIdleTimer(); + initializeIdleTimer(mConfig.legacyIdleTimerTimeout); FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId)); } -void RefreshRateSelector::initializeIdleTimer() { - if (mConfig.idleTimerTimeout > 0ms) { +void RefreshRateSelector::initializeIdleTimer(std::chrono::milliseconds timeout) { + if (timeout > 0ms) { mIdleTimer.emplace( - "IdleTimer", mConfig.idleTimerTimeout, + "IdleTimer", timeout, [this] { std::scoped_lock lock(mIdleTimerCallbacksMutex); if (const auto callbacks = getIdleTimerCallbacks()) { @@ -1351,9 +1386,40 @@ auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyRes mGetRankedFrameRatesCache.reset(); - if (*getCurrentPolicyLocked() == oldPolicy) { + const auto& idleScreenConfigOpt = getCurrentPolicyLocked()->idleScreenConfigOpt; + if (idleScreenConfigOpt != oldPolicy.idleScreenConfigOpt) { + if (!idleScreenConfigOpt.has_value()) { + // fallback to legacy timer if existed, otherwise pause the old timer + LOG_ALWAYS_FATAL_IF(!mIdleTimer); + if (mConfig.legacyIdleTimerTimeout > 0ms) { + mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout); + mIdleTimer->resume(); + } else { + mIdleTimer->pause(); + } + } else if (idleScreenConfigOpt->timeoutMillis > 0) { + // create a new timer or reconfigure + const auto timeout = std::chrono::milliseconds{idleScreenConfigOpt->timeoutMillis}; + if (!mIdleTimer) { + initializeIdleTimer(timeout); + if (mIdleTimerStarted) { + mIdleTimer->start(); + } + } else { + mIdleTimer->setInterval(timeout); + mIdleTimer->resume(); + } + } else { + if (mIdleTimer) { + mIdleTimer->pause(); + } + } + } + + if (getCurrentPolicyLocked()->similarExceptIdleConfig(oldPolicy)) { return SetPolicyResult::Unchanged; } + constructAvailableRefreshRates(); displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId(); @@ -1429,7 +1495,8 @@ void RefreshRateSelector::constructAvailableRefreshRates() { } return str; }; - ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str()); + ALOGV("%s render rates: %s, isVrrDevice? %d", rangeName, stringifyModes().c_str(), + mIsVrrDevice); return frameRateModes; }; @@ -1438,6 +1505,11 @@ void RefreshRateSelector::constructAvailableRefreshRates() { mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request"); } +bool RefreshRateSelector::isVrrDevice() const { + std::lock_guard lock(mLock); + return mIsVrrDevice; +} + Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const { using namespace fps_approx_ops; @@ -1549,7 +1621,10 @@ void RefreshRateSelector::dump(utils::Dumper& dumper) const { } std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() { - return mConfig.idleTimerTimeout; + if (FlagManager::getInstance().idle_screen_refresh_rate_timeout() && mIdleTimer) { + return std::chrono::duration_cast<std::chrono::milliseconds>(mIdleTimer->interval()); + } + return mConfig.legacyIdleTimerTimeout; } // TODO(b/293651105): Extract category FpsRange mapping to OEM-configurable config. diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 6051e8935d..4f491d9986 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -67,26 +67,32 @@ public: FpsRanges primaryRanges; // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details. FpsRanges appRequestRanges; + // The idle timer configuration, if provided. + std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig> idleScreenConfigOpt; Policy() = default; Policy(DisplayModeId defaultMode, FpsRange range, - bool allowGroupSwitching = kAllowGroupSwitchingDefault) + bool allowGroupSwitching = kAllowGroupSwitchingDefault, + const std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig>& + idleScreenConfigOpt = std::nullopt) : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range}, - allowGroupSwitching) {} + allowGroupSwitching, idleScreenConfigOpt) {} Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges, - bool allowGroupSwitching = kAllowGroupSwitchingDefault) + bool allowGroupSwitching = kAllowGroupSwitchingDefault, + const std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig>& + idleScreenConfigOpt = std::nullopt) : defaultMode(defaultMode), allowGroupSwitching(allowGroupSwitching), primaryRanges(primaryRanges), - appRequestRanges(appRequestRanges) {} + appRequestRanges(appRequestRanges), + idleScreenConfigOpt(idleScreenConfigOpt) {} bool operator==(const Policy& other) const { using namespace fps_approx_ops; - return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges && - appRequestRanges == other.appRequestRanges && - allowGroupSwitching == other.allowGroupSwitching; + return similarExceptIdleConfig(other) && + idleScreenConfigOpt == other.idleScreenConfigOpt; } bool operator!=(const Policy& other) const { return !(*this == other); } @@ -95,6 +101,13 @@ public: return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max); } + bool similarExceptIdleConfig(const Policy& updated) const { + using namespace fps_approx_ops; + return defaultMode == updated.defaultMode && primaryRanges == updated.primaryRanges && + appRequestRanges == updated.appRequestRanges && + allowGroupSwitching == updated.allowGroupSwitching; + } + std::string toString() const; }; @@ -233,14 +246,18 @@ public: struct RankedFrameRates { FrameRateRanking ranking; // Ordered by descending score. GlobalSignals consideredSignals; + Fps pacesetterFps; bool operator==(const RankedFrameRates& other) const { - return ranking == other.ranking && consideredSignals == other.consideredSignals; + return ranking == other.ranking && consideredSignals == other.consideredSignals && + isApproxEqual(pacesetterFps, other.pacesetterFps); } }; - RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const - EXCLUDES(mLock); + // If valid, `pacesetterFps` (used by follower displays) filters the ranking to modes matching + // that refresh rate. + RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals, + Fps pacesetterFps = {}) const EXCLUDES(mLock); FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) { std::lock_guard lock(mLock); @@ -287,7 +304,7 @@ public: int frameRateMultipleThreshold = 0; // The Idle Timer timeout. 0 timeout means no idle timer. - std::chrono::milliseconds idleTimerTimeout = 0ms; + std::chrono::milliseconds legacyIdleTimerTimeout = 0ms; // The controller representing how the kernel idle timer will be configured // either on the HWC api or sysprop. @@ -298,7 +315,7 @@ public: DisplayModes, DisplayModeId activeModeId, Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled, .frameRateMultipleThreshold = 0, - .idleTimerTimeout = 0ms, + .legacyIdleTimerTimeout = 0ms, .kernelIdleTimerController = {}}); RefreshRateSelector(const RefreshRateSelector&) = delete; @@ -379,12 +396,14 @@ public: } void startIdleTimer() { + mIdleTimerStarted = true; if (mIdleTimer) { mIdleTimer->start(); } } void stopIdleTimer() { + mIdleTimerStarted = false; if (mIdleTimer) { mIdleTimer->stop(); } @@ -406,6 +425,8 @@ public: std::chrono::milliseconds getIdleTimerTimeout(); + bool isVrrDevice() const; + private: friend struct TestableRefreshRateSelector; @@ -415,7 +436,8 @@ private: const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock); RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const REQUIRES(mLock); + GlobalSignals signals, Fps pacesetterFps) const + REQUIRES(mLock); // Returns number of display frames and remainder when dividing the layer refresh period by // display refresh period. @@ -474,7 +496,7 @@ private: void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext); - void initializeIdleTimer(); + void initializeIdleTimer(std::chrono::milliseconds timeout); std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const REQUIRES(mIdleTimerCallbacksMutex) { @@ -513,6 +535,9 @@ private: std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock); std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock); + // Caches whether the device is VRR-compatible based on the active display mode. + bool mIsVrrDevice GUARDED_BY(mLock) = false; + Policy mDisplayManagerPolicy GUARDED_BY(mLock); std::optional<Policy> mOverridePolicy GUARDED_BY(mLock); @@ -534,8 +559,16 @@ private: Config::FrameRateOverride mFrameRateOverrideConfig; struct GetRankedFrameRatesCache { - std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments; + std::vector<LayerRequirement> layers; + GlobalSignals signals; + Fps pacesetterFps; + RankedFrameRates result; + + bool matches(const GetRankedFrameRatesCache& other) const { + return layers == other.layers && signals == other.signals && + isApproxEqual(pacesetterFps, other.pacesetterFps); + } }; mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock); @@ -544,6 +577,7 @@ private: std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex); // Used to detect (lack of) frame activity. ftl::Optional<scheduler::OneShotTimer> mIdleTimer; + std::atomic<bool> mIdleTimerStarted = false; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index d92edb81e0..7968096111 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -306,8 +306,11 @@ Period Scheduler::getVsyncPeriod(uid_t uid) { const auto pacesetterOpt = pacesetterDisplayLocked(); LOG_ALWAYS_FATAL_IF(!pacesetterOpt); const Display& pacesetter = *pacesetterOpt; - return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps, - pacesetter.schedulePtr->period()); + const FrameRateMode& frameRateMode = pacesetter.selectorPtr->getActiveMode(); + const auto refreshRate = frameRateMode.fps; + const auto displayVsync = frameRateMode.modePtr->getVsyncRate(); + const auto numPeriod = RefreshRateSelector::getFrameRateDivisor(displayVsync, refreshRate); + return std::make_pair(refreshRate, numPeriod * pacesetter.schedulePtr->period()); }(); const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod(); @@ -1146,38 +1149,31 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { ATRACE_CALL(); - using RankedRefreshRates = RefreshRateSelector::RankedFrameRates; - ui::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking; + DisplayModeChoiceMap modeChoices; const auto globalSignals = makeGlobalSignals(); - Fps pacesetterFps; + + const Fps pacesetterFps = [&]() REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext) { + auto rankedFrameRates = + pacesetterSelectorPtrLocked()->getRankedFrameRates(mPolicy.contentRequirements, + globalSignals); + + const Fps pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps; + + modeChoices.try_emplace(*mPacesetterDisplayId, + DisplayModeChoice::from(std::move(rankedFrameRates))); + return pacesetterFps; + }(); for (const auto& [id, display] : mDisplays) { + if (id == *mPacesetterDisplayId) continue; + auto rankedFrameRates = - display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, - globalSignals); - if (id == *mPacesetterDisplayId) { - pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps; - } - perDisplayRanking.push_back(std::move(rankedFrameRates)); - } + display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals, + pacesetterFps); - DisplayModeChoiceMap modeChoices; - using fps_approx_ops::operator==; - - for (auto& [rankings, signals] : perDisplayRanking) { - const auto chosenFrameRateMode = - ftl::find_if(rankings, - [&](const auto& ranking) { - return ranking.frameRateMode.fps == pacesetterFps; - }) - .transform([](const auto& scoredFrameRate) { - return scoredFrameRate.get().frameRateMode; - }) - .value_or(rankings.front().frameRateMode); - - modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(), - DisplayModeChoice{chosenFrameRateMode, signals}); + modeChoices.try_emplace(id, DisplayModeChoice::from(std::move(rankedFrameRates))); } + return modeChoices; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 494a91bf21..ccb3aa712a 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -402,6 +402,11 @@ private: DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals) : mode(std::move(mode)), consideredSignals(consideredSignals) {} + static DisplayModeChoice from(RefreshRateSelector::RankedFrameRates rankedFrameRates) { + return {rankedFrameRates.ranking.front().frameRateMode, + rankedFrameRates.consideredSignals}; + } + FrameRateMode mode; GlobalSignals consideredSignals; @@ -437,6 +442,7 @@ private: // IEventThreadCallback overrides bool throttleVsync(TimePoint, uid_t) override; + // Get frame interval Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index db1930da54..092bc0d0b6 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -377,15 +377,20 @@ void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { mRenderRateOpt = renderRate; const auto renderPeriodDelta = prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0; - const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() && - mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs(); if (applyImmediately) { + ATRACE_FORMAT_INSTANT("applyImmediately"); while (mTimelines.size() > 1) { mTimelines.pop_front(); } mTimelines.front().setRenderRate(renderRate); - } else if (newRenderRateIsHigher) { + return; + } + + const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() && + mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs(); + if (newRenderRateIsHigher) { + ATRACE_FORMAT_INSTANT("newRenderRateIsHigher"); mTimelines.clear(); mLastCommittedVsync = TimePoint::fromNs(0); @@ -548,10 +553,23 @@ void VSyncPredictor::clearTimestamps() { mLastTimestampIndex = 0; } - mTimelines.clear(); - mLastCommittedVsync = TimePoint::fromNs(0); mIdealPeriod = Period::fromNs(idealPeriod()); - mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); + if (mTimelines.empty()) { + mLastCommittedVsync = TimePoint::fromNs(0); + mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); + } else { + while (mTimelines.size() > 1) { + mTimelines.pop_front(); + } + mTimelines.front().setRenderRate(mRenderRateOpt); + // set mLastCommittedVsync to a valid vsync but don't commit too much in the future + const auto vsyncOpt = mTimelines.front().nextAnticipatedVSyncTimeFrom( + getVSyncPredictionModelLocked(), + /* minFramePeriodOpt */ std::nullopt, + snapToVsync(mClock->now()), MissedVsync{}, + /* lastVsyncOpt */ std::nullopt); + mLastCommittedVsync = *vsyncOpt; + } } bool VSyncPredictor::needsMoreSamples() const { @@ -583,6 +601,7 @@ void VSyncPredictor::purgeTimelines(android::TimePoint now) { if (mRenderRateOpt && mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase < mClock->now()) { + ATRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase"); mTimelines.clear(); mLastCommittedVsync = TimePoint::fromNs(0); mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); @@ -630,33 +649,30 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime const auto threshold = model.slope / 2; const auto lastFrameMissed = lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold; - nsecs_t vsyncFixupTime = 0; if (FlagManager::getInstance().vrr_config() && lastFrameMissed) { // If the last frame missed is the last vsync, we already shifted the timeline. Depends on // whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a different // fixup. There is no need to to shift the vsync timeline again. vsyncTime += missedVsync.fixup.ns(); ATRACE_FORMAT_INSTANT("lastFrameMissed"); - } else if (minFramePeriodOpt) { - if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) { - // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it - // first before trying to use it. - if (mLastVsyncSequence->seq > 0) { - lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); - } - const auto vsyncDiff = vsyncTime - *lastVsyncOpt; - if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) { - vsyncFixupTime = *lastVsyncOpt + minFramePeriodOpt->ns() - vsyncTime; - ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f " - "from " - "prev. " - "adjust by %.2f", - static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f, - static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f, - static_cast<float>(vsyncFixupTime) / 1e6f); - } + } else if (FlagManager::getInstance().vrr_config() && minFramePeriodOpt && mRenderRateOpt && + lastVsyncOpt) { + // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it + // first before trying to use it. + lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); + const auto vsyncDiff = vsyncTime - *lastVsyncOpt; + if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) { + // avoid a duplicate vsync + ATRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f which " + "is %.2f " + "from " + "prev. " + "adjust by %.2f", + static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f, + static_cast<float>(vsyncDiff) / 1e6f, + static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f); + vsyncTime += mRenderRateOpt->getPeriodNsecs(); } - vsyncTime += vsyncFixupTime; } ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f); @@ -666,12 +682,6 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime return std::nullopt; } - // If we needed a fixup, it means that we changed the render rate and the chosen vsync would - // cross minFramePeriod. In that case we need to shift the entire vsync timeline. - if (vsyncFixupTime > 0) { - shiftVsyncSequence(Duration::fromNs(vsyncFixupTime)); - } - return TimePoint::fromNs(vsyncTime); } diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 3ed1d41a01..a661292f9d 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -83,7 +83,7 @@ private: }; struct MissedVsync { - TimePoint vsync; + TimePoint vsync = TimePoint::fromNs(0); Duration fixup = Duration::fromNs(0); }; @@ -97,7 +97,7 @@ private: std::optional<TimePoint> validUntil() const { return mValidUntil; } bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate); void shiftVsyncSequence(Duration phase); - void setRenderRate(Fps renderRate) { mRenderRateOpt = renderRate; } + void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; } private: nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync); diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 186a2d6740..9b8f310e16 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -217,6 +217,11 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> mMoreSamplesNeeded = mTracker.needsMoreSamples(); } + if (mExternalIgnoreFences) { + // keep HWVSync on as long as we ignore present fences. + mMoreSamplesNeeded = true; + } + if (!mMoreSamplesNeeded) { setIgnorePresentFencesInternal(false); } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index bf210afe6d..734058a371 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -40,6 +40,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> +#include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/Display.h> @@ -167,10 +168,6 @@ #define NO_THREAD_SAFETY_ANALYSIS \ _Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"") -// To enable layer borders in the system, change the below flag to true. -#undef DOES_CONTAIN_BORDER -#define DOES_CONTAIN_BORDER false - namespace android { using namespace std::chrono_literals; using namespace std::string_literals; @@ -578,11 +575,11 @@ void SurfaceFlinger::run() { sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure, float requestedRefreshRate) { - // onTransact already checks for some permissions, but adding an additional check here. - // This is to ensure that only system and graphics can request to create a secure + // SurfaceComposerAIDL checks for some permissions, but adding an additional check here. + // This is to ensure that only root, system, and graphics can request to create a secure // display. Secure displays can show secure content so we add an additional restriction on it. const int uid = IPCThreadState::self()->getCallingUid(); - if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) { + if (secure && uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM) { ALOGE("Only privileged processes can create a secure display"); return nullptr; } @@ -2702,27 +2699,14 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mLayerMetadataSnapshotNeeded = false; } - if (DOES_CONTAIN_BORDER) { - refreshArgs.borderInfoList.clear(); - mDrawingState.traverse([&refreshArgs](Layer* layer) { - if (layer->isBorderEnabled()) { - compositionengine::BorderRenderInfo info; - info.width = layer->getBorderWidth(); - info.color = layer->getBorderColor(); - layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) { - info.layerIds.push_back(ilayer->getSequence()); - }); - refreshArgs.borderInfoList.emplace_back(std::move(info)); - } - }); - } - refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache); - refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); - for (auto layer : mLayersWithQueuedFrames) { - if (auto layerFE = layer->getCompositionEngineLayerFE()) - refreshArgs.layersWithQueuedFrames.push_back(layerFE); + if (!FlagManager::getInstance().ce_fence_promise()) { + refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); + for (auto& layer : mLayersWithQueuedFrames) { + if (const auto& layerFE = layer->getCompositionEngineLayerFE()) + refreshArgs.layersWithQueuedFrames.push_back(layerFE); + } } refreshArgs.outputColorSetting = mDisplayColorSetting; @@ -2784,22 +2768,56 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } refreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); - for (auto [layer, layerFE] : layers) { + for (auto& [layer, layerFE] : layers) { layer->onPreComposition(refreshArgs.refreshStartTime); } - mCompositionEngine->present(refreshArgs); - moveSnapshotsFromCompositionArgs(refreshArgs, layers); + if (FlagManager::getInstance().ce_fence_promise()) { + for (auto& [layer, layerFE] : layers) { + attachReleaseFenceFutureToLayer(layer, layerFE, + layerFE->mSnapshot->outputFilter.layerStack); + } - for (auto [layer, layerFE] : layers) { - CompositionResult compositionResult{layerFE->stealCompositionResult()}; - for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) { - Layer* clonedFrom = layer->getClonedFrom().get(); - auto owningLayer = clonedFrom ? clonedFrom : layer; - owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack); + refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); + for (auto& layer : mLayersWithQueuedFrames) { + if (const auto& layerFE = layer->getCompositionEngineLayerFE()) { + refreshArgs.layersWithQueuedFrames.push_back(layerFE); + // Some layers are not displayed and do not yet have a future release fence + if (layerFE->getReleaseFencePromiseStatus() == + LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED || + layerFE->getReleaseFencePromiseStatus() == + LayerFE::ReleaseFencePromiseStatus::FULFILLED) { + // layerStack is invalid because layer is not on a display + attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(), + ui::INVALID_LAYER_STACK); + } + } } - if (compositionResult.lastClientCompositionFence) { - layer->setWasClientComposed(compositionResult.lastClientCompositionFence); + + mCompositionEngine->present(refreshArgs); + moveSnapshotsFromCompositionArgs(refreshArgs, layers); + + for (auto& [layer, layerFE] : layers) { + CompositionResult compositionResult{layerFE->stealCompositionResult()}; + if (compositionResult.lastClientCompositionFence) { + layer->setWasClientComposed(compositionResult.lastClientCompositionFence); + } + } + + } else { + mCompositionEngine->present(refreshArgs); + moveSnapshotsFromCompositionArgs(refreshArgs, layers); + + for (auto [layer, layerFE] : layers) { + CompositionResult compositionResult{layerFE->stealCompositionResult()}; + for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) { + Layer* clonedFrom = layer->getClonedFrom().get(); + auto owningLayer = clonedFrom ? clonedFrom : layer; + owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack); + } + if (compositionResult.lastClientCompositionFence) { + layer->setWasClientComposed(compositionResult.lastClientCompositionFence); + } } } @@ -3065,8 +3083,13 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, auto optDisplay = layerStackToDisplay.get(layerStack); if (optDisplay && !optDisplay->get()->isVirtual()) { auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId()); - layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(), - ui::INVALID_LAYER_STACK); + if (FlagManager::getInstance().ce_fence_promise()) { + layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence), + ui::INVALID_LAYER_STACK); + } else { + layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(), + ui::INVALID_LAYER_STACK); + } } } layer->releasePendingBuffer(presentTime.ns()); @@ -3565,7 +3588,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( {.enableFrameRateOverride = enableFrameRateOverride, .frameRateMultipleThreshold = base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0), - .idleTimerTimeout = idleTimerTimeoutMs, + .legacyIdleTimerTimeout = idleTimerTimeoutMs, .kernelIdleTimerController = kernelIdleTimerController}; creationArgs.refreshRateSelector = @@ -5461,11 +5484,6 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (what & layer_state_t::eBlurRegionsChanged) { if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eRenderBorderChanged) { - if (layer->enableBorder(s.borderEnabled, s.borderWidth, s.borderColor)) { - flags |= eTraversalNeeded; - } - } if (what & layer_state_t::eLayerStackChanged) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); // We only allow setting layer stacks for top level layers, @@ -6244,9 +6262,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)}, {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)}, {"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)}, - {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, - {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, - {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, + {"--latency"s, argsMainThreadDumper(&SurfaceFlinger::dumpStats)}, + {"--latency-clear"s, argsMainThreadDumper(&SurfaceFlinger::clearStats)}, + {"--list"s, mainThreadDumper(&SurfaceFlinger::listLayers)}, {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)}, {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)}, {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, @@ -6279,28 +6297,29 @@ status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { return doDump(fd, DumpArgs(), asProto); } -void SurfaceFlinger::listLayersLocked(std::string& result) const { - mCurrentState.traverseInZOrder( - [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); }); +void SurfaceFlinger::listLayers(std::string& result) const { + for (const auto& layer : mLayerLifecycleManager.getLayers()) { + StringAppendF(&result, "%s\n", layer->getDebugString().c_str()); + } } -void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const { +void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const { StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC()); if (args.size() < 2) return; const auto name = String8(args[1]); - mCurrentState.traverseInZOrder([&](Layer* layer) { + traverseLegacyLayers([&](Layer* layer) { if (layer->getName() == name.c_str()) { layer->dumpFrameStats(result); } }); } -void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { +void SurfaceFlinger::clearStats(const DumpArgs& args, std::string&) { const bool clearAll = args.size() < 2; const auto name = clearAll ? String8() : String8(args[1]); - mCurrentState.traverse([&](Layer* layer) { + traverseLegacyLayers([&](Layer* layer) { if (clearAll || layer->getName() == name.c_str()) { layer->clearFrameStats(); } @@ -7998,7 +8017,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, // really small crop or frameScale if (reqSize.width <= 0 || reqSize.height <= 0) { - ALOGW("Failed to captureLayes: crop or scale too small"); + ALOGW("Failed to captureLayers: crop or scale too small"); invokeScreenCaptureError(BAD_VALUE, captureListener); return; } @@ -8072,6 +8091,17 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, args.allowProtected, args.grayscale, captureListener); } +// Creates a Future release fence for a layer and keeps track of it in a list to +// release the buffer when the Future is complete. Calls from composittion +// involve needing to refresh the composition start time for stats. +void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, + ui::LayerStack layerStack) { + ftl::Future<FenceResult> futureFence = layerFE->createReleaseFenceFuture(); + Layer* clonedFrom = layer->getClonedFrom().get(); + auto owningLayer = clonedFrom ? clonedFrom : layer; + owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack); +} + bool SurfaceFlinger::layersHasProtectedLayer( const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { bool protectedLayerFound = false; @@ -8107,7 +8137,6 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, const bool supportsProtected = getRenderEngine().supportsProtectedContent(); bool hasProtectedLayer = false; if (allowProtected && supportsProtected) { - // Snapshots must be taken from the main thread. auto layers = mScheduler->schedule([=]() { return getLayerSnapshots(); }).get(); hasProtectedLayer = layersHasProtectedLayer(layers); } @@ -8148,6 +8177,14 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( auto takeScreenshotFn = [=, this, renderAreaFuture = std::move(renderAreaFuture)]() REQUIRES( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { + // LayerSnapshots must be obtained from the main thread. + auto layers = getLayerSnapshots(); + if (FlagManager::getInstance().ce_fence_promise()) { + for (auto& [layer, layerFE] : layers) { + attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); + } + } + ScreenCaptureResults captureResults; std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get(); if (!renderArea) { @@ -8161,8 +8198,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( ftl::SharedFuture<FenceResult> renderFuture; renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) { - renderFuture = renderScreenImpl(renderArea, getLayerSnapshots, buffer, regionSampling, - grayscale, isProtected, captureResults); + renderFuture = renderScreenImpl(renderArea, buffer, regionSampling, grayscale, + isProtected, captureResults, layers); }); if (captureListener) { @@ -8179,6 +8216,10 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( return renderFuture; }; + // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number + // of calls on the main thread. renderAreaFuture runs on the main thread and should + // no longer be a future, so that it does not need to make an additional jump on the + // main thread whenever get() is called. auto future = mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn))); @@ -8194,9 +8235,17 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) { - ATRACE_CALL(); - auto layers = getLayerSnapshots(); + return renderScreenImpl(renderArea, buffer, regionSampling, grayscale, isProtected, + captureResults, layers); +} + +ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( + std::shared_ptr<const RenderArea> renderArea, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, + bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, + std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) { + ATRACE_CALL(); for (auto& [_, layerFE] : layers) { frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get(); @@ -8350,24 +8399,26 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() : ftl::yield(present()).share(); - for (auto& [layer, layerFE] : layers) { - layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK, - [layerFE = std::move(layerFE)](FenceResult) { - if (FlagManager::getInstance() - .screenshot_fence_preservation()) { - const auto compositionResult = - layerFE->stealCompositionResult(); - const auto& fences = compositionResult.releaseFences; - // CompositionEngine may choose to cull layers that - // aren't visible, so pass a non-fence. - return fences.empty() ? Fence::NO_FENCE - : fences.back().first.get(); - } else { - return layerFE->stealCompositionResult() - .releaseFences.back() - .first.get(); - } - }); + if (!FlagManager::getInstance().ce_fence_promise()) { + for (auto& [layer, layerFE] : layers) { + layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK, + [layerFE = std::move(layerFE)](FenceResult) { + if (FlagManager::getInstance() + .screenshot_fence_preservation()) { + const auto compositionResult = + layerFE->stealCompositionResult(); + const auto& fences = compositionResult.releaseFences; + // CompositionEngine may choose to cull layers that + // aren't visible, so pass a non-fence. + return fences.empty() ? Fence::NO_FENCE + : fences.back().first.get(); + } else { + return layerFE->stealCompositionResult() + .releaseFences.back() + .first.get(); + } + }); + } } return presentFuture; @@ -8588,8 +8639,13 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return INVALID_OPERATION; } else { using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy; + const auto idleScreenConfigOpt = + FlagManager::getInstance().idle_screen_refresh_rate_timeout() + ? specs.idleScreenRefreshRateConfig + : std::nullopt; const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges), - translate(specs.appRequestRanges), specs.allowGroupSwitching}; + translate(specs.appRequestRanges), specs.allowGroupSwitching, + idleScreenConfigOpt}; return setDesiredDisplayModeSpecsInternal(display, policy); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 0cc8fbb98a..c106abda37 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -509,10 +509,7 @@ private: return lockedDumper(std::bind(dump, this, _1, _2, _3)); } - template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> - Dumper mainThreadDumper(F dump) { - using namespace std::placeholders; - Dumper dumper = std::bind(dump, this, _3); + Dumper mainThreadDumperImpl(Dumper dumper) { return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void { mScheduler ->schedule( @@ -522,6 +519,18 @@ private: }; } + template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> + Dumper mainThreadDumper(F dump) { + using namespace std::placeholders; + return mainThreadDumperImpl(std::bind(dump, this, _3)); + } + + template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> + Dumper argsMainThreadDumper(F dump) { + using namespace std::placeholders; + return mainThreadDumperImpl(std::bind(dump, this, _1, _3)); + } + // Maximum allowed number of display frames that can be set through backdoor static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048; @@ -872,6 +881,11 @@ private: // Traverse through all the layers and compute and cache its bounds. void computeLayerBounds(); + // Creates a promise for a future release fence for a layer. This allows for + // the layer to keep track of when its buffer can be released. + void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack); + + // Checks if a protected layer exists in a list of layers. bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize, @@ -883,12 +897,22 @@ private: const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&); + // Overloaded version of renderScreenImpl that is used when layer snapshots have + // not yet been captured, and thus cannot yet be passed in as a parameter. + // Needed for TestableSurfaceFlinger. ftl::SharedFuture<FenceResult> renderScreenImpl( std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); + ftl::SharedFuture<FenceResult> renderScreenImpl( + std::shared_ptr<const RenderArea>, + const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, + bool grayscale, bool isProtected, ScreenCaptureResults&, + std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) EXCLUDES(mStateLock) + REQUIRES(kMainThreadContext); + // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a // matching ownerUid void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, @@ -1113,9 +1137,9 @@ private: void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; - void listLayersLocked(std::string& result) const; - void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); - void clearStatsLocked(const DumpArgs& args, std::string& result); + void listLayers(std::string& result) const; + void dumpStats(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); + void clearStats(const DumpArgs& args, std::string& result); void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const; void dumpFrameTimeline(const DumpArgs& args, std::string& result) const; void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext); diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h index 6a66fff75f..7a0fb5ef6e 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.h +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -24,6 +24,7 @@ #include <mutex> #include <optional> +#include <set> #include <thread> #include "FrontEnd/DisplayInfo.h" diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 7b5298c82e..222ae30acb 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -30,6 +30,7 @@ #include <cinttypes> #include <binder/IInterface.h> +#include <common/FlagManager.h> #include <utils/RefBase.h> namespace android { @@ -128,9 +129,17 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& sp<IBinder> surfaceControl = handle->surfaceControl.promote(); if (surfaceControl) { sp<Fence> prevFence = nullptr; - for (const auto& future : handle->previousReleaseFences) { - mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence); + + if (FlagManager::getInstance().ce_fence_promise()) { + for (auto& future : handle->previousReleaseFences) { + mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence); + } + } else { + for (const auto& future : handle->previousSharedReleaseFences) { + mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence); + } } + handle->previousReleaseFence = prevFence; handle->previousReleaseFences.clear(); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index 245398f2f4..cb7150b943 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -46,7 +46,8 @@ public: bool releasePreviousBuffer = false; std::string name; sp<Fence> previousReleaseFence; - std::vector<ftl::SharedFuture<FenceResult>> previousReleaseFences; + std::vector<ftl::Future<FenceResult>> previousReleaseFences; + std::vector<ftl::SharedFuture<FenceResult>> previousSharedReleaseFences; std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1; nsecs_t latchTime = -1; std::optional<uint32_t> transformHint = std::nullopt; diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp index 4a89dd06ce..6b971a7597 100644 --- a/services/surfaceflinger/common/Android.bp +++ b/services/surfaceflinger/common/Android.bp @@ -36,6 +36,7 @@ cc_library_static { static_libs: [ "libsurfaceflingerflags", "android.os.flags-aconfig-cc", + "android.server.display.flags-aconfig-cc", ], } @@ -47,6 +48,7 @@ cc_library_static { static_libs: [ "libsurfaceflingerflags_test", "android.os.flags-aconfig-cc-test", + "android.server.display.flags-aconfig-cc", ], } @@ -59,6 +61,7 @@ cc_defaults { "libsurfaceflinger_common", "libsurfaceflingerflags", "android.os.flags-aconfig-cc", + "android.server.display.flags-aconfig-cc", ], } @@ -71,5 +74,6 @@ cc_defaults { "libsurfaceflinger_common_test", "libsurfaceflingerflags_test", "android.os.flags-aconfig-cc-test", + "android.server.display.flags-aconfig-cc", ], } diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 3b669c6b54..6507abad4d 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -28,6 +28,7 @@ #include <android_os.h> #include <com_android_graphics_surfaceflinger_flags.h> +#include <com_android_server_display_feature_flags.h> namespace android { using namespace com::android::graphics::surfaceflinger; @@ -110,11 +111,13 @@ void FlagManager::dump(std::string& result) const { /// Trunk stable server flags /// DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display); + DUMP_SERVER_FLAG(adpf_gpu_sf); DUMP_SERVER_FLAG(adpf_use_fmq_channel); /// Trunk stable readonly flags /// DUMP_READ_ONLY_FLAG(connected_display); DUMP_READ_ONLY_FLAG(enable_small_area_detection); + DUMP_READ_ONLY_FLAG(frame_rate_category_mrr); DUMP_READ_ONLY_FLAG(misc1); DUMP_READ_ONLY_FLAG(vrr_config); DUMP_READ_ONLY_FLAG(hotplug2); @@ -134,6 +137,8 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(restore_blur_step); DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro); DUMP_READ_ONLY_FLAG(protected_if_client); + DUMP_READ_ONLY_FLAG(ce_fence_promise); + DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG #undef DUMP_FLAG_INTERVAL @@ -187,6 +192,9 @@ bool FlagManager::getServerConfigurableFlag(const char* experimentFlagName) cons #define FLAG_MANAGER_SERVER_FLAG_IMPORTED(name, syspropOverride, owner) \ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, owner) +#define FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(name, syspropOverride, owner) \ + FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, owner) + /// Legacy server flags /// FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "") FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint", @@ -197,6 +205,7 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, /// Trunk stable readonly flags /// FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") +FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") FLAG_MANAGER_READ_ONLY_FLAG(misc1, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config") FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") @@ -210,18 +219,24 @@ FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "") FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "") FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target") FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "") -FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "") +FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching") FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation") FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan") FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "") FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step") FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "") FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "") +FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, ""); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") +FLAG_MANAGER_SERVER_FLAG(adpf_gpu_sf, "") /// Trunk stable server flags from outside SurfaceFlinger /// FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os) +/// Trunk stable readonly flags from outside SurfaceFlinger /// +FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "", + com::android::server::display::feature::flags) + } // namespace android diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 763963e24f..964943ca3f 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -49,10 +49,12 @@ public: /// Trunk stable server flags /// bool refresh_rate_overlay_on_external_display() const; + bool adpf_gpu_sf() const; bool adpf_use_fmq_channel() const; /// Trunk stable readonly flags /// bool connected_display() const; + bool frame_rate_category_mrr() const; bool enable_small_area_detection() const; bool misc1() const; bool vrr_config() const; @@ -73,6 +75,8 @@ public: bool restore_blur_step() const; bool dont_skip_on_early_ro() const; bool protected_if_client() const; + bool ce_fence_promise() const; + bool idle_screen_refresh_rate_timeout() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/common/include/common/test/FlagUtils.h b/services/surfaceflinger/common/include/common/test/FlagUtils.h index d61fcb5438..5317cbbc54 100644 --- a/services/surfaceflinger/common/include/common/test/FlagUtils.h +++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h @@ -18,9 +18,13 @@ #include <common/FlagManager.h> -#define SET_FLAG_FOR_TEST(name, value) \ - TestFlagSetter _testflag_ { \ - (name), (name), (value) \ +// indirection to resolve __LINE__ in SET_FLAG_FOR_TEST, it's used to create a unique TestFlagSetter +// setter var name everytime so multiple flags can be set in a test +#define CONCAT_INNER(a, b) a##b +#define CONCAT(a, b) CONCAT_INNER(a, b) +#define SET_FLAG_FOR_TEST(name, value) \ + TestFlagSetter CONCAT(_testFlag_, __LINE__) { \ + (name), (name), (value) \ } namespace android { diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index 5451752d91..0a5cde33a5 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -4,10 +4,39 @@ package: "com.android.graphics.surfaceflinger.flags" container: "system" flag { + name: "adpf_gpu_sf" + namespace: "game" + description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL" + bug: "284324521" +} # adpf_gpu_sf + +flag { + name: "ce_fence_promise" + namespace: "window_surfaces" + description: "Moves logic for buffer release fences into LayerFE" + bug: "294936197" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "dont_skip_on_early_ro2" namespace: "core_graphics" description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" bug: "273702768" } # dont_skip_on_early_ro2 +flag { + name: "frame_rate_category_mrr" + namespace: "core_graphics" + description: "Enable to use frame rate category and newer frame rate votes such as GTE in SurfaceFlinger scheduler, to guard dVRR changes from MRR devices" + bug: "330224639" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # frame_rate_category_mrr + # IMPORTANT - please keep alphabetize to reduce merge conflicts diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 925fe0b794..38fc977931 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -38,10 +38,10 @@ cc_test { "DereferenceSurfaceControl_test.cpp", "DisplayConfigs_test.cpp", "DisplayEventReceiver_test.cpp", + "Dumpsys_test.cpp", "EffectLayer_test.cpp", "HdrSdrRatioOverlay_test.cpp", "InvalidHandles_test.cpp", - "LayerBorder_test.cpp", "LayerCallback_test.cpp", "LayerRenderTypeTransaction_test.cpp", "LayerState_test.cpp", @@ -67,6 +67,7 @@ cc_test { static_libs: [ "liblayers_proto", "android.hardware.graphics.composer@2.1", + "libsurfaceflinger_common", ], shared_libs: [ "android.hardware.graphics.common@1.2", @@ -82,6 +83,7 @@ cc_test { "libprotobuf-cpp-full", "libui", "libutils", + "server_configurable_flags", ], header_libs: [ "libnativewindow_headers", diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index 822ac4d99f..9b83713f50 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -241,7 +241,7 @@ TEST_F(CredentialsTest, CreateDisplayTest) { // Check with root. { UIDFaker f(AID_ROOT); - ASSERT_FALSE(condition()); + ASSERT_TRUE(condition()); } // Check as a Graphics user. diff --git a/services/surfaceflinger/tests/Dumpsys_test.cpp b/services/surfaceflinger/tests/Dumpsys_test.cpp new file mode 100644 index 0000000000..c3914e5422 --- /dev/null +++ b/services/surfaceflinger/tests/Dumpsys_test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/native_window.h> +#include <gtest/gtest.h> +#include <gui/SurfaceComposerClient.h> +#include "android-base/stringprintf.h" +#include "utils/Errors.h" + +namespace android { + +namespace { +status_t runShellCommand(const std::string& cmd, std::string& result) { + FILE* pipe = popen(cmd.c_str(), "r"); + if (!pipe) { + return UNKNOWN_ERROR; + } + + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), pipe) != NULL) { + result += buffer; + } + + pclose(pipe); + return OK; +} +} // namespace + +using android::hardware::graphics::common::V1_1::BufferUsage; + +class WaitForCompletedCallback { +public: + WaitForCompletedCallback() = default; + ~WaitForCompletedCallback() = default; + + static void transactionCompletedCallback(void* callbackContext, nsecs_t /* latchTime */, + const sp<Fence>& /* presentFence */, + const std::vector<SurfaceControlStats>& /* stats */) { + ASSERT_NE(callbackContext, nullptr) << "failed to get callback context"; + WaitForCompletedCallback* context = static_cast<WaitForCompletedCallback*>(callbackContext); + context->notify(); + } + + void wait() { + std::unique_lock lock(mMutex); + cv.wait(lock, [this] { return mCallbackReceived; }); + } + + void notify() { + std::unique_lock lock(mMutex); + mCallbackReceived = true; + cv.notify_one(); + } + +private: + std::mutex mMutex; + std::condition_variable cv; + bool mCallbackReceived = false; +}; + +TEST(Dumpsys, listLayers) { + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); + ASSERT_EQ(NO_ERROR, client->initCheck()); + auto newLayer = + client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0); + std::string layersAsString; + EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --list", layersAsString)); + EXPECT_NE(strstr(layersAsString.c_str(), ""), nullptr); +} + +TEST(Dumpsys, stats) { + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); + ASSERT_EQ(NO_ERROR, client->initCheck()); + auto newLayer = + client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0); + uint64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; + + sp<GraphicBuffer> buffer = + sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, 1u, usageFlags, "test"); + + WaitForCompletedCallback callback; + SurfaceComposerClient::Transaction() + .setBuffer(newLayer, buffer) + .addTransactionCompletedCallback(WaitForCompletedCallback::transactionCompletedCallback, + &callback) + .apply(); + callback.wait(); + std::string stats; + std::string layerName = base::StringPrintf("MY_TEST_LAYER#%d", newLayer->getLayerId()); + EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency " + layerName, stats)); + EXPECT_NE(std::string(""), stats); + EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency-clear " + layerName, stats)); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp deleted file mode 100644 index 00e134b4d2..0000000000 --- a/services/surfaceflinger/tests/LayerBorder_test.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -// TODO: Amend all tests when screenshots become fully reworked for borders -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include <chrono> // std::chrono::seconds -#include <thread> // std::this_thread::sleep_for -#include "LayerTransactionTest.h" - -namespace android { - -class LayerBorderTest : public LayerTransactionTest { -protected: - virtual void SetUp() { - LayerTransactionTest::SetUp(); - ASSERT_EQ(NO_ERROR, mClient->initCheck()); - - toHalf3 = ColorTransformHelper::toHalf3; - toHalf4 = ColorTransformHelper::toHalf4; - - const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); - ASSERT_FALSE(ids.empty()); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); - ASSERT_FALSE(display == nullptr); - mColorOrange = toHalf4({255, 140, 0, 255}); - mParentLayer = createColorLayer("Parent layer", Color::RED); - - mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */, - 0 /* height */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceContainer | - ISurfaceComposerClient::eNoColorFill, - mParentLayer->getHandle()); - EXPECT_NE(nullptr, mContainerLayer.get()) << "failed to create container layer"; - - mEffectLayer1 = mClient->createSurface(String8("Effect Layer"), 0 /* width */, - 0 /* height */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect | - ISurfaceComposerClient::eNoColorFill, - mContainerLayer->getHandle()); - EXPECT_NE(nullptr, mEffectLayer1.get()) << "failed to create effect layer 1"; - - mEffectLayer2 = mClient->createSurface(String8("Effect Layer"), 0 /* width */, - 0 /* height */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect | - ISurfaceComposerClient::eNoColorFill, - mContainerLayer->getHandle()); - - EXPECT_NE(nullptr, mEffectLayer2.get()) << "failed to create effect layer 2"; - - asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - t.setLayer(mParentLayer, INT32_MAX - 20).show(mParentLayer); - t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); - - t.setColor(mEffectLayer1, toHalf3(Color::BLUE)); - - t.setColor(mEffectLayer2, toHalf3(Color::GREEN)); - }); - } - - virtual void TearDown() { - // Uncomment the line right below when running any of the tests - // std::this_thread::sleep_for (std::chrono::seconds(30)); - LayerTransactionTest::TearDown(); - mParentLayer = 0; - } - - std::function<half3(Color)> toHalf3; - std::function<half4(Color)> toHalf4; - sp<SurfaceControl> mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2; - half4 mColorOrange; -}; - -TEST_F(LayerBorderTest, OverlappingVisibleRegions) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, PartiallyCoveredVisibleRegion) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mEffectLayer1, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, NonOverlappingVisibleRegion) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200)); - t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, EmptyVisibleRegion) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400)); - t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600)); - - t.enableBorder(mEffectLayer1, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, ZOrderAdjustment) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setLayer(mParentLayer, 10); - t.setLayer(mEffectLayer1, 30); - t.setLayer(mEffectLayer2, 20); - - t.enableBorder(mEffectLayer1, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, GrandChildHierarchy) { - sp<SurfaceControl> containerLayer2 = - mClient->createSurface(String8("Container Layer"), 0 /* width */, 0 /* height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceContainer | - ISurfaceComposerClient::eNoColorFill, - mContainerLayer->getHandle()); - EXPECT_NE(nullptr, containerLayer2.get()) << "failed to create container layer 2"; - - sp<SurfaceControl> effectLayer3 = - mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect | - ISurfaceComposerClient::eNoColorFill, - containerLayer2->getHandle()); - - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setCrop(effectLayer3, Rect(400, 400, 800, 800)); - t.setColor(effectLayer3, toHalf3(Color::BLUE)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(effectLayer3); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, TransparentAlpha) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setAlpha(mEffectLayer1, 0.0f); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, SemiTransparentAlpha) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setAlpha(mEffectLayer2, 0.5f); - - t.enableBorder(mEffectLayer2, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, InvisibleLayers) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.hide(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, LayerWithBuffer) { - asTransaction([&](Transaction& t) { - t.hide(mEffectLayer1); - t.hide(mEffectLayer2); - t.show(mContainerLayer); - - sp<SurfaceControl> layer = - mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceBufferState, - mContainerLayer->getHandle()); - - sp<GraphicBuffer> buffer = - sp<GraphicBuffer>::make(400u, 400u, PIXEL_FORMAT_RGBA_8888, 1u, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY | - BufferUsage::GPU_TEXTURE, - "test"); - TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN); - TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE); - - t.setBuffer(layer, buffer); - t.setPosition(layer, 100, 100); - t.show(layer); - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - }); -} - -TEST_F(LayerBorderTest, CustomWidth) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 50, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, CustomColor) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, toHalf4({255, 0, 255, 255})); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, CustomWidthAndColorAndOpacity) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200)); - t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600)); - - t.enableBorder(mContainerLayer, true, 40, toHalf4({255, 255, 0, 128})); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index 79886bde45..b4496d3f1a 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -1295,4 +1295,74 @@ TEST_F(LayerCallbackTest, SetNullBufferOnLayerWithoutBuffer) { } } +TEST_F(LayerCallbackTest, OccludedLayerHasReleaseCallback) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1a, callback1b, callback2a, callback2b; + int err = fillTransaction(transaction1, &callback1a, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2a, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + ui::Size bufferSize = getBufferSize(); + + // Occlude layer1 with layer2 + TransactionUtils::setFrame(transaction1, layer1, + Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32)); + TransactionUtils::setFrame(transaction2, layer2, + Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32)); + transaction1.apply(); + transaction2.apply(); + + ExpectedResult expected1a, expected1b, expected2a, expected2b; + expected1a.addSurface(ExpectedResult::Transaction::PRESENTED, {layer1}, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::NOT_RELEASED); + + expected2a.addSurface(ExpectedResult::Transaction::PRESENTED, {layer2}, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::NOT_RELEASED); + + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1a, expected1a, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2a, expected2a, true)); + + // Submit new buffers so previous buffers can be released + err = fillTransaction(transaction1, &callback1b, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2b, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + TransactionUtils::setFrame(transaction1, layer1, + Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32)); + TransactionUtils::setFrame(transaction2, layer2, + Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32)); + transaction1.apply(); + transaction2.apply(); + + expected1b.addSurface(ExpectedResult::Transaction::PRESENTED, {layer1}, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::RELEASED); + + expected2b.addSurface(ExpectedResult::Transaction::PRESENTED, {layer2}, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::RELEASED); + + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1b, expected1b, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2b, expected2b, true)); +} } // namespace android diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp index 15ff696412..7fce7e9af9 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -18,9 +18,12 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include <common/FlagManager.h> #include <ui/DisplayState.h> #include "LayerTransactionTest.h" +#include "gui/SurfaceComposerClient.h" +#include "ui/DisplayId.h" namespace android { @@ -37,7 +40,8 @@ protected: const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); - mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); + mMainDisplayId = ids.front(); + mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(mMainDisplayId); SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState); SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode); @@ -85,6 +89,7 @@ protected: ui::DisplayState mMainDisplayState; ui::DisplayMode mMainDisplayMode; sp<IBinder> mMainDisplay; + PhysicalDisplayId mMainDisplayId; sp<IBinder> mVirtualDisplay; sp<IGraphicBufferProducer> mProducer; sp<SurfaceControl> mColorLayer; @@ -119,6 +124,9 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { createDisplay(mMainDisplayState.layerStackSpaceRect, ui::DEFAULT_LAYER_STACK); createColorLayer(ui::DEFAULT_LAYER_STACK); + sp<SurfaceControl> mirrorSc = + SurfaceComposerClient::getDefault()->mirrorDisplay(mMainDisplayId); + asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); // Verify color layer renders correctly on main display and it is mirrored on the @@ -133,6 +141,37 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); } +TEST_F(MultiDisplayLayerBoundsTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) { + // Create a display and use a unique layerstack ID for mirrorDisplay() so + // the contents of the main display are mirrored on to the virtual display. + + // A unique layerstack ID must be used because sharing the same layerFE + // with more than one display is unsupported. A unique layerstack ensures + // that a different layerFE is used between displays. + constexpr ui::LayerStack layerStack{77687666}; // ASCII for MDLB (MultiDisplayLayerBounds) + createDisplay(mMainDisplayState.layerStackSpaceRect, layerStack); + createColorLayer(ui::DEFAULT_LAYER_STACK); + + sp<SurfaceControl> mirrorSc = + SurfaceComposerClient::getDefault()->mirrorDisplay(mMainDisplayId); + + asTransaction([&](Transaction& t) { + t.setPosition(mColorLayer, 10, 10); + t.setLayerStack(mirrorSc, layerStack); + }); + + // Verify color layer renders correctly on main display and it is mirrored on the + // virtual display. + std::unique_ptr<ScreenCapture> sc; + ScreenCapture::captureScreen(&sc, mMainDisplay); + sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); + sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); + + ScreenCapture::captureScreen(&sc, mVirtualDisplay); + sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); + sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); +} + } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index 797a64c7d7..87e6d3ee9f 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -16,9 +16,11 @@ #ifndef ANDROID_TRANSACTION_TEST_HARNESSES #define ANDROID_TRANSACTION_TEST_HARNESSES +#include <common/FlagManager.h> #include <ui/DisplayState.h> #include "LayerTransactionTest.h" +#include "ui/LayerStack.h" namespace android { @@ -36,9 +38,10 @@ public: case RenderPath::VIRTUAL_DISPLAY: const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); + const PhysicalDisplayId displayId = ids.front(); const auto displayToken = ids.empty() ? nullptr - : SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); + : SurfaceComposerClient::getPhysicalDisplayToken(displayId); ui::DisplayState displayState; SurfaceComposerClient::getDisplayState(displayToken, &displayState); @@ -66,11 +69,21 @@ public: vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/); + constexpr ui::LayerStack layerStack{ + 848472}; // ASCII for TTH (TransactionTestHarnesses) + sp<SurfaceControl> mirrorSc = + SurfaceComposerClient::getDefault()->mirrorDisplay(displayId); + SurfaceComposerClient::Transaction t; t.setDisplaySurface(vDisplay, producer); - t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK); t.setDisplayProjection(vDisplay, displayState.orientation, Rect(displayState.layerStackSpaceRect), Rect(resolution)); + if (FlagManager::getInstance().ce_fence_promise()) { + t.setDisplayLayerStack(vDisplay, layerStack); + t.setLayerStack(mirrorSc, layerStack); + } else { + t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK); + } t.apply(); SurfaceComposerClient::Transaction().apply(true); @@ -85,6 +98,15 @@ public: constexpr bool kContainsHdr = false; auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer, kContainsHdr); itemConsumer->releaseBuffer(item); + + // Possible race condition with destroying virtual displays, in which + // CompositionEngine::present may attempt to be called on the same + // display multiple times. The layerStack is set to invalid here so + // that the display is ignored if that scenario occurs. + if (FlagManager::getInstance().ce_fence_promise()) { + t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK); + t.apply(true); + } SurfaceComposerClient::destroyDisplay(vDisplay); return sc; } diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp index 0adf0b617a..51b5f40a52 100644 --- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -85,7 +85,7 @@ TEST_F(FlagManagerTest, legacyReturnsValue) { EXPECT_EQ(false, mFlagManager.test_flag()); } -TEST_F(FlagManagerTest, creashesIfQueriedBeforeBoot) { +TEST_F(FlagManagerTest, crashesIfQueriedBeforeBoot) { mFlagManager.markBootIncomplete(); EXPECT_DEATH(FlagManager::getInstance() .refresh_rate_overlay_on_external_display(), ""); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 110f324c8b..2fb80b146e 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -274,6 +274,8 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote) { } TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + createLegacyAndFrontedEndLayer(1); setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 9b8ff42782..c63aaeb487 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -548,7 +548,41 @@ TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) { EXPECT_EQ(0, frequentLayerCount(time)); } +TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false); + + auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()) + .WillRepeatedly( + Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default, + Seamlessness::OnlySeamless, FrameRateCategory::High))); + + // Set default to Min so it is obvious that the vote reset triggered. + setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + + nsecs_t time = systemTime(); + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); + time += HI_FPS_PERIOD; + } + + // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr. + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory); +} + TEST_F(LayerHistoryTest, oneLayerExplicitCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()) @@ -588,6 +622,8 @@ TEST_F(LayerHistoryTest, oneLayerExplicitCategory) { // This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote, // the category is NoPreference. TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()) @@ -617,6 +653,8 @@ TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) { } TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()) diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 3baa48d002..94989aac84 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -17,6 +17,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <common/test/FlagUtils.h> #include <renderengine/mock/FakeExternalTexture.h> #include "FrontEnd/LayerHierarchy.h" @@ -26,6 +27,8 @@ #include "LayerHierarchyTest.h" #include "ui/GraphicTypes.h" +#include <com_android_graphics_surfaceflinger_flags.h> + #define UPDATE_AND_VERIFY(BUILDER, ...) \ ({ \ SCOPED_TRACE(""); \ @@ -42,6 +45,7 @@ namespace android::surfaceflinger::frontend { using ftl::Flags; using namespace ftl::flag_operators; +using namespace com::android::graphics::surfaceflinger; // To run test: /** @@ -668,6 +672,8 @@ TEST_F(LayerSnapshotTest, translateDataspace) { // This test is similar to "frameRate" test case but checks that the setFrameRateCategory API // interaction also works correctly with the setFrameRate API within SF frontend. TEST_F(LayerSnapshotTest, frameRateWithCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + // ROOT // ├── 1 // │ ├── 11 (frame rate set to 244.f) @@ -864,6 +870,8 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategy) { } TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { + SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true); + // ROOT // ├── 1 // │ ├── 11 diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index d9343c7813..86ad86eae2 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -20,6 +20,8 @@ #include <DisplayHardware/PowerAdvisor.h> #include <android_os.h> #include <binder/Status.h> +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/FlagManager.h> #include <common/test/FlagUtils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -51,6 +53,20 @@ public: void setTimingTestingMode(bool testinMode); void allowReportActualToAcquireMutex(); bool sessionExists(); + int64_t toNanos(Duration d); + + struct GpuTestConfig { + bool adpfGpuFlagOn; + Duration frame1GpuFenceDuration; + Duration frame2GpuFenceDuration; + Duration vsyncPeriod; + Duration presentDuration = 0ms; + Duration postCompDuration = 0ms; + bool frame1RequiresRenderEngine; + bool frame2RequiresRenderEngine; + }; + + WorkDuration testGpuScenario(GpuTestConfig& config); protected: TestableSurfaceFlinger mFlinger; @@ -65,6 +81,10 @@ bool PowerAdvisorTest::sessionExists() { return mPowerAdvisor->mHintSession != nullptr; } +int64_t PowerAdvisorTest::toNanos(Duration d) { + return std::chrono::nanoseconds(d).count(); +} + void PowerAdvisorTest::SetUp() { mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger()); mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>(); @@ -117,6 +137,71 @@ void PowerAdvisorTest::allowReportActualToAcquireMutex() { mPowerAdvisor->mDelayReportActualMutexAcquisitonPromise.set_value(true); } +WorkDuration PowerAdvisorTest::testGpuScenario(GpuTestConfig& config) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_gpu_sf, + config.adpfGpuFlagOn); + mPowerAdvisor->onBootFinished(); + startPowerHintSession(); + + std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0), + GpuVirtualDisplayId(1)}; + mPowerAdvisor->setDisplays(displayIds); + auto display1 = displayIds[0]; + // 60hz + + TimePoint startTime = TimePoint::now(); + // advisor only starts on frame 2 so do an initial frame + fakeBasicFrameTiming(startTime, config.vsyncPeriod); + setExpectedTiming(config.vsyncPeriod, startTime + config.vsyncPeriod); + + // report GPU + mPowerAdvisor->setRequiresRenderEngine(display1, config.frame1RequiresRenderEngine); + if (config.adpfGpuFlagOn) { + mPowerAdvisor->setGpuStartTime(display1, startTime); + } + if (config.frame1GpuFenceDuration.count() == Fence::SIGNAL_TIME_PENDING) { + mPowerAdvisor->setGpuFenceTime(display1, + std::make_unique<FenceTime>(Fence::SIGNAL_TIME_PENDING)); + } else { + TimePoint end = startTime + config.frame1GpuFenceDuration; + mPowerAdvisor->setGpuFenceTime(display1, std::make_unique<FenceTime>(end.ns())); + } + + // increment the frame + std::this_thread::sleep_for(config.vsyncPeriod); + startTime = TimePoint::now(); + fakeBasicFrameTiming(startTime, config.vsyncPeriod); + setExpectedTiming(config.vsyncPeriod, startTime + config.vsyncPeriod); + + // report GPU + mPowerAdvisor->setRequiresRenderEngine(display1, config.frame2RequiresRenderEngine); + if (config.adpfGpuFlagOn) { + mPowerAdvisor->setGpuStartTime(display1, startTime); + } + if (config.frame2GpuFenceDuration.count() == Fence::SIGNAL_TIME_PENDING) { + mPowerAdvisor->setGpuFenceTime(display1, + std::make_unique<FenceTime>(Fence::SIGNAL_TIME_PENDING)); + } else { + TimePoint end = startTime + config.frame2GpuFenceDuration; + mPowerAdvisor->setGpuFenceTime(display1, std::make_unique<FenceTime>(end.ns())); + } + mPowerAdvisor->setSfPresentTiming(startTime, startTime + config.presentDuration); + mPowerAdvisor->setCompositeEnd(startTime + config.presentDuration + config.postCompDuration); + + // don't report timing for the HWC + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime, startTime); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime, startTime); + + std::vector<aidl::android::hardware::power::WorkDuration> durationReq; + EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(_)) + .Times(1) + .WillOnce(DoAll(testing::SaveArg<0>(&durationReq), + testing::Return(testing::ByMove(HalResult<void>::ok())))); + mPowerAdvisor->reportActualWorkDuration(); + EXPECT_EQ(durationReq.size(), 1u); + return durationReq[0]; +} + Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) { return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate : PowerAdvisor::kFenceWaitStartDelayValidated); @@ -390,5 +475,148 @@ TEST_F(PowerAdvisorTest, legacyHintSessionCreationStillWorks) { mPowerAdvisor->startPowerHintSession({1, 2, 3}); } +TEST_F(PowerAdvisorTest, setGpuFenceTime_cpuThenGpuFrames) { + GpuTestConfig config{ + .adpfGpuFlagOn = false, + // faked buffer fence time for testing + .frame1GpuFenceDuration = 41ms, + .frame2GpuFenceDuration = 31ms, + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = false, + .frame2RequiresRenderEngine = true, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, 0L); + EXPECT_EQ(res.cpuDurationNanos, 0L); + EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin())); + EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_cpuThenGpuFrames_flagOn) { + GpuTestConfig config{ + .adpfGpuFlagOn = true, + .frame1GpuFenceDuration = 40ms, + .frame2GpuFenceDuration = 30ms, + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = false, + .frame2RequiresRenderEngine = true, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms)); + EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms)); + EXPECT_EQ(res.durationNanos, toNanos(30ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_gpuThenCpuFrames) { + GpuTestConfig config{ + .adpfGpuFlagOn = false, + // faked fence time for testing + .frame1GpuFenceDuration = 41ms, + .frame2GpuFenceDuration = 31ms, + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = true, + .frame2RequiresRenderEngine = false, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, 0L); + EXPECT_EQ(res.cpuDurationNanos, 0L); + EXPECT_EQ(res.durationNanos, toNanos(10ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_gpuThenCpuFrames_flagOn) { + GpuTestConfig config{ + .adpfGpuFlagOn = true, + .frame1GpuFenceDuration = 40ms, + .frame2GpuFenceDuration = 30ms, + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = true, + .frame2RequiresRenderEngine = false, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, 0L); + EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms)); + EXPECT_EQ(res.durationNanos, toNanos(10ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_twoSignaledGpuFrames) { + GpuTestConfig config{ + .adpfGpuFlagOn = false, + // added a margin as a workaround since we set GPU start time at the time of fence set + // call + .frame1GpuFenceDuration = 31ms, + .frame2GpuFenceDuration = 51ms, + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = true, + .frame2RequiresRenderEngine = true, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, 0L); + EXPECT_EQ(res.cpuDurationNanos, 0L); + EXPECT_GE(res.durationNanos, toNanos(50ms + getErrorMargin())); + EXPECT_LE(res.durationNanos, toNanos(51ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_twoSignaledGpuFenceFrames_flagOn) { + GpuTestConfig config{ + .adpfGpuFlagOn = true, + .frame1GpuFenceDuration = 30ms, + .frame2GpuFenceDuration = 50ms, + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = true, + .frame2RequiresRenderEngine = true, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, toNanos(50ms)); + EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms)); + EXPECT_EQ(res.durationNanos, toNanos(50ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_UnsingaledGpuFenceFrameUsingPreviousFrame) { + GpuTestConfig config{ + .adpfGpuFlagOn = false, + .frame1GpuFenceDuration = 31ms, + .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING), + .vsyncPeriod = 10ms, + .presentDuration = 2ms, + .postCompDuration = 8ms, + .frame1RequiresRenderEngine = true, + .frame2RequiresRenderEngine = true, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, 0L); + EXPECT_EQ(res.cpuDurationNanos, 0L); + EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin())); + EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin())); +} + +TEST_F(PowerAdvisorTest, setGpuFenceTime_UnsingaledGpuFenceFrameUsingPreviousFrame_flagOn) { + GpuTestConfig config{ + .adpfGpuFlagOn = true, + .frame1GpuFenceDuration = 30ms, + .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING), + .vsyncPeriod = 10ms, + .presentDuration = 22ms, + .postCompDuration = 88ms, + .frame1RequiresRenderEngine = true, + .frame2RequiresRenderEngine = true, + }; + WorkDuration res = testGpuScenario(config); + EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms)); + EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms)); + EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin())); +} + } // namespace } // namespace android::Hwc2::impl diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index fe0e3d11ae..cf9a7d3e69 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -103,8 +103,9 @@ struct TestableRefreshRateSelector : RefreshRateSelector { auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; } auto getRankedFrameRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals = {}) const { - const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals); + GlobalSignals signals = {}, Fps pacesetterFps = {}) const { + const auto result = + RefreshRateSelector::getRankedFrameRates(layers, signals, pacesetterFps); EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(), ScoredFrameRate::DescendingScore{})); @@ -114,8 +115,8 @@ struct TestableRefreshRateSelector : RefreshRateSelector { auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers, GlobalSignals signals) const { - const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals); - return std::make_pair(ranking, consideredSignals); + const auto result = getRankedFrameRates(layers, signals); + return std::make_pair(result.ranking, result.consideredSignals); } FrameRateMode getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {}, @@ -295,6 +296,12 @@ protected: << "Did not get expected frame rate for frameRate=" << to_string(testCase.desiredFrameRate) << " category=" << ftl::enum_string(testCase.frameRateCategory); + EXPECT_EQ(testCase.expectedModeId, + selector.getBestFrameRateMode(layers).modePtr->getId()) + << "Did not get expected DisplayModeId for modeId=" + << ftl::to_underlying(testCase.expectedModeId) + << " frameRate=" << to_string(testCase.desiredFrameRate) + << " category=" << ftl::enum_string(testCase.frameRateCategory); } } }; @@ -1381,7 +1388,7 @@ TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { auto selector = createSelector(kModes_60_90, kModeId60); - auto [refreshRates, signals] = selector.getRankedFrameRates({}, {}); + auto [refreshRates, signals, _] = selector.getRankedFrameRates({}, {}); EXPECT_FALSE(signals.powerOnImminent); auto expectedRefreshRates = []() -> std::vector<FrameRateMode> { @@ -1465,10 +1472,32 @@ TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) { } } +TEST_P(RefreshRateSelectorTest, pacesetterConsidered) { + auto selector = createSelector(kModes_60_90, kModeId60); + constexpr RefreshRateSelector::GlobalSignals kNoSignals; + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].vote = LayerVoteType::Min; + + // The pacesetterFps takes precedence over the LayerRequirement. + { + const auto result = selector.getRankedFrameRates(layers, {}, 90_Hz); + EXPECT_EQ(kMode90, result.ranking.front().frameRateMode.modePtr); + EXPECT_EQ(kNoSignals, result.consideredSignals); + } + + // The pacesetterFps takes precedence over GlobalSignals. + { + const auto result = selector.getRankedFrameRates(layers, {.touch = true}, 60_Hz); + EXPECT_EQ(kMode60, result.ranking.front().frameRateMode.modePtr); + EXPECT_EQ(kNoSignals, result.consideredSignals); + } +} + TEST_P(RefreshRateSelectorTest, touchConsidered) { auto selector = createSelector(kModes_60_90, kModeId60); - auto [_, signals] = selector.getRankedFrameRates({}, {}); + auto signals = selector.getRankedFrameRates({}, {}).consideredSignals; EXPECT_FALSE(signals.touch); std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true}); @@ -1534,7 +1563,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_30_60 {0_Hz, FrameRateCategory::High, 90_Hz}, {0_Hz, FrameRateCategory::Normal, 60_Hz}, {0_Hz, FrameRateCategory::Low, 30_Hz}, - {0_Hz, FrameRateCategory::NoPreference, 30_Hz}, + {0_Hz, FrameRateCategory::NoPreference, 60_Hz}, // Cases that have both desired frame rate and frame rate category requirements. {24_Hz, FrameRateCategory::High, 120_Hz}, @@ -1591,6 +1620,7 @@ TEST_P(RefreshRateSelectorTest, // Expected result Fps expectedFrameRate = 0_Hz; + DisplayModeId expectedModeId = kModeId90; }; testFrameRateCategoryWithMultipleLayers( @@ -1605,7 +1635,7 @@ TEST_P(RefreshRateSelectorTest, testFrameRateCategoryWithMultipleLayers( std::initializer_list<Case>{ - {0_Hz, FrameRateCategory::Normal, 60_Hz}, + {0_Hz, FrameRateCategory::Normal, 60_Hz, kModeId60}, {0_Hz, FrameRateCategory::High, 90_Hz}, {0_Hz, FrameRateCategory::NoPreference, 90_Hz}, }, @@ -1614,18 +1644,18 @@ TEST_P(RefreshRateSelectorTest, testFrameRateCategoryWithMultipleLayers( std::initializer_list<Case>{ {30_Hz, FrameRateCategory::High, 90_Hz}, - {24_Hz, FrameRateCategory::High, 120_Hz}, - {12_Hz, FrameRateCategory::Normal, 120_Hz}, - {30_Hz, FrameRateCategory::NoPreference, 120_Hz}, + {24_Hz, FrameRateCategory::High, 120_Hz, kModeId120}, + {12_Hz, FrameRateCategory::Normal, 120_Hz, kModeId120}, + {30_Hz, FrameRateCategory::NoPreference, 120_Hz, kModeId120}, }, selector); testFrameRateCategoryWithMultipleLayers( std::initializer_list<Case>{ - {24_Hz, FrameRateCategory::Default, 120_Hz}, - {30_Hz, FrameRateCategory::Default, 120_Hz}, - {120_Hz, FrameRateCategory::Default, 120_Hz}, + {24_Hz, FrameRateCategory::Default, 120_Hz, kModeId120}, + {30_Hz, FrameRateCategory::Default, 120_Hz, kModeId120}, + {120_Hz, FrameRateCategory::Default, 120_Hz, kModeId120}, }, selector); } @@ -1640,6 +1670,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategoryMultiL // Expected result Fps expectedFrameRate = 0_Hz; + DisplayModeId expectedModeId = kModeId120; }; testFrameRateCategoryWithMultipleLayers(std::initializer_list< @@ -1970,6 +2001,122 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_Touch } TEST_P(RefreshRateSelectorTest, + getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) { + SET_FLAG_FOR_TEST(flags::vrr_config, false); + using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; + struct LayerArg { + // Params + FrameRateCategory frameRateCategory = FrameRateCategory::Default; + LayerVoteType voteType = LayerVoteType::ExplicitDefault; + + // Expected result + Fps expectedFrameRate = 0_Hz; + DisplayModeId expectedModeId = kModeId60; + }; + + const auto runTest = [&](const TestableRefreshRateSelector& selector, + const std::initializer_list<LayerArg>& layerArgs, + const RefreshRateSelector::GlobalSignals& signals) { + std::vector<LayerRequirement> layers; + for (auto testCase : layerArgs) { + ALOGI("**** %s: Testing frameRateCategory=%s", __func__, + ftl::enum_string(testCase.frameRateCategory).c_str()); + + if (testCase.frameRateCategory != FrameRateCategory::Default) { + std::stringstream ss; + ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")"; + LayerRequirement layer = {.name = ss.str(), + .vote = LayerVoteType::ExplicitCategory, + .frameRateCategory = testCase.frameRateCategory, + .weight = 1.f}; + layers.push_back(layer); + } + + if (testCase.voteType != LayerVoteType::ExplicitDefault) { + std::stringstream ss; + ss << ftl::enum_string(testCase.voteType); + LayerRequirement layer = {.name = ss.str(), + .vote = testCase.voteType, + .weight = 1.f}; + layers.push_back(layer); + } + + EXPECT_EQ(testCase.expectedFrameRate, + selector.getBestFrameRateMode(layers, signals).modePtr->getPeakFps()) + << "Did not get expected frame rate for" + << " category=" << ftl::enum_string(testCase.frameRateCategory); + EXPECT_EQ(testCase.expectedModeId, + selector.getBestFrameRateMode(layers, signals).modePtr->getId()) + << "Did not get expected DisplayModeId for modeId=" + << ftl::to_underlying(testCase.expectedModeId) + << " category=" << ftl::enum_string(testCase.frameRateCategory); + } + }; + + { + // IdleTimer not configured + auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120); + ASSERT_EQ(0ms, selector.getIdleTimerTimeout()); + + runTest(selector, + std::initializer_list<LayerArg>{ + // Rate does not change due to NoPreference. + {.frameRateCategory = FrameRateCategory::NoPreference, + .expectedFrameRate = 120_Hz, + .expectedModeId = kModeId120}, + {.voteType = LayerVoteType::NoVote, + .expectedFrameRate = 120_Hz, + .expectedModeId = kModeId120}, + {.frameRateCategory = FrameRateCategory::NoPreference, + .expectedFrameRate = 120_Hz, + .expectedModeId = kModeId120}, + }, + {.idle = false}); + } + + // IdleTimer configured + constexpr std::chrono::milliseconds kIdleTimerTimeoutMs = 10ms; + auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120, + Config{ + .legacyIdleTimerTimeout = kIdleTimerTimeoutMs, + }); + ASSERT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); + ASSERT_EQ(kIdleTimerTimeoutMs, selector.getIdleTimerTimeout()); + runTest(selector, + std::initializer_list<LayerArg>{ + // Rate won't change immediately and will stay 120 due to NoPreference, as + // idle timer did not timeout yet. + {.frameRateCategory = FrameRateCategory::NoPreference, + .expectedFrameRate = 120_Hz, + .expectedModeId = kModeId120}, + {.voteType = LayerVoteType::NoVote, + .expectedFrameRate = 120_Hz, + .expectedModeId = kModeId120}, + {.frameRateCategory = FrameRateCategory::NoPreference, + .expectedFrameRate = 120_Hz, + .expectedModeId = kModeId120}, + }, + {.idle = false}); + + // Idle timer is triggered using GlobalSignals. + ASSERT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); + ASSERT_EQ(kIdleTimerTimeoutMs, selector.getIdleTimerTimeout()); + runTest(selector, + std::initializer_list<LayerArg>{ + {.frameRateCategory = FrameRateCategory::NoPreference, + .expectedFrameRate = 60_Hz, + .expectedModeId = kModeId60}, + {.voteType = LayerVoteType::NoVote, + .expectedFrameRate = 60_Hz, + .expectedModeId = kModeId60}, + {.frameRateCategory = FrameRateCategory::NoPreference, + .expectedFrameRate = 60_Hz, + .expectedModeId = kModeId60}, + }, + {.idle = true}); +} + +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_smoothSwitchOnly_60_120_nonVrr) { if (GetParam() != Config::FrameRateOverride::Enabled) { return; @@ -1992,8 +2139,7 @@ TEST_P(RefreshRateSelectorTest, const std::initializer_list<Case> testCases = { // These layers may switch modes because smoothSwitchOnly=false. {FrameRateCategory::Default, false, 120_Hz, kModeId120}, - // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate. - {FrameRateCategory::NoPreference, false, 60_Hz, kModeId60}, + {FrameRateCategory::NoPreference, false, 120_Hz, kModeId120}, {FrameRateCategory::Low, false, 30_Hz, kModeId60}, {FrameRateCategory::Normal, false, 60_Hz, kModeId60}, {FrameRateCategory::High, false, 120_Hz, kModeId120}, @@ -2240,7 +2386,7 @@ TEST_P(RefreshRateSelectorTest, lr.name = "60Hz ExplicitDefault"; lr.focused = true; - const auto [rankedFrameRate, signals] = + const auto [rankedFrameRate, signals, _] = selector.getRankedFrameRates(layers, {.touch = true, .idle = true}); EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60); @@ -2464,7 +2610,7 @@ TEST_P(RefreshRateSelectorTest, EXPECT_EQ(SetPolicyResult::Changed, selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}})); - const auto [ranking, signals] = selector.getRankedFrameRates({}, {}); + const auto [ranking, signals, _] = selector.getRankedFrameRates({}, {}); EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90); EXPECT_FALSE(signals.touch); @@ -2848,7 +2994,7 @@ TEST_P(RefreshRateSelectorTest, idle) { layers[0].vote = voteType; layers[0].desiredRefreshRate = 90_Hz; - const auto [ranking, signals] = + const auto [ranking, signals, _] = selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true}); // Refresh rate will be chosen by either touch state or idle state. @@ -2998,16 +3144,17 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) { auto selector = createSelector(kModes_30_60_72_90_120, kModeId60); using GlobalSignals = RefreshRateSelector::GlobalSignals; - const auto args = std::make_pair(std::vector<LayerRequirement>{}, - GlobalSignals{.touch = true, .idle = true}); - const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{ {90_Hz, kMode90}}}, GlobalSignals{.touch = true}}; - selector.mutableGetRankedRefreshRatesCache() = {args, result}; + selector.mutableGetRankedRefreshRatesCache() = {.layers = std::vector<LayerRequirement>{}, + .signals = GlobalSignals{.touch = true, + .idle = true}, + .result = result}; - EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second)); + const auto& cache = *selector.mutableGetRankedRefreshRatesCache(); + EXPECT_EQ(result, selector.getRankedFrameRates(cache.layers, cache.signals)); } TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) { @@ -3015,15 +3162,18 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) { EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache()); - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true}; + const std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; + const RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true}; + const Fps pacesetterFps = 60_Hz; - const auto result = selector.getRankedFrameRates(layers, globalSignals); + const auto result = selector.getRankedFrameRates(layers, globalSignals, pacesetterFps); const auto& cache = selector.mutableGetRankedRefreshRatesCache(); ASSERT_TRUE(cache); - EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals)); + EXPECT_EQ(cache->layers, layers); + EXPECT_EQ(cache->signals, globalSignals); + EXPECT_EQ(cache->pacesetterFps, pacesetterFps); EXPECT_EQ(cache->result, result); } @@ -3950,7 +4100,7 @@ TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) { layers[0].vote = voteType; layers[0].desiredRefreshRate = 90_Hz; - const auto [ranking, signals] = + const auto [ranking, signals, _] = selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true}); // Refresh rate will be chosen by either touch state or idle state. diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index d4735c7558..7479a4f890 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -362,7 +362,7 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { globalSignals)(kDisplayId2, FrameRateMode{60_Hz, kDisplay2Mode60}, - globalSignals); + GlobalSignals{}); std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; @@ -381,7 +381,7 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { globalSignals)(kDisplayId2, FrameRateMode{120_Hz, kDisplay2Mode120}, - globalSignals); + GlobalSignals{}); mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); @@ -400,7 +400,7 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { globalSignals)(kDisplayId2, FrameRateMode{120_Hz, kDisplay2Mode120}, - globalSignals); + GlobalSignals{}); const auto actualChoices = mScheduler->chooseDisplayModes(); EXPECT_EQ(expectedChoices, actualChoices); @@ -422,10 +422,10 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120}, globalSignals)(kDisplayId2, FrameRateMode{120_Hz, kDisplay2Mode120}, - globalSignals)(kDisplayId3, - FrameRateMode{60_Hz, - kDisplay3Mode60}, - globalSignals); + GlobalSignals{})(kDisplayId3, + FrameRateMode{60_Hz, + kDisplay3Mode60}, + GlobalSignals{}); const auto actualChoices = mScheduler->chooseDisplayModes(); EXPECT_EQ(expectedChoices, actualChoices); @@ -440,12 +440,12 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { expectedChoices = ftl::init::map< const PhysicalDisplayId&, DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60}, - globalSignals)(kDisplayId2, - FrameRateMode{60_Hz, kDisplay2Mode60}, - globalSignals)(kDisplayId3, - FrameRateMode{60_Hz, - kDisplay3Mode60}, - globalSignals); + GlobalSignals{})(kDisplayId2, + FrameRateMode{60_Hz, kDisplay2Mode60}, + GlobalSignals{})(kDisplayId3, + FrameRateMode{60_Hz, + kDisplay3Mode60}, + globalSignals); const auto actualChoices = mScheduler->chooseDisplayModes(); EXPECT_EQ(expectedChoices, actualChoices); @@ -607,10 +607,10 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(4500))); + TimePoint::fromNs(5500))); EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(6500))); + TimePoint::fromNs(7500))); } TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) { diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 48707cb6d2..eafba0ad8d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -704,14 +704,17 @@ TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) { EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000)); vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); - EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); - EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); - EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); - EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000)); - EXPECT_EQ(11000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000)); - EXPECT_EQ(13000, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000)); - EXPECT_EQ(17000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500)); - EXPECT_EQ(20000, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000)); + EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); + EXPECT_EQ(9500, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000)); + EXPECT_EQ(11500, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000)); + EXPECT_EQ(13500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000)); + EXPECT_EQ(16500, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500)); + EXPECT_EQ(20500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000)); + + // matches the previous cadence + EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(20500, 20500)); } TEST_F(VSyncPredictorTest, minFramePeriodDoesntApplyWhenSameWithRefreshRate) { @@ -820,7 +823,7 @@ TEST_F(VSyncPredictorTest, returnsCorrectVsyncWhenLastIsNot) { vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); vrrTracker.addVsyncTimestamp(0); - EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1234, 1234)); + EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(1234, 1234)); } TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { @@ -944,6 +947,27 @@ TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(8000)); } +// b/329310308 +TEST_F(VSyncPredictorTest, renderRateChangeAfterAppliedImmediately) { + tracker.addVsyncTimestamp(1000); + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(2000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(2001), Eq(3000)); + + tracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ true); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(3000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(3001), Eq(5000)); + + tracker.setRenderRate(Fps::fromPeriodNsecs(4000), /*applyImmediately*/ false); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(3000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(3001), Eq(5000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(9000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(13000)); +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 8d9623de1c..e3aa4ef77b 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -195,9 +195,7 @@ TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) { TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { bool periodFlushed = true; - EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); - mReactor.setIgnorePresentFences(true); - + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3); nsecs_t const newPeriod = 5000; mReactor.onDisplayModeChanged(displayMode(newPeriod), false); @@ -207,7 +205,7 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); - EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); + EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { @@ -463,8 +461,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { bool periodFlushed = true; - EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); - mReactor.setIgnorePresentFences(true); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3); nsecs_t const newPeriod = 5000; mReactor.onDisplayModeChanged(displayMode(newPeriod), false); @@ -476,7 +473,7 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed)); EXPECT_TRUE(periodFlushed); - EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); + EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { @@ -486,8 +483,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { *mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */); bool periodFlushed = true; - EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4); - idleReactor.setIgnorePresentFences(true); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5); // First, set the same period, which should only be confirmed when we receive two // matching callbacks @@ -512,7 +508,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed)); EXPECT_TRUE(periodFlushed); - EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0))); + EXPECT_FALSE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0))); } } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index 8e8eb1dc1f..e8630ba689 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -40,6 +40,7 @@ public: MOCK_METHOD(void, reportActualWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override)); + MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override)); MOCK_METHOD(void, setGpuFenceTime, (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); MOCK_METHOD(void, setHwcValidateTiming, @@ -49,8 +50,8 @@ public: (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime), (override)); MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); - MOCK_METHOD(void, setRequiresClientComposition, - (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine), + (override)); MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override)); MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime), (override)); diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h index 38c422a26d..07916b60a7 100644 --- a/services/surfaceflinger/tests/utils/ColorUtils.h +++ b/services/surfaceflinger/tests/utils/ColorUtils.h @@ -33,10 +33,6 @@ struct Color { static const Color WHITE; static const Color BLACK; static const Color TRANSPARENT; - - half3 toHalf3() { return half3{r / 255.0f, g / 255.0f, b / 255.0f}; } - - half4 toHalf4() { return half4{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}; } }; const Color Color::RED{255, 0, 0, 255}; @@ -85,14 +81,6 @@ public: } color = ret; } - - static half3 toHalf3(const Color& color) { - return half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}; - } - - static half4 toHalf4(const Color& color) { - return half4{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f}; - } }; } // namespace } // namespace android diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 13141933ac..74d3d9dc64 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -555,8 +555,7 @@ PixelFormat GetNativePixelFormat(VkFormat format) { return native_format; } -DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace, - PixelFormat pixelFormat) { +DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace, VkFormat format) { switch (colorspace) { case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: return DataSpace::SRGB; @@ -575,7 +574,7 @@ DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace, case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: return DataSpace::SRGB; case VK_COLOR_SPACE_BT2020_LINEAR_EXT: - if (pixelFormat == PixelFormat::RGBA_FP16) { + if (format == VK_FORMAT_R16G16B16A16_SFLOAT) { return DataSpace::BT2020_LINEAR_EXTENDED; } else { return DataSpace::BT2020_LINEAR; @@ -764,21 +763,20 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, }; + VkFormat format = VK_FORMAT_UNDEFINED; if (colorspace_ext) { for (VkColorSpaceKHR colorSpace : colorSpaceSupportedByVkEXTSwapchainColorspace) { - if (GetNativeDataspace(colorSpace, GetNativePixelFormat( - VK_FORMAT_R8G8B8A8_UNORM)) != - DataSpace::UNKNOWN) { + format = VK_FORMAT_R8G8B8A8_UNORM; + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_UNORM, colorSpace}); + VkSurfaceFormatKHR{format, colorSpace}); } - if (GetNativeDataspace(colorSpace, GetNativePixelFormat( - VK_FORMAT_R8G8B8A8_SRGB)) != - DataSpace::UNKNOWN) { + format = VK_FORMAT_R8G8B8A8_SRGB; + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_SRGB, colorSpace}); + VkSurfaceFormatKHR{format, colorSpace}); } } } @@ -787,78 +785,73 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, // Android users. This includes the ANGLE team (a layered implementation of // OpenGL-ES). + format = VK_FORMAT_R5G6B5_UNORM_PACK16; desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; if (AHardwareBuffer_isSupported(&desc)) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { for (VkColorSpaceKHR colorSpace : colorSpaceSupportedByVkEXTSwapchainColorspace) { - if (GetNativeDataspace( - colorSpace, - GetNativePixelFormat(VK_FORMAT_R5G6B5_UNORM_PACK16)) != + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R5G6B5_UNORM_PACK16, colorSpace}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, colorSpace}); } } } } + format = VK_FORMAT_R16G16B16A16_SFLOAT; desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; if (AHardwareBuffer_isSupported(&desc)) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { for (VkColorSpaceKHR colorSpace : colorSpaceSupportedByVkEXTSwapchainColorspace) { - if (GetNativeDataspace( - colorSpace, - GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) != + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, colorSpace}); } } for ( VkColorSpaceKHR colorSpace : colorSpaceSupportedByVkEXTSwapchainColorspaceOnFP16SurfaceOnly) { - if (GetNativeDataspace( - colorSpace, - GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) != + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, colorSpace}); } } } } + format = VK_FORMAT_A2B10G10R10_UNORM_PACK32; desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { for (VkColorSpaceKHR colorSpace : colorSpaceSupportedByVkEXTSwapchainColorspace) { - if (GetNativeDataspace( - colorSpace, GetNativePixelFormat( - VK_FORMAT_A2B10G10R10_UNORM_PACK32)) != + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_A2B10G10R10_UNORM_PACK32, colorSpace}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, colorSpace}); } } } } + format = VK_FORMAT_R8_UNORM; desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM; if (AHardwareBuffer_isSupported(&desc)) { if (colorspace_ext) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, VK_COLOR_SPACE_PASS_THROUGH_EXT}); } } @@ -877,22 +870,18 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, rgba10x6_formats_ext = true; } } + format = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16; desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM; if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) { all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { for (VkColorSpaceKHR colorSpace : colorSpaceSupportedByVkEXTSwapchainColorspace) { - if (GetNativeDataspace( - colorSpace, - GetNativePixelFormat( - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)) != + if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - colorSpace}); + all_formats.emplace_back( + VkSurfaceFormatKHR{format, colorSpace}); } } } @@ -1670,8 +1659,8 @@ VkResult CreateSwapchainKHR(VkDevice device, PixelFormat native_pixel_format = GetNativePixelFormat(create_info->imageFormat); - DataSpace native_dataspace = - GetNativeDataspace(create_info->imageColorSpace, native_pixel_format); + DataSpace native_dataspace = GetNativeDataspace( + create_info->imageColorSpace, create_info->imageFormat); if (native_dataspace == DataSpace::UNKNOWN) { ALOGE( "CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) " |