diff options
32 files changed, 1618 insertions, 1256 deletions
diff --git a/data/etc/Android.bp b/data/etc/Android.bp index da232a5935..99f4fbd7a5 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -335,6 +335,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.vulkan.compute-0.prebuilt.xml", + src: "android.hardware.vulkan.compute-0.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.vulkan.level-1.prebuilt.xml", src: "android.hardware.vulkan.level-1.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/include/input/InputConsumer.h b/include/input/InputConsumer.h new file mode 100644 index 0000000000..560e804c68 --- /dev/null +++ b/include/input/InputConsumer.h @@ -0,0 +1,249 @@ +/* + * 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 + +/* + * Native input transport. + * + * The InputConsumer is used by the application to receive events from the input dispatcher. + */ + +#include "InputTransport.h" + +namespace android { + +/* + * Consumes input events from an input channel. + */ +class InputConsumer { +public: + /* Create a consumer associated with an input channel. */ + explicit InputConsumer(const std::shared_ptr<InputChannel>& channel); + /* Create a consumer associated with an input channel, override resampling system property */ + explicit InputConsumer(const std::shared_ptr<InputChannel>& channel, + bool enableTouchResampling); + + /* Destroys the consumer and releases its input channel. */ + ~InputConsumer(); + + /* Gets the underlying input channel. */ + inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } + + /* Consumes an input event from the input channel and copies its contents into + * an InputEvent object created using the specified factory. + * + * Tries to combine a series of move events into larger batches whenever possible. + * + * If consumeBatches is false, then defers consuming pending batched events if it + * is possible for additional samples to be added to them later. Call hasPendingBatch() + * to determine whether a pending batch is available to be consumed. + * + * If consumeBatches is true, then events are still batched but they are consumed + * immediately as soon as the input channel is exhausted. + * + * The frameTime parameter specifies the time when the current display frame started + * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. + * + * The returned sequence number is never 0 unless the operation failed. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no event present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns NO_MEMORY if the event could not be created. + * Other errors probably indicate that the channel is broken. + */ + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent); + + /* Sends a finished signal to the publisher to inform it that the message + * with the specified sequence number has finished being process and whether + * the message was handled by the consumer. + * + * Returns OK on success. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. + */ + status_t sendFinishedSignal(uint32_t seq, bool handled); + + status_t sendTimeline(int32_t inputEventId, + std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); + + /* Returns true if there is a pending batch. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + bool hasPendingBatch() const; + + /* Returns the source of first pending batch if exist. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + int32_t getPendingBatchSource() const; + + /* 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 dump() const; + +private: + // True if touch resampling is enabled. + const bool mResampleTouch; + + std::shared_ptr<InputChannel> mChannel; + + // The current input message. + InputMessage mMsg; + + // True if mMsg contains a valid input message that was deferred from the previous + // call to consume and that still needs to be handled. + bool mMsgDeferred; + + // Batched motion events per device and source. + struct Batch { + std::vector<InputMessage> samples; + }; + std::vector<Batch> mBatches; + + // Touch state per device and source, only for sources of class pointer. + struct History { + nsecs_t eventTime; + BitSet32 idBits; + int32_t idToIndex[MAX_POINTER_ID + 1]; + PointerCoords pointers[MAX_POINTERS]; + + void initializeFrom(const InputMessage& msg) { + eventTime = msg.body.motion.eventTime; + idBits.clear(); + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + uint32_t id = msg.body.motion.pointers[i].properties.id; + idBits.markBit(id); + idToIndex[id] = i; + pointers[i].copyFrom(msg.body.motion.pointers[i].coords); + } + } + + void initializeFrom(const History& other) { + eventTime = other.eventTime; + idBits = other.idBits; // temporary copy + for (size_t i = 0; i < other.idBits.count(); i++) { + uint32_t id = idBits.clearFirstMarkedBit(); + int32_t index = other.idToIndex[id]; + idToIndex[id] = index; + pointers[index].copyFrom(other.pointers[index]); + } + idBits = other.idBits; // final copy + } + + const PointerCoords& getPointerById(uint32_t id) const { return pointers[idToIndex[id]]; } + + bool hasPointerId(uint32_t id) const { return idBits.hasBit(id); } + }; + struct TouchState { + int32_t deviceId; + int32_t source; + size_t historyCurrent; + size_t historySize; + History history[2]; + History lastResample; + + void initialize(int32_t incomingDeviceId, int32_t incomingSource) { + deviceId = incomingDeviceId; + source = incomingSource; + historyCurrent = 0; + historySize = 0; + lastResample.eventTime = 0; + lastResample.idBits.clear(); + } + + void addHistory(const InputMessage& msg) { + historyCurrent ^= 1; + if (historySize < 2) { + historySize += 1; + } + history[historyCurrent].initializeFrom(msg); + } + + const History* getHistory(size_t index) const { + return &history[(historyCurrent + index) & 1]; + } + + bool recentCoordinatesAreIdentical(uint32_t id) const { + // Return true if the two most recently received "raw" coordinates are identical + if (historySize < 2) { + return false; + } + if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) { + return false; + } + float currentX = getHistory(0)->getPointerById(id).getX(); + float currentY = getHistory(0)->getPointerById(id).getY(); + float previousX = getHistory(1)->getPointerById(id).getX(); + float previousY = getHistory(1)->getPointerById(id).getY(); + if (currentX == previousX && currentY == previousY) { + return true; + } + return false; + } + }; + std::vector<TouchState> mTouchStates; + + // Chain of batched sequence numbers. When multiple input messages are combined into + // a batch, we append a record here that associates the last sequence number in the + // batch with the previous one. When the finished signal is sent, we traverse the + // chain to individually finish all input messages that were part of the batch. + struct SeqChain { + uint32_t seq; // sequence number of batched input message + uint32_t chain; // sequence number of previous batched input message + }; + std::vector<SeqChain> mSeqChains; + + // 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; + + status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, + InputEvent** outEvent); + status_t consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count, + uint32_t* outSeq, InputEvent** outEvent); + + void updateTouchState(InputMessage& msg); + void resampleTouchState(nsecs_t frameTime, MotionEvent* event, const InputMessage* next); + + ssize_t findBatch(int32_t deviceId, int32_t source) const; + ssize_t findTouchState(int32_t deviceId, int32_t source) const; + + nsecs_t getConsumeTime(uint32_t seq) const; + void popConsumeTime(uint32_t seq); + status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); + + static void rewriteMessage(TouchState& state, InputMessage& msg); + static bool canAddSample(const Batch& batch, const InputMessage* msg); + static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); + + static bool isTouchResamplingEnabled(); +}; + +} // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index aca4b622d1..5f9c8f5a5c 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -451,229 +451,4 @@ private: InputVerifier mInputVerifier; }; -/* - * Consumes input events from an input channel. - */ -class InputConsumer { -public: - /* Create a consumer associated with an input channel. */ - explicit InputConsumer(const std::shared_ptr<InputChannel>& channel); - /* Create a consumer associated with an input channel, override resampling system property */ - explicit InputConsumer(const std::shared_ptr<InputChannel>& channel, - bool enableTouchResampling); - - /* Destroys the consumer and releases its input channel. */ - ~InputConsumer(); - - /* Gets the underlying input channel. */ - inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } - - /* Consumes an input event from the input channel and copies its contents into - * an InputEvent object created using the specified factory. - * - * Tries to combine a series of move events into larger batches whenever possible. - * - * If consumeBatches is false, then defers consuming pending batched events if it - * is possible for additional samples to be added to them later. Call hasPendingBatch() - * to determine whether a pending batch is available to be consumed. - * - * If consumeBatches is true, then events are still batched but they are consumed - * immediately as soon as the input channel is exhausted. - * - * The frameTime parameter specifies the time when the current display frame started - * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. - * - * The returned sequence number is never 0 unless the operation failed. - * - * Returns OK on success. - * Returns WOULD_BLOCK if there is no event present. - * Returns DEAD_OBJECT if the channel's peer has been closed. - * Returns NO_MEMORY if the event could not be created. - * Other errors probably indicate that the channel is broken. - */ - status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, - uint32_t* outSeq, InputEvent** outEvent); - - /* Sends a finished signal to the publisher to inform it that the message - * with the specified sequence number has finished being process and whether - * the message was handled by the consumer. - * - * Returns OK on success. - * Returns BAD_VALUE if seq is 0. - * Other errors probably indicate that the channel is broken. - */ - status_t sendFinishedSignal(uint32_t seq, bool handled); - - status_t sendTimeline(int32_t inputEventId, - std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); - - /* Returns true if there is a pending batch. - * - * Should be called after calling consume() with consumeBatches == false to determine - * whether consume() should be called again later on with consumeBatches == true. - */ - bool hasPendingBatch() const; - - /* Returns the source of first pending batch if exist. - * - * Should be called after calling consume() with consumeBatches == false to determine - * whether consume() should be called again later on with consumeBatches == true. - */ - int32_t getPendingBatchSource() const; - - /* 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 dump() const; - -private: - // True if touch resampling is enabled. - const bool mResampleTouch; - - std::shared_ptr<InputChannel> mChannel; - - // The current input message. - InputMessage mMsg; - - // True if mMsg contains a valid input message that was deferred from the previous - // call to consume and that still needs to be handled. - bool mMsgDeferred; - - // Batched motion events per device and source. - struct Batch { - std::vector<InputMessage> samples; - }; - std::vector<Batch> mBatches; - - // Touch state per device and source, only for sources of class pointer. - struct History { - nsecs_t eventTime; - BitSet32 idBits; - int32_t idToIndex[MAX_POINTER_ID + 1]; - PointerCoords pointers[MAX_POINTERS]; - - void initializeFrom(const InputMessage& msg) { - eventTime = msg.body.motion.eventTime; - idBits.clear(); - for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { - uint32_t id = msg.body.motion.pointers[i].properties.id; - idBits.markBit(id); - idToIndex[id] = i; - pointers[i].copyFrom(msg.body.motion.pointers[i].coords); - } - } - - void initializeFrom(const History& other) { - eventTime = other.eventTime; - idBits = other.idBits; // temporary copy - for (size_t i = 0; i < other.idBits.count(); i++) { - uint32_t id = idBits.clearFirstMarkedBit(); - int32_t index = other.idToIndex[id]; - idToIndex[id] = index; - pointers[index].copyFrom(other.pointers[index]); - } - idBits = other.idBits; // final copy - } - - const PointerCoords& getPointerById(uint32_t id) const { - return pointers[idToIndex[id]]; - } - - bool hasPointerId(uint32_t id) const { - return idBits.hasBit(id); - } - }; - struct TouchState { - int32_t deviceId; - int32_t source; - size_t historyCurrent; - size_t historySize; - History history[2]; - History lastResample; - - void initialize(int32_t deviceId, int32_t source) { - this->deviceId = deviceId; - this->source = source; - historyCurrent = 0; - historySize = 0; - lastResample.eventTime = 0; - lastResample.idBits.clear(); - } - - void addHistory(const InputMessage& msg) { - historyCurrent ^= 1; - if (historySize < 2) { - historySize += 1; - } - history[historyCurrent].initializeFrom(msg); - } - - const History* getHistory(size_t index) const { - return &history[(historyCurrent + index) & 1]; - } - - bool recentCoordinatesAreIdentical(uint32_t id) const { - // Return true if the two most recently received "raw" coordinates are identical - if (historySize < 2) { - return false; - } - if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) { - return false; - } - float currentX = getHistory(0)->getPointerById(id).getX(); - float currentY = getHistory(0)->getPointerById(id).getY(); - float previousX = getHistory(1)->getPointerById(id).getX(); - float previousY = getHistory(1)->getPointerById(id).getY(); - if (currentX == previousX && currentY == previousY) { - return true; - } - return false; - } - }; - std::vector<TouchState> mTouchStates; - - // Chain of batched sequence numbers. When multiple input messages are combined into - // a batch, we append a record here that associates the last sequence number in the - // batch with the previous one. When the finished signal is sent, we traverse the - // chain to individually finish all input messages that were part of the batch. - struct SeqChain { - uint32_t seq; // sequence number of batched input message - uint32_t chain; // sequence number of previous batched input message - }; - std::vector<SeqChain> mSeqChains; - - // 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; - - status_t consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); - status_t consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); - - void updateTouchState(InputMessage& msg); - void resampleTouchState(nsecs_t frameTime, MotionEvent* event, - const InputMessage *next); - - ssize_t findBatch(int32_t deviceId, int32_t source) const; - ssize_t findTouchState(int32_t deviceId, int32_t source) const; - - nsecs_t getConsumeTime(uint32_t seq) const; - void popConsumeTime(uint32_t seq); - status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); - - static void rewriteMessage(TouchState& state, InputMessage& msg); - static bool canAddSample(const Batch& batch, const InputMessage* msg); - static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); - - static bool isTouchResamplingEnabled(); -}; - } // namespace android diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index de624e44f6..826e199093 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -164,47 +164,88 @@ LIBBINDER_NDK34 { # introduced=UpsideDownCake LIBBINDER_NDK35 { # introduced=VanillaIceCream global: APersistableBundle_readFromParcel; + APersistableBundle_readFromParcel; # llndk=202404 APersistableBundle_writeToParcel; + APersistableBundle_writeToParcel; # llndk=202404 APersistableBundle_new; + APersistableBundle_new; # llndk=202404 APersistableBundle_dup; + APersistableBundle_dup; # llndk=202404 APersistableBundle_delete; + APersistableBundle_delete; # llndk=202404 APersistableBundle_isEqual; + APersistableBundle_isEqual; # llndk=202404 APersistableBundle_size; + APersistableBundle_size; # llndk=202404 APersistableBundle_erase; + APersistableBundle_erase; # llndk=202404 APersistableBundle_putBoolean; + APersistableBundle_putBoolean; # llndk=202404 APersistableBundle_putInt; + APersistableBundle_putInt; # llndk=202404 APersistableBundle_putLong; + APersistableBundle_putLong; # llndk=202404 APersistableBundle_putDouble; + APersistableBundle_putDouble; # llndk=202404 APersistableBundle_putString; + APersistableBundle_putString; # llndk=202404 APersistableBundle_putBooleanVector; + APersistableBundle_putBooleanVector; # llndk=202404 APersistableBundle_putIntVector; + APersistableBundle_putIntVector; # llndk=202404 APersistableBundle_putLongVector; + APersistableBundle_putLongVector; # llndk=202404 APersistableBundle_putDoubleVector; + APersistableBundle_putDoubleVector; # llndk=202404 APersistableBundle_putStringVector; + APersistableBundle_putStringVector; # llndk=202404 APersistableBundle_putPersistableBundle; + APersistableBundle_putPersistableBundle; # llndk=202404 APersistableBundle_getBoolean; + APersistableBundle_getBoolean; # llndk=202404 APersistableBundle_getInt; + APersistableBundle_getInt; # llndk=202404 APersistableBundle_getLong; + APersistableBundle_getLong; # llndk=202404 APersistableBundle_getDouble; + APersistableBundle_getDouble; # llndk=202404 APersistableBundle_getString; + APersistableBundle_getString; # llndk=202404 APersistableBundle_getBooleanVector; + APersistableBundle_getBooleanVector; # llndk=202404 APersistableBundle_getIntVector; + APersistableBundle_getIntVector; # llndk=202404 APersistableBundle_getLongVector; + APersistableBundle_getLongVector; # llndk=202404 APersistableBundle_getDoubleVector; + APersistableBundle_getDoubleVector; # llndk=202404 APersistableBundle_getStringVector; + APersistableBundle_getStringVector; # llndk=202404 APersistableBundle_getPersistableBundle; + APersistableBundle_getPersistableBundle; # llndk=202404 APersistableBundle_getBooleanKeys; + APersistableBundle_getBooleanKeys; # llndk=202404 APersistableBundle_getIntKeys; + APersistableBundle_getIntKeys; # llndk=202404 APersistableBundle_getLongKeys; + APersistableBundle_getLongKeys; # llndk=202404 APersistableBundle_getDoubleKeys; + APersistableBundle_getDoubleKeys; # llndk=202404 APersistableBundle_getStringKeys; + APersistableBundle_getStringKeys; # llndk=202404 APersistableBundle_getBooleanVectorKeys; + APersistableBundle_getBooleanVectorKeys; # llndk=202404 APersistableBundle_getIntVectorKeys; + APersistableBundle_getIntVectorKeys; # llndk=202404 APersistableBundle_getLongVectorKeys; + APersistableBundle_getLongVectorKeys; # llndk=202404 APersistableBundle_getDoubleVectorKeys; + APersistableBundle_getDoubleVectorKeys; # llndk=202404 APersistableBundle_getStringVectorKeys; + APersistableBundle_getStringVectorKeys; # llndk=202404 APersistableBundle_getPersistableBundleKeys; - AServiceManager_openDeclaredPassthroughHal; # systemapi llndk + APersistableBundle_getPersistableBundleKeys; # llndk=202404 + AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404 }; LIBBINDER_NDK_PLATFORM { diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index a9d6e8d3bf..9791212e07 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -41,6 +41,7 @@ #include <android/os/IInputFlinger.h> #include <gui/WindowInfo.h> #include <input/Input.h> +#include <input/InputConsumer.h> #include <input/InputTransport.h> #include <ui/DisplayMode.h> diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6b8bc01c24..65e93a9a87 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -204,6 +204,7 @@ cc_library { srcs: [ "AccelerationCurve.cpp", "Input.cpp", + "InputConsumer.cpp", "InputDevice.cpp", "InputEventLabels.cpp", "InputTransport.cpp", diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp new file mode 100644 index 0000000000..e0d874ef76 --- /dev/null +++ b/libs/input/InputConsumer.cpp @@ -0,0 +1,939 @@ +/** + * 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 <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <math.h> +#include <poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <binder/Parcel.h> +#include <cutils/properties.h> +#include <ftl/enum.h> +#include <log/log.h> +#include <utils/Trace.h> + +#include <com_android_input_flags.h> +#include <input/InputConsumer.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); + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +/** + * Log debug messages about touch event resampling. + * + * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG". + * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately + * on debuggable builds (e.g. userdebug). + */ +bool debugResampling() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_TRANSPORT_RESAMPLING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", + ANDROID_LOG_INFO); + return DEBUG_TRANSPORT_RESAMPLING; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); +} + +void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) { + 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); +} + +void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) { + event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus); +} + +void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) { + event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled); +} + +void initializeDragEvent(DragEvent& event, const InputMessage& msg) { + event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y, + msg.body.drag.isExiting); +} + +void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i] = msg.body.motion.pointers[i].properties; + pointerCoords[i] = 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, pointerCoords); +} + +void addSample(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (uint32_t i = 0; i < pointerCount; i++) { + pointerCoords[i] = msg.body.motion.pointers[i].coords; + } + + event.setMetaState(event.getMetaState() | msg.body.motion.metaState); + event.addSample(msg.body.motion.eventTime, pointerCoords); +} + +void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) { + event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode); +} + +// Nanoseconds per milliseconds. +constexpr nsecs_t NANOS_PER_MS = 1000000; + +// Latency added during resampling. A few milliseconds doesn't hurt much but +// reduces the impact of mispredicted touch positions. +const std::chrono::duration RESAMPLE_LATENCY = 5ms; + +// Minimum time difference between consecutive samples before attempting to resample. +const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; + +// Maximum time difference between consecutive samples before attempting to resample +// by extrapolation. +const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; + +// Maximum time to predict forward from the last known state, to avoid predicting too +// far into the future. This time is further bounded by 50% of the last time delta. +const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; + +/** + * System property for enabling / disabling touch resampling. + * Resampling extrapolates / interpolates the reported touch event coordinates to better + * align them to the VSYNC signal, thus resulting in smoother scrolling performance. + * Resampling is not needed (and should be disabled) on hardware that already + * has touch events triggered by VSYNC. + * Set to "1" to enable resampling (default). + * Set to "0" to disable resampling. + * Resampling is enabled by default. + */ +const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; + +inline float lerp(float a, float b, float alpha) { + return a + alpha * (b - a); +} + +inline bool isPointerEvent(int32_t source) { + return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; +} + +bool shouldResampleTool(ToolType toolType) { + return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN; +} + +} // namespace + +using android::base::Result; +using android::base::StringPrintf; + +// --- InputConsumer --- + +InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel) + : InputConsumer(channel, isTouchResamplingEnabled()) {} + +InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel, + bool enableTouchResampling) + : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {} + +InputConsumer::~InputConsumer() {} + +bool InputConsumer::isTouchResamplingEnabled() { + return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); +} + +status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, + mChannel->getName().c_str(), toString(consumeBatches), frameTime); + + *outSeq = 0; + *outEvent = nullptr; + + // Fetch the next input message. + // Loop until an event can be returned or no additional events are received. + while (!*outEvent) { + if (mMsgDeferred) { + // mMsg contains a valid input message from the previous call to consume + // that has not yet been processed. + mMsgDeferred = false; + } else { + // Receive a fresh message. + status_t result = mChannel->receiveMessage(&mMsg); + if (result == OK) { + const auto [_, inserted] = + mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, + mMsg.header.seq); + + // Trace the event processing timeline - event was just read from the socket + ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq); + } + if (result) { + // Consume the next batched event unless batches are being held for later. + if (consumeBatches || result != WOULD_BLOCK) { + result = consumeBatch(factory, frameTime, outSeq, outEvent); + if (*outEvent) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + } + return result; + } + } + + switch (mMsg.header.type) { + case InputMessage::Type::KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; + + initializeKeyEvent(*keyEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = keyEvent; + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + + case InputMessage::Type::MOTION: { + ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches[batchIndex]; + if (canAddSample(batch, &mMsg)) { + batch.samples.push_back(mMsg); + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ appended to batch event", + mChannel->getName().c_str()); + break; + } else if (isPointerEvent(mMsg.body.motion.source) && + mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { + // No need to process events that we are going to cancel anyways + const size_t count = batch.samples.size(); + for (size_t i = 0; i < count; i++) { + const InputMessage& msg = batch.samples[i]; + sendFinishedSignal(msg.header.seq, false); + } + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); + mBatches.erase(mBatches.begin() + batchIndex); + } else { + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new message until later. + mMsgDeferred = true; + status_t result = consumeSamples(factory, batch, batch.samples.size(), + outSeq, outEvent); + mBatches.erase(mBatches.begin() + batchIndex); + if (result) { + return result; + } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + } + + // Start a new batch if needed. + if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || + mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + Batch batch; + batch.samples.push_back(mMsg); + mBatches.push_back(batch); + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ started batch event", + mChannel->getName().c_str()); + break; + } + + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; + + updateTouchState(mMsg); + initializeMotionEvent(*motionEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = motionEvent; + + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + + case InputMessage::Type::FINISHED: + case InputMessage::Type::TIMELINE: { + LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " + "InputConsumer!", + ftl::enum_string(mMsg.header.type).c_str()); + break; + } + + case InputMessage::Type::FOCUS: { + FocusEvent* focusEvent = factory->createFocusEvent(); + if (!focusEvent) return NO_MEMORY; + + initializeFocusEvent(*focusEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = focusEvent; + break; + } + + case InputMessage::Type::CAPTURE: { + CaptureEvent* captureEvent = factory->createCaptureEvent(); + if (!captureEvent) return NO_MEMORY; + + initializeCaptureEvent(*captureEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = captureEvent; + break; + } + + case InputMessage::Type::DRAG: { + DragEvent* dragEvent = factory->createDragEvent(); + if (!dragEvent) return NO_MEMORY; + + initializeDragEvent(*dragEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = dragEvent; + break; + } + + case InputMessage::Type::TOUCH_MODE: { + TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); + if (!touchModeEvent) return NO_MEMORY; + + initializeTouchModeEvent(*touchModeEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = touchModeEvent; + break; + } + } + } + return OK; +} + +status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent) { + status_t result; + for (size_t i = mBatches.size(); i > 0;) { + i--; + Batch& batch = mBatches[i]; + if (frameTime < 0) { + result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); + mBatches.erase(mBatches.begin() + i); + return result; + } + + nsecs_t sampleTime = frameTime; + if (mResampleTouch) { + sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count(); + } + ssize_t split = findSampleNoLaterThan(batch, sampleTime); + if (split < 0) { + continue; + } + + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); + const InputMessage* next; + if (batch.samples.empty()) { + mBatches.erase(mBatches.begin() + i); + next = nullptr; + } else { + next = &batch.samples[0]; + } + if (!result && mResampleTouch) { + resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); + } + return result; + } + + return WOULD_BLOCK; +} + +status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, Batch& batch, + size_t count, uint32_t* outSeq, InputEvent** outEvent) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; + + uint32_t chain = 0; + for (size_t i = 0; i < count; i++) { + InputMessage& msg = batch.samples[i]; + updateTouchState(msg); + if (i) { + SeqChain seqChain; + seqChain.seq = msg.header.seq; + seqChain.chain = chain; + mSeqChains.push_back(seqChain); + addSample(*motionEvent, msg); + } else { + initializeMotionEvent(*motionEvent, msg); + } + chain = msg.header.seq; + } + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); + + *outSeq = chain; + *outEvent = motionEvent; + return OK; +} + +void InputConsumer::updateTouchState(InputMessage& msg) { + if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) { + return; + } + + int32_t deviceId = msg.body.motion.deviceId; + int32_t source = msg.body.motion.source; + + // Update the touch state history to incorporate the new input message. + // If the message is in the past relative to the most recently produced resampled + // touch, then use the resampled time and coordinates instead. + switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index < 0) { + mTouchStates.push_back({}); + index = mTouchStates.size() - 1; + } + TouchState& touchState = mTouchStates[index]; + touchState.initialize(deviceId, source); + touchState.addHistory(msg); + break; + } + + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + touchState.addHistory(msg); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + rewriteMessage(touchState, msg); + touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); + } + break; + } + + case AMOTION_EVENT_ACTION_SCROLL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + rewriteMessage(touchState, msg); + mTouchStates.erase(mTouchStates.begin() + index); + } + break; + } + } +} + +/** + * Replace the coordinates in msg with the coordinates in lastResample, if necessary. + * + * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time + * is in the past relative to msg and the past two events do not contain identical coordinates), + * then invalidate the lastResample data for that pointer. + * If the two past events have identical coordinates, then lastResample data for that pointer will + * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is + * resampled to the new value x1, then x1 will always be used to replace x0 until some new value + * not equal to x0 is received. + */ +void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { + nsecs_t eventTime = msg.body.motion.eventTime; + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + uint32_t id = msg.body.motion.pointers[i].properties.id; + if (state.lastResample.idBits.hasBit(id)) { + if (eventTime < state.lastResample.eventTime || + state.recentCoordinatesAreIdentical(id)) { + PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; + const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); + ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, + resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(), + msgCoords.getY()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); + msgCoords.isResampled = true; + } else { + state.lastResample.idBits.clearBit(id); + } + } + } +} + +void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, + const InputMessage* next) { + if (!mResampleTouch || !(isPointerEvent(event->getSource())) || + event->getAction() != AMOTION_EVENT_ACTION_MOVE) { + return; + } + + ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); + if (index < 0) { + ALOGD_IF(debugResampling(), "Not resampled, no touch state for device."); + return; + } + + TouchState& touchState = mTouchStates[index]; + if (touchState.historySize < 1) { + ALOGD_IF(debugResampling(), "Not resampled, no history for device."); + return; + } + + // Ensure that the current sample has all of the pointers that need to be reported. + const History* current = touchState.getHistory(0); + size_t pointerCount = event->getPointerCount(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + if (!current->idBits.hasBit(id)) { + ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id); + return; + } + } + + // Find the data to use for resampling. + const History* other; + History future; + float alpha; + if (next) { + // Interpolate between current sample and future sample. + // So current->eventTime <= sampleTime <= future.eventTime. + future.initializeFrom(*next); + other = &future; + nsecs_t delta = future.eventTime - current->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", + delta); + return; + } + alpha = float(sampleTime - current->eventTime) / delta; + } else if (touchState.historySize >= 2) { + // Extrapolate future sample using current sample and past sample. + // So other->eventTime <= current->eventTime <= sampleTime. + other = touchState.getHistory(1); + nsecs_t delta = current->eventTime - other->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", + delta); + return; + } else if (delta > RESAMPLE_MAX_DELTA) { + ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.", + delta); + return; + } + nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION); + if (sampleTime > maxPredict) { + ALOGD_IF(debugResampling(), + "Sample time is too far in the future, adjusting prediction " + "from %" PRId64 " to %" PRId64 " ns.", + sampleTime - current->eventTime, maxPredict - current->eventTime); + sampleTime = maxPredict; + } + alpha = float(current->eventTime - sampleTime) / delta; + } else { + ALOGD_IF(debugResampling(), "Not resampled, insufficient data."); + return; + } + + if (current->eventTime == sampleTime) { + // Prevents having 2 events with identical times and coordinates. + return; + } + + // Resample touch coordinates. + History oldLastResample; + oldLastResample.initializeFrom(touchState.lastResample); + touchState.lastResample.eventTime = sampleTime; + touchState.lastResample.idBits.clear(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + touchState.lastResample.idToIndex[id] = i; + touchState.lastResample.idBits.markBit(id); + if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) { + // We maintain the previously resampled value for this pointer (stored in + // oldLastResample) when the coordinates for this pointer haven't changed since then. + // This way we don't introduce artificial jitter when pointers haven't actually moved. + // The isResampled flag isn't cleared as the values don't reflect what the device is + // actually reporting. + + // We know here that the coordinates for the pointer haven't changed because we + // would've cleared the resampled bit in rewriteMessage if they had. We can't modify + // lastResample in place because the mapping from pointer ID to index may have changed. + touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id); + continue; + } + + PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; + const PointerCoords& currentCoords = current->getPointerById(id); + resampledCoords = currentCoords; + resampledCoords.isResampled = true; + if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { + const PointerCoords& otherCoords = other->getPointerById(id); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, + lerp(currentCoords.getX(), otherCoords.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, + lerp(currentCoords.getY(), otherCoords.getY(), alpha)); + ALOGD_IF(debugResampling(), + "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); + } else { + ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, + resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY()); + } + } + + event->addSample(sampleTime, touchState.lastResample.pointers); +} + +status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().c_str(), seq, toString(handled)); + + if (!seq) { + ALOGE("Attempted to send a finished signal with sequence number 0."); + return BAD_VALUE; + } + + // Send finished signals for the batch sequence chain first. + size_t seqChainCount = mSeqChains.size(); + if (seqChainCount) { + uint32_t currentSeq = seq; + uint32_t chainSeqs[seqChainCount]; + size_t chainIndex = 0; + for (size_t i = seqChainCount; i > 0;) { + i--; + const SeqChain& seqChain = mSeqChains[i]; + if (seqChain.seq == currentSeq) { + currentSeq = seqChain.chain; + chainSeqs[chainIndex++] = currentSeq; + mSeqChains.erase(mSeqChains.begin() + i); + } + } + status_t status = OK; + while (!status && chainIndex > 0) { + chainIndex--; + status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); + } + if (status) { + // An error occurred so at least one signal was not sent, reconstruct the chain. + for (;;) { + SeqChain seqChain; + seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; + seqChain.chain = chainSeqs[chainIndex]; + mSeqChains.push_back(seqChain); + if (!chainIndex) break; + chainIndex--; + } + return status; + } + } + + // Send finished signal for the last message in the batch. + return sendUnchainedFinishedSignal(seq, handled); +} + +status_t InputConsumer::sendTimeline(int32_t inputEventId, + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, + mChannel->getName().c_str(), inputEventId, + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], + graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); + + InputMessage msg; + msg.header.type = InputMessage::Type::TIMELINE; + msg.header.seq = 0; + msg.body.timeline.eventId = inputEventId; + msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline); + return mChannel->sendMessage(&msg); +} + +nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { + 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); + return it->second; +} + +void InputConsumer::popConsumeTime(uint32_t seq) { + mConsumeTimes.erase(seq); +} + +status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { + InputMessage msg; + msg.header.type = InputMessage::Type::FINISHED; + msg.header.seq = seq; + msg.body.finished.handled = handled; + msg.body.finished.consumeTime = getConsumeTime(seq); + status_t result = mChannel->sendMessage(&msg); + if (result == OK) { + // Remove the consume time if the socket write succeeded. We will not need to ack this + // message anymore. If the socket write did not succeed, we will try again and will still + // need consume time. + popConsumeTime(seq); + + // Trace the event processing timeline - event was just finished + ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq); + } + return result; +} + +bool InputConsumer::hasPendingBatch() const { + return !mBatches.empty(); +} + +int32_t InputConsumer::getPendingBatchSource() const { + if (mBatches.empty()) { + return AINPUT_SOURCE_CLASS_NONE; + } + + const Batch& batch = mBatches[0]; + const InputMessage& head = batch.samples[0]; + return head.body.motion.source; +} + +bool InputConsumer::probablyHasInput() const { + return hasPendingBatch() || mChannel->probablyHasInput(); +} + +ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mBatches.size(); i++) { + const Batch& batch = mBatches[i]; + const InputMessage& head = batch.samples[0]; + if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { + return i; + } + } + return -1; +} + +ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mTouchStates.size(); i++) { + const TouchState& touchState = mTouchStates[i]; + if (touchState.deviceId == deviceId && touchState.source == source) { + return i; + } + } + return -1; +} + +bool InputConsumer::canAddSample(const Batch& batch, const InputMessage* msg) { + const InputMessage& head = batch.samples[0]; + uint32_t pointerCount = msg->body.motion.pointerCount; + if (head.body.motion.pointerCount != pointerCount || + head.body.motion.action != msg->body.motion.action) { + return false; + } + for (size_t i = 0; i < pointerCount; i++) { + if (head.body.motion.pointers[i].properties != msg->body.motion.pointers[i].properties) { + return false; + } + } + return true; +} + +ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { + size_t numSamples = batch.samples.size(); + size_t index = 0; + while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) { + index += 1; + } + return ssize_t(index) - 1; +} + +std::string InputConsumer::dump() const { + std::string out; + out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n"; + out = out + "mChannel = " + mChannel->getName() + "\n"; + out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; + if (mMsgDeferred) { + out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n"; + } + out += "Batches:\n"; + for (const Batch& batch : mBatches) { + out += " Batch:\n"; + for (const InputMessage& msg : batch.samples) { + out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, + ftl::enum_string(msg.header.type).c_str()); + switch (msg.header.type) { + case InputMessage::Type::KEY: { + out += android::base::StringPrintf("action=%s keycode=%" PRId32, + KeyEvent::actionToString( + msg.body.key.action), + msg.body.key.keyCode); + break; + } + case InputMessage::Type::MOTION: { + out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action); + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + const float x = msg.body.motion.pointers[i].coords.getX(); + const float y = msg.body.motion.pointers[i].coords.getY(); + out += android::base::StringPrintf("\n Pointer %" PRIu32 + " : x=%.1f y=%.1f", + i, x, y); + } + break; + } + case InputMessage::Type::FINISHED: { + out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64, + toString(msg.body.finished.handled), + msg.body.finished.consumeTime); + break; + } + case InputMessage::Type::FOCUS: { + out += android::base::StringPrintf("hasFocus=%s", + toString(msg.body.focus.hasFocus)); + break; + } + case InputMessage::Type::CAPTURE: { + out += android::base::StringPrintf("hasCapture=%s", + toString(msg.body.capture + .pointerCaptureEnabled)); + break; + } + case InputMessage::Type::DRAG: { + out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s", + msg.body.drag.x, msg.body.drag.y, + toString(msg.body.drag.isExiting)); + break; + } + case InputMessage::Type::TIMELINE: { + const nsecs_t gpuCompletedTime = + msg.body.timeline + .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + out += android::base::StringPrintf("inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 + ", presentTime=%" PRId64, + msg.body.timeline.eventId, gpuCompletedTime, + presentTime); + break; + } + case InputMessage::Type::TOUCH_MODE: { + out += android::base::StringPrintf("isInTouchMode=%s", + toString(msg.body.touchMode.isInTouchMode)); + break; + } + } + out += "\n"; + } + } + if (mBatches.empty()) { + out += " <empty>\n"; + } + out += "mSeqChains:\n"; + for (const SeqChain& chain : mSeqChains) { + out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq, + chain.chain); + } + if (mSeqChains.empty()) { + out += " <empty>\n"; + } + out += "mConsumeTimes:\n"; + for (const auto& [seq, consumeTime] : mConsumeTimes) { + out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq, + consumeTime); + } + if (mConsumeTimes.empty()) { + out += " <empty>\n"; + } + return out; +} + +} // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index b3a36ebf5a..1869483474 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -51,14 +51,6 @@ const bool DEBUG_CHANNEL_MESSAGES = const bool DEBUG_CHANNEL_LIFECYCLE = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Lifecycle", ANDROID_LOG_INFO); -/** - * 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); - const bool IS_DEBUGGABLE_BUILD = #if defined(__ANDROID__) android::base::GetBoolProperty("ro.debuggable", false); @@ -81,23 +73,6 @@ bool debugTransportPublisher() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO); } -/** - * Log debug messages about touch event resampling. - * - * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG". - * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately - * on debuggable builds (e.g. userdebug). - */ -bool debugResampling() { - if (!IS_DEBUGGABLE_BUILD) { - static const bool DEBUG_TRANSPORT_RESAMPLING = - __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", - ANDROID_LOG_INFO); - return DEBUG_TRANSPORT_RESAMPLING; - } - return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); -} - android::base::unique_fd dupChannelFd(int fd) { android::base::unique_fd newFd(::dup(fd)); if (!newFd.ok()) { @@ -113,103 +88,11 @@ android::base::unique_fd dupChannelFd(int fd) { return newFd; } -void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) { - 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); -} - -void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) { - event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus); -} - -void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) { - event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled); -} - -void initializeDragEvent(DragEvent& event, const InputMessage& msg) { - event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y, - msg.body.drag.isExiting); -} - -void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) { - uint32_t pointerCount = msg.body.motion.pointerCount; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i] = msg.body.motion.pointers[i].properties; - pointerCoords[i] = 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, pointerCoords); -} - -void addSample(MotionEvent& event, const InputMessage& msg) { - uint32_t pointerCount = msg.body.motion.pointerCount; - PointerCoords pointerCoords[pointerCount]; - for (uint32_t i = 0; i < pointerCount; i++) { - pointerCoords[i] = msg.body.motion.pointers[i].coords; - } - - event.setMetaState(event.getMetaState() | msg.body.motion.metaState); - event.addSample(msg.body.motion.eventTime, pointerCoords); -} - -void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) { - event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode); -} - // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. It just needs to be big enough to hold // a few dozen large multi-finger motion events in the case where an application gets // behind processing touches. -static constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024; - -// Nanoseconds per milliseconds. -static constexpr nsecs_t NANOS_PER_MS = 1000000; - -// Latency added during resampling. A few milliseconds doesn't hurt much but -// reduces the impact of mispredicted touch positions. -const std::chrono::duration RESAMPLE_LATENCY = 5ms; - -// Minimum time difference between consecutive samples before attempting to resample. -static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; - -// Maximum time difference between consecutive samples before attempting to resample -// by extrapolation. -static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; - -// Maximum time to predict forward from the last known state, to avoid predicting too -// far into the future. This time is further bounded by 50% of the last time delta. -static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; - -/** - * System property for enabling / disabling touch resampling. - * Resampling extrapolates / interpolates the reported touch event coordinates to better - * align them to the VSYNC signal, thus resulting in smoother scrolling performance. - * Resampling is not needed (and should be disabled) on hardware that already - * has touch events triggered by VSYNC. - * Set to "1" to enable resampling (default). - * Set to "0" to disable resampling. - * Resampling is enabled by default. - */ -static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; +constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024; /** * Crash if the events that are getting sent to the InputPublisher are inconsistent. @@ -220,18 +103,6 @@ bool verifyEvents() { __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO); } -inline float lerp(float a, float b, float alpha) { - return a + alpha * (b - a); -} - -inline bool isPointerEvent(int32_t source) { - return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; -} - -bool shouldResampleTool(ToolType toolType) { - return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN; -} - } // namespace using android::base::Result; @@ -892,756 +763,4 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC return android::base::Error(UNKNOWN_ERROR); } -// --- InputConsumer --- - -InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel) - : InputConsumer(channel, isTouchResamplingEnabled()) {} - -InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel, - bool enableTouchResampling) - : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {} - -InputConsumer::~InputConsumer() { -} - -bool InputConsumer::isTouchResamplingEnabled() { - return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); -} - -status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, - mChannel->getName().c_str(), toString(consumeBatches), frameTime); - - *outSeq = 0; - *outEvent = nullptr; - - // Fetch the next input message. - // Loop until an event can be returned or no additional events are received. - while (!*outEvent) { - if (mMsgDeferred) { - // mMsg contains a valid input message from the previous call to consume - // that has not yet been processed. - mMsgDeferred = false; - } else { - // Receive a fresh message. - status_t result = mChannel->receiveMessage(&mMsg); - if (result == OK) { - const auto [_, inserted] = - mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); - LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, - mMsg.header.seq); - - // Trace the event processing timeline - event was just read from the socket - ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq); - } - if (result) { - // Consume the next batched event unless batches are being held for later. - if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent); - if (*outEvent) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed batch event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - } - return result; - } - } - - switch (mMsg.header.type) { - case InputMessage::Type::KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (!keyEvent) return NO_MEMORY; - - initializeKeyEvent(*keyEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = keyEvent; - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed key event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - - case InputMessage::Type::MOTION: { - ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); - if (batchIndex >= 0) { - Batch& batch = mBatches[batchIndex]; - if (canAddSample(batch, &mMsg)) { - batch.samples.push_back(mMsg); - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ appended to batch event", - mChannel->getName().c_str()); - break; - } else if (isPointerEvent(mMsg.body.motion.source) && - mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { - // No need to process events that we are going to cancel anyways - const size_t count = batch.samples.size(); - for (size_t i = 0; i < count; i++) { - const InputMessage& msg = batch.samples[i]; - sendFinishedSignal(msg.header.seq, false); - } - batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); - mBatches.erase(mBatches.begin() + batchIndex); - } else { - // We cannot append to the batch in progress, so we need to consume - // the previous batch right now and defer the new message until later. - mMsgDeferred = true; - status_t result = consumeSamples(factory, batch, batch.samples.size(), - outSeq, outEvent); - mBatches.erase(mBatches.begin() + batchIndex); - if (result) { - return result; - } - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed batch event and " - "deferred current event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - } - - // Start a new batch if needed. - if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || - mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - Batch batch; - batch.samples.push_back(mMsg); - mBatches.push_back(batch); - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ started batch event", - mChannel->getName().c_str()); - break; - } - - MotionEvent* motionEvent = factory->createMotionEvent(); - if (!motionEvent) return NO_MEMORY; - - updateTouchState(mMsg); - initializeMotionEvent(*motionEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = motionEvent; - - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - - case InputMessage::Type::FINISHED: - case InputMessage::Type::TIMELINE: { - LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " - "InputConsumer!", - ftl::enum_string(mMsg.header.type).c_str()); - break; - } - - case InputMessage::Type::FOCUS: { - FocusEvent* focusEvent = factory->createFocusEvent(); - if (!focusEvent) return NO_MEMORY; - - initializeFocusEvent(*focusEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = focusEvent; - break; - } - - case InputMessage::Type::CAPTURE: { - CaptureEvent* captureEvent = factory->createCaptureEvent(); - if (!captureEvent) return NO_MEMORY; - - initializeCaptureEvent(*captureEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = captureEvent; - break; - } - - case InputMessage::Type::DRAG: { - DragEvent* dragEvent = factory->createDragEvent(); - if (!dragEvent) return NO_MEMORY; - - initializeDragEvent(*dragEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = dragEvent; - break; - } - - case InputMessage::Type::TOUCH_MODE: { - TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); - if (!touchModeEvent) return NO_MEMORY; - - initializeTouchModeEvent(*touchModeEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = touchModeEvent; - break; - } - } - } - return OK; -} - -status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { - status_t result; - for (size_t i = mBatches.size(); i > 0; ) { - i--; - Batch& batch = mBatches[i]; - if (frameTime < 0) { - result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); - mBatches.erase(mBatches.begin() + i); - return result; - } - - nsecs_t sampleTime = frameTime; - if (mResampleTouch) { - sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count(); - } - ssize_t split = findSampleNoLaterThan(batch, sampleTime); - if (split < 0) { - continue; - } - - result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); - const InputMessage* next; - if (batch.samples.empty()) { - mBatches.erase(mBatches.begin() + i); - next = nullptr; - } else { - next = &batch.samples[0]; - } - if (!result && mResampleTouch) { - resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); - } - return result; - } - - return WOULD_BLOCK; -} - -status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - - uint32_t chain = 0; - for (size_t i = 0; i < count; i++) { - InputMessage& msg = batch.samples[i]; - updateTouchState(msg); - if (i) { - SeqChain seqChain; - seqChain.seq = msg.header.seq; - seqChain.chain = chain; - mSeqChains.push_back(seqChain); - addSample(*motionEvent, msg); - } else { - initializeMotionEvent(*motionEvent, msg); - } - chain = msg.header.seq; - } - batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); - - *outSeq = chain; - *outEvent = motionEvent; - return OK; -} - -void InputConsumer::updateTouchState(InputMessage& msg) { - if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) { - return; - } - - int32_t deviceId = msg.body.motion.deviceId; - int32_t source = msg.body.motion.source; - - // Update the touch state history to incorporate the new input message. - // If the message is in the past relative to the most recently produced resampled - // touch, then use the resampled time and coordinates instead. - switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: { - ssize_t index = findTouchState(deviceId, source); - if (index < 0) { - mTouchStates.push_back({}); - index = mTouchStates.size() - 1; - } - TouchState& touchState = mTouchStates[index]; - touchState.initialize(deviceId, source); - touchState.addHistory(msg); - break; - } - - case AMOTION_EVENT_ACTION_MOVE: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - touchState.addHistory(msg); - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_POINTER_UP: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - rewriteMessage(touchState, msg); - touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); - } - break; - } - - case AMOTION_EVENT_ACTION_SCROLL: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - rewriteMessage(touchState, msg); - mTouchStates.erase(mTouchStates.begin() + index); - } - break; - } - } -} - -/** - * Replace the coordinates in msg with the coordinates in lastResample, if necessary. - * - * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time - * is in the past relative to msg and the past two events do not contain identical coordinates), - * then invalidate the lastResample data for that pointer. - * If the two past events have identical coordinates, then lastResample data for that pointer will - * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is - * resampled to the new value x1, then x1 will always be used to replace x0 until some new value - * not equal to x0 is received. - */ -void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { - nsecs_t eventTime = msg.body.motion.eventTime; - for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { - uint32_t id = msg.body.motion.pointers[i].properties.id; - if (state.lastResample.idBits.hasBit(id)) { - if (eventTime < state.lastResample.eventTime || - state.recentCoordinatesAreIdentical(id)) { - PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; - const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); - ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, - resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(), - msgCoords.getY()); - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); - msgCoords.isResampled = true; - } else { - state.lastResample.idBits.clearBit(id); - } - } - } -} - -void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, - const InputMessage* next) { - if (!mResampleTouch - || !(isPointerEvent(event->getSource())) - || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { - return; - } - - ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); - if (index < 0) { - ALOGD_IF(debugResampling(), "Not resampled, no touch state for device."); - return; - } - - TouchState& touchState = mTouchStates[index]; - if (touchState.historySize < 1) { - ALOGD_IF(debugResampling(), "Not resampled, no history for device."); - return; - } - - // Ensure that the current sample has all of the pointers that need to be reported. - const History* current = touchState.getHistory(0); - size_t pointerCount = event->getPointerCount(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t id = event->getPointerId(i); - if (!current->idBits.hasBit(id)) { - ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id); - return; - } - } - - // Find the data to use for resampling. - const History* other; - History future; - float alpha; - if (next) { - // Interpolate between current sample and future sample. - // So current->eventTime <= sampleTime <= future.eventTime. - future.initializeFrom(*next); - other = &future; - nsecs_t delta = future.eventTime - current->eventTime; - if (delta < RESAMPLE_MIN_DELTA) { - ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", - delta); - return; - } - alpha = float(sampleTime - current->eventTime) / delta; - } else if (touchState.historySize >= 2) { - // Extrapolate future sample using current sample and past sample. - // So other->eventTime <= current->eventTime <= sampleTime. - other = touchState.getHistory(1); - nsecs_t delta = current->eventTime - other->eventTime; - if (delta < RESAMPLE_MIN_DELTA) { - ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", - delta); - return; - } else if (delta > RESAMPLE_MAX_DELTA) { - ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.", - delta); - return; - } - nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION); - if (sampleTime > maxPredict) { - ALOGD_IF(debugResampling(), - "Sample time is too far in the future, adjusting prediction " - "from %" PRId64 " to %" PRId64 " ns.", - sampleTime - current->eventTime, maxPredict - current->eventTime); - sampleTime = maxPredict; - } - alpha = float(current->eventTime - sampleTime) / delta; - } else { - ALOGD_IF(debugResampling(), "Not resampled, insufficient data."); - return; - } - - if (current->eventTime == sampleTime) { - // Prevents having 2 events with identical times and coordinates. - return; - } - - // Resample touch coordinates. - History oldLastResample; - oldLastResample.initializeFrom(touchState.lastResample); - touchState.lastResample.eventTime = sampleTime; - touchState.lastResample.idBits.clear(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t id = event->getPointerId(i); - touchState.lastResample.idToIndex[id] = i; - touchState.lastResample.idBits.markBit(id); - if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) { - // We maintain the previously resampled value for this pointer (stored in - // oldLastResample) when the coordinates for this pointer haven't changed since then. - // This way we don't introduce artificial jitter when pointers haven't actually moved. - // The isResampled flag isn't cleared as the values don't reflect what the device is - // actually reporting. - - // We know here that the coordinates for the pointer haven't changed because we - // would've cleared the resampled bit in rewriteMessage if they had. We can't modify - // lastResample in place becasue the mapping from pointer ID to index may have changed. - touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id); - continue; - } - - PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; - const PointerCoords& currentCoords = current->getPointerById(id); - resampledCoords = currentCoords; - resampledCoords.isResampled = true; - if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { - const PointerCoords& otherCoords = other->getPointerById(id); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, - lerp(currentCoords.getX(), otherCoords.getX(), alpha)); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, - lerp(currentCoords.getY(), otherCoords.getY(), alpha)); - ALOGD_IF(debugResampling(), - "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " - "other (%0.3f, %0.3f), alpha %0.3f", - id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), - currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); - } else { - ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, - resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), - currentCoords.getY()); - } - } - - event->addSample(sampleTime, touchState.lastResample.pointers); -} - -status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", - mChannel->getName().c_str(), seq, toString(handled)); - - if (!seq) { - ALOGE("Attempted to send a finished signal with sequence number 0."); - return BAD_VALUE; - } - - // Send finished signals for the batch sequence chain first. - size_t seqChainCount = mSeqChains.size(); - if (seqChainCount) { - uint32_t currentSeq = seq; - uint32_t chainSeqs[seqChainCount]; - size_t chainIndex = 0; - for (size_t i = seqChainCount; i > 0; ) { - i--; - const SeqChain& seqChain = mSeqChains[i]; - if (seqChain.seq == currentSeq) { - currentSeq = seqChain.chain; - chainSeqs[chainIndex++] = currentSeq; - mSeqChains.erase(mSeqChains.begin() + i); - } - } - status_t status = OK; - while (!status && chainIndex > 0) { - chainIndex--; - status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); - } - if (status) { - // An error occurred so at least one signal was not sent, reconstruct the chain. - for (;;) { - SeqChain seqChain; - seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; - seqChain.chain = chainSeqs[chainIndex]; - mSeqChains.push_back(seqChain); - if (!chainIndex) break; - chainIndex--; - } - return status; - } - } - - // Send finished signal for the last message in the batch. - return sendUnchainedFinishedSignal(seq, handled); -} - -status_t InputConsumer::sendTimeline(int32_t inputEventId, - std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 - ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, - mChannel->getName().c_str(), inputEventId, - graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], - graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); - - InputMessage msg; - msg.header.type = InputMessage::Type::TIMELINE; - msg.header.seq = 0; - msg.body.timeline.eventId = inputEventId; - msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline); - return mChannel->sendMessage(&msg); -} - -nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { - 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); - return it->second; -} - -void InputConsumer::popConsumeTime(uint32_t seq) { - mConsumeTimes.erase(seq); -} - -status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { - InputMessage msg; - msg.header.type = InputMessage::Type::FINISHED; - msg.header.seq = seq; - msg.body.finished.handled = handled; - msg.body.finished.consumeTime = getConsumeTime(seq); - status_t result = mChannel->sendMessage(&msg); - if (result == OK) { - // Remove the consume time if the socket write succeeded. We will not need to ack this - // message anymore. If the socket write did not succeed, we will try again and will still - // need consume time. - popConsumeTime(seq); - - // Trace the event processing timeline - event was just finished - ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq); - } - return result; -} - -bool InputConsumer::hasPendingBatch() const { - return !mBatches.empty(); -} - -int32_t InputConsumer::getPendingBatchSource() const { - if (mBatches.empty()) { - return AINPUT_SOURCE_CLASS_NONE; - } - - const Batch& batch = mBatches[0]; - const InputMessage& head = batch.samples[0]; - return head.body.motion.source; -} - -bool InputConsumer::probablyHasInput() const { - return hasPendingBatch() || mChannel->probablyHasInput(); -} - -ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { - for (size_t i = 0; i < mBatches.size(); i++) { - const Batch& batch = mBatches[i]; - const InputMessage& head = batch.samples[0]; - if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { - return i; - } - } - return -1; -} - -ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { - for (size_t i = 0; i < mTouchStates.size(); i++) { - const TouchState& touchState = mTouchStates[i]; - if (touchState.deviceId == deviceId && touchState.source == source) { - return i; - } - } - return -1; -} - -bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { - const InputMessage& head = batch.samples[0]; - uint32_t pointerCount = msg->body.motion.pointerCount; - if (head.body.motion.pointerCount != pointerCount - || head.body.motion.action != msg->body.motion.action) { - return false; - } - for (size_t i = 0; i < pointerCount; i++) { - if (head.body.motion.pointers[i].properties - != msg->body.motion.pointers[i].properties) { - return false; - } - } - return true; -} - -ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { - size_t numSamples = batch.samples.size(); - size_t index = 0; - while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) { - index += 1; - } - return ssize_t(index) - 1; -} - -std::string InputConsumer::dump() const { - std::string out; - out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n"; - out = out + "mChannel = " + mChannel->getName() + "\n"; - out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; - if (mMsgDeferred) { - out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n"; - } - out += "Batches:\n"; - for (const Batch& batch : mBatches) { - out += " Batch:\n"; - for (const InputMessage& msg : batch.samples) { - out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, - ftl::enum_string(msg.header.type).c_str()); - switch (msg.header.type) { - case InputMessage::Type::KEY: { - out += android::base::StringPrintf("action=%s keycode=%" PRId32, - KeyEvent::actionToString( - msg.body.key.action), - msg.body.key.keyCode); - break; - } - case InputMessage::Type::MOTION: { - out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action); - for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { - const float x = msg.body.motion.pointers[i].coords.getX(); - const float y = msg.body.motion.pointers[i].coords.getY(); - out += android::base::StringPrintf("\n Pointer %" PRIu32 - " : x=%.1f y=%.1f", - i, x, y); - } - break; - } - case InputMessage::Type::FINISHED: { - out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64, - toString(msg.body.finished.handled), - msg.body.finished.consumeTime); - break; - } - case InputMessage::Type::FOCUS: { - out += android::base::StringPrintf("hasFocus=%s", - toString(msg.body.focus.hasFocus)); - break; - } - case InputMessage::Type::CAPTURE: { - out += android::base::StringPrintf("hasCapture=%s", - toString(msg.body.capture - .pointerCaptureEnabled)); - break; - } - case InputMessage::Type::DRAG: { - out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s", - msg.body.drag.x, msg.body.drag.y, - toString(msg.body.drag.isExiting)); - break; - } - case InputMessage::Type::TIMELINE: { - const nsecs_t gpuCompletedTime = - msg.body.timeline - .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; - const nsecs_t presentTime = - msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; - out += android::base::StringPrintf("inputEventId=%" PRId32 - ", gpuCompletedTime=%" PRId64 - ", presentTime=%" PRId64, - msg.body.timeline.eventId, gpuCompletedTime, - presentTime); - break; - } - case InputMessage::Type::TOUCH_MODE: { - out += android::base::StringPrintf("isInTouchMode=%s", - toString(msg.body.touchMode.isInTouchMode)); - break; - } - } - out += "\n"; - } - } - if (mBatches.empty()) { - out += " <empty>\n"; - } - out += "mSeqChains:\n"; - for (const SeqChain& chain : mSeqChains) { - out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq, - chain.chain); - } - if (mSeqChains.empty()) { - out += " <empty>\n"; - } - out += "mConsumeTimes:\n"; - for (const auto& [seq, consumeTime] : mConsumeTimes) { - out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq, - consumeTime); - } - if (mConsumeTimes.empty()) { - out += " <empty>\n"; - } - return out; -} - } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index b5fab496e2..332831febb 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -17,6 +17,7 @@ #include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> #include <gui/constants.h> +#include <input/InputConsumer.h> #include <input/InputTransport.h> using android::base::Result; diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp index 0b0bb63a81..6e23d4e910 100644 --- a/libs/input/tests/TouchResampling_test.cpp +++ b/libs/input/tests/TouchResampling_test.cpp @@ -19,6 +19,7 @@ #include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> +#include <input/InputConsumer.h> #include <input/InputTransport.h> using namespace std::chrono_literals; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 09d7cb5d3f..39902b1635 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -88,6 +88,7 @@ filegroup { "skia/SkiaGLRenderEngine.cpp", "skia/SkiaVkRenderEngine.cpp", "skia/VulkanInterface.cpp", + "skia/compat/GaneshGpuContext.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index ee95e59d90..02e7337053 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -21,12 +21,12 @@ #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 <android/hardware_buffer.h> #include "ColorSpaces.h" #include "log/log_main.h" #include "utils/Trace.h" @@ -35,47 +35,37 @@ namespace android { namespace renderengine { namespace skia { -AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, +AutoBackendTexture::AutoBackendTexture(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, CleanupManager& cleanupMgr) - : mCleanupMgr(cleanupMgr), mIsOutputBuffer(isOutputBuffer) { + : 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 = context->backend(); + GrBackendApi backend = mGrContext->backend(); if (backend == GrBackendApi::kOpenGL) { backendFormat = - GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false); + GrAHardwareBufferUtils::GetGLBackendFormat(mGrContext.get(), desc.format, false); mBackendTexture = - GrAHardwareBufferUtils::MakeGLBackendTexture(context, - buffer, - desc.width, - desc.height, - &mDeleteProc, - &mUpdateProc, - &mImageCtx, - createProtectedImage, - backendFormat, + GrAHardwareBufferUtils::MakeGLBackendTexture(mGrContext.get(), buffer, desc.width, + desc.height, &mDeleteProc, + &mUpdateProc, &mImageCtx, + createProtectedImage, backendFormat, isOutputBuffer); } else if (backend == GrBackendApi::kVulkan) { - backendFormat = - GrAHardwareBufferUtils::GetVulkanBackendFormat(context, - buffer, - desc.format, - false); + backendFormat = GrAHardwareBufferUtils::GetVulkanBackendFormat(mGrContext.get(), buffer, + desc.format, false); mBackendTexture = - GrAHardwareBufferUtils::MakeVulkanBackendTexture(context, - buffer, - desc.width, - desc.height, - &mDeleteProc, - &mUpdateProc, - &mImageCtx, - createProtectedImage, - backendFormat, - isOutputBuffer); + 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)); } @@ -158,12 +148,11 @@ void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace } } -sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType, - GrDirectContext* context) { +sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) { ATRACE_CALL(); if (mBackendTexture.isValid()) { - mUpdateProc(mImageCtx, context); + mUpdateProc(mImageCtx, mGrContext.get()); } auto colorType = mColorType; @@ -174,7 +163,7 @@ sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaTyp } sk_sp<SkImage> image = - SkImages::BorrowTextureFrom(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, + SkImages::BorrowTextureFrom(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, alphaType, toSkColorSpace(dataspace), releaseImageProc, this); if (image.get()) { @@ -190,13 +179,12 @@ sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaTyp return mImage; } -sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace, - GrDirectContext* context) { +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"); if (!mSurface.get() || mDataspace != dataspace) { sk_sp<SkSurface> surface = - SkSurfaces::WrapBackendTexture(context, mBackendTexture, + SkSurfaces::WrapBackendTexture(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin, 0, mColorType, toSkColorSpace(dataspace), nullptr, releaseSurfaceProc, this); diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h index 509ac40f77..1d5b5655db 100644 --- a/libs/renderengine/skia/AutoBackendTexture.h +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -24,6 +24,7 @@ #include <ui/GraphicTypes.h> #include "android-base/macros.h" +#include "compat/SkiaGpuContext.h" #include <mutex> #include <vector> @@ -80,7 +81,7 @@ public: // of shared ownership with Skia objects, so we wrap it here instead. class LocalRef { public: - LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, + LocalRef(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, CleanupManager& cleanupMgr) { mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr); mTexture->ref(); @@ -95,14 +96,13 @@ public: // Makes a new SkImage from the texture content. // As SkImages are immutable but buffer content is not, we create // a new SkImage every time. - sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType, - GrDirectContext* context) { - return mTexture->makeImage(dataspace, alphaType, context); + sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) { + return mTexture->makeImage(dataspace, alphaType); } // Makes a new SkSurface from the texture content, if needed. - sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context) { - return mTexture->getOrCreateSurface(dataspace, context); + sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace) { + return mTexture->getOrCreateSurface(dataspace); } SkColorType colorType() const { return mTexture->mColorType; } @@ -114,8 +114,10 @@ public: }; private: + DISALLOW_COPY_AND_ASSIGN(AutoBackendTexture); + // Creates a GrBackendTexture whose contents come from the provided buffer. - AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, + AutoBackendTexture(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer, CleanupManager& cleanupMgr); // The only way to invoke dtor is with unref, when mUsageCount is 0. @@ -130,24 +132,24 @@ private: // Makes a new SkImage from the texture content. // As SkImages are immutable but buffer content is not, we create // a new SkImage every time. - sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType, - GrDirectContext* context); + sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType); // Makes a new SkSurface from the texture content, if needed. - sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context); + 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); int mUsageCount = 0; - const bool mIsOutputBuffer; sk_sp<SkImage> mImage = nullptr; sk_sp<SkSurface> mSurface = nullptr; diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index fea4129ec0..f10c98d6e2 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -21,6 +21,8 @@ #include "SkiaGLRenderEngine.h" +#include "compat/SkiaGpuContext.h" + #include <EGL/egl.h> #include <EGL/eglext.h> #include <GrContextOptions.h> @@ -207,7 +209,7 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt, placeholder, protectedContext, protectedPlaceholder)); - engine->ensureGrContextsCreated(); + engine->ensureContextsCreated(); ALOGI("OpenGL ES informations:"); ALOGI("vendor : %s", extensions.getVendor()); @@ -295,9 +297,7 @@ SkiaGLRenderEngine::~SkiaGLRenderEngine() { eglReleaseThread(); } -SkiaRenderEngine::Contexts SkiaGLRenderEngine::createDirectContexts( - const GrContextOptions& options) { - +SkiaRenderEngine::Contexts SkiaGLRenderEngine::createContexts() { LOG_ALWAYS_FATAL_IF(isProtected(), "Cannot setup contexts while already in protected mode"); @@ -306,10 +306,10 @@ SkiaRenderEngine::Contexts SkiaGLRenderEngine::createDirectContexts( LOG_ALWAYS_FATAL_IF(!glInterface.get(), "GrGLMakeNativeInterface() failed"); SkiaRenderEngine::Contexts contexts; - contexts.first = GrDirectContexts::MakeGL(glInterface, options); + contexts.first = SkiaGpuContext::MakeGL_Ganesh(glInterface, mSkSLCacheMonitor); if (supportsProtectedContentImpl()) { useProtectedContextImpl(GrProtected::kYes); - contexts.second = GrDirectContexts::MakeGL(glInterface, options); + contexts.second = SkiaGpuContext::MakeGL_Ganesh(glInterface, mSkSLCacheMonitor); useProtectedContextImpl(GrProtected::kNo); } @@ -330,14 +330,14 @@ bool SkiaGLRenderEngine::useProtectedContextImpl(GrProtected isProtected) { return eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; } -void SkiaGLRenderEngine::waitFence(GrDirectContext*, base::borrowed_fd fenceFd) { +void SkiaGLRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) { if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) { ATRACE_NAME("SkiaGLRenderEngine::waitFence"); sync_wait(fenceFd.get(), -1); } } -base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) { +base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context) { base::unique_fd drawFence = flush(); bool requireSync = drawFence.get() < 0; @@ -346,8 +346,8 @@ base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) { } else { ATRACE_BEGIN("Submit(sync=false)"); } - bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : - GrSyncCpu::kNo); + bool success = + context->grDirectContext()->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo); ATRACE_END(); if (!success) { ALOGE("Failed to flush RenderEngine commands"); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index af3311041d..2e22478274 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -59,11 +59,11 @@ public: protected: // Implementations of abstract SkiaRenderEngine functions specific to // rendering backend - virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options); + virtual SkiaRenderEngine::Contexts createContexts(); bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; - void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override; - base::unique_fd flushAndSubmit(GrDirectContext* context) override; + void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; + base::unique_fd flushAndSubmit(SkiaGpuContext* context) override; void appendBackendSpecificInfoToDump(std::string& result) override; private: diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 6e393f03fa..27daeba092 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -74,6 +74,7 @@ #include "Cache.h" #include "ColorSpaces.h" +#include "compat/SkiaGpuContext.h" #include "filters/BlurFilter.h" #include "filters/GaussianBlurFilter.h" #include "filters/KawaseBlurFilter.h" @@ -290,14 +291,12 @@ void SkiaRenderEngine::finishRenderingAndAbandonContext() { delete mBlurFilter; } - if (mGrContext) { - mGrContext->flushAndSubmit(GrSyncCpu::kYes); - mGrContext->abandonContext(); + if (mContext) { + mContext->finishRenderingAndAbandonContext(); } - if (mProtectedGrContext) { - mProtectedGrContext->flushAndSubmit(GrSyncCpu::kYes); - mProtectedGrContext->abandonContext(); + if (mProtectedContext) { + mProtectedContext->finishRenderingAndAbandonContext(); } } @@ -308,24 +307,24 @@ void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) { } // release any scratch resources before switching into a new mode - if (getActiveGrContext()) { - getActiveGrContext()->purgeUnlockedResources(GrPurgeResourceOptions::kScratchResourcesOnly); + if (getActiveContext()) { + getActiveContext()->purgeUnlockedScratchResources(); } // Backend-specific way to switch to protected context if (useProtectedContextImpl( useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) { mInProtectedContext = useProtectedContext; - // given that we are sharing the same thread between two GrContexts we need to + // given that we are sharing the same thread between two contexts we need to // make sure that the thread state is reset when switching between the two. - if (getActiveGrContext()) { - getActiveGrContext()->resetContext(); + if (getActiveContext()) { + getActiveContext()->resetContextIfApplicable(); } } } -GrDirectContext* SkiaRenderEngine::getActiveGrContext() { - return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get(); +SkiaGpuContext* SkiaRenderEngine::getActiveContext() { + return mInProtectedContext ? mProtectedContext.get() : mContext.get(); } static float toDegrees(uint32_t transform) { @@ -374,17 +373,12 @@ static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destin sourceTransfer != destTransfer; } -void SkiaRenderEngine::ensureGrContextsCreated() { - if (mGrContext) { +void SkiaRenderEngine::ensureContextsCreated() { + if (mContext) { return; } - GrContextOptions options; - options.fDisableDriverCorrectnessWorkarounds = true; - options.fDisableDistanceFieldPaths = true; - options.fReducedShaderVariations = true; - options.fPersistentCache = &mSkSLCacheMonitor; - std::tie(mGrContext, mProtectedGrContext) = createDirectContexts(options); + std::tie(mContext, mProtectedContext) = createContexts(); } void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, @@ -413,7 +407,7 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, // switch back after the buffer is cached). However, for non-protected content we can bind // the texture in either GL context because they are initialized with the same share_context // which allows the texture state to be shared between them. - auto grContext = getActiveGrContext(); + auto context = getActiveContext(); auto& cache = mTextureCache; std::lock_guard<std::mutex> lock(mRenderingMutex); @@ -424,8 +418,7 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER; } std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = - std::make_shared<AutoBackendTexture::LocalRef>(grContext, - buffer->toAHardwareBuffer(), + std::make_shared<AutoBackendTexture::LocalRef>(context, buffer->toAHardwareBuffer(), isRenderable, mTextureCleanupMgr); cache.insert({buffer->getId(), imageTextureRef}); } @@ -477,7 +470,7 @@ std::shared_ptr<AutoBackendTexture::LocalRef> SkiaRenderEngine::getOrCreateBacke return it->second; } } - return std::make_shared<AutoBackendTexture::LocalRef>(getActiveGrContext(), + return std::make_shared<AutoBackendTexture::LocalRef>(getActiveContext(), buffer->toAHardwareBuffer(), isOutputBuffer, mTextureCleanupMgr); } @@ -667,8 +660,8 @@ void SkiaRenderEngine::drawLayersInternal( validateOutputBufferUsage(buffer->getBuffer()); - auto grContext = getActiveGrContext(); - LOG_ALWAYS_FATAL_IF(grContext->abandoned(), "GrContext is abandoned/device lost at start of %s", + auto context = getActiveContext(); + LOG_ALWAYS_FATAL_IF(context->isAbandoned(), "Context is abandoned/device lost at start of %s", __func__); // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called @@ -677,10 +670,9 @@ void SkiaRenderEngine::drawLayersInternal( auto surfaceTextureRef = getOrCreateBackendTexture(buffer->getBuffer(), true); // wait on the buffer to be ready to use prior to using it - waitFence(grContext, bufferFence); + waitFence(context, bufferFence); - sk_sp<SkSurface> dstSurface = - surfaceTextureRef->getOrCreateSurface(display.outputDataspace, grContext); + sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(display.outputDataspace); SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); if (dstCanvas == nullptr) { @@ -845,7 +837,7 @@ void SkiaRenderEngine::drawLayersInternal( if (blurRect.width() > 0 && blurRect.height() > 0) { if (layer.backgroundBlurRadius > 0) { ATRACE_NAME("BackgroundBlur"); - auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius, + auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius, blurInput, blurRect); cachedBlurs[layer.backgroundBlurRadius] = blurredImage; @@ -859,7 +851,7 @@ void SkiaRenderEngine::drawLayersInternal( if (cachedBlurs[region.blurRadius] == nullptr) { ATRACE_NAME("BlurRegion"); cachedBlurs[region.blurRadius] = - mBlurFilter->generate(grContext, region.blurRadius, blurInput, + mBlurFilter->generate(context, region.blurRadius, blurInput, blurRect); } @@ -948,7 +940,7 @@ void SkiaRenderEngine::drawLayersInternal( // if the layer's buffer has a fence, then we must must respect the fence prior to using // the buffer. if (layer.source.buffer.fence != nullptr) { - waitFence(grContext, layer.source.buffer.fence->get()); + waitFence(context, layer.source.buffer.fence->get()); } // isOpaque means we need to ignore the alpha in the image, @@ -972,7 +964,7 @@ void SkiaRenderEngine::drawLayersInternal( : item.isOpaque ? kOpaque_SkAlphaType : item.usePremultipliedAlpha ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; - sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext); + sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType); auto texMatrix = getSkM44(item.textureTransform).asM33(); // textureTansform was intended to be passed directly into a shader, so when @@ -1159,7 +1151,7 @@ void SkiaRenderEngine::drawLayersInternal( skgpu::ganesh::Flush(activeSurface); } - auto drawFence = sp<Fence>::make(flushAndSubmit(grContext)); + auto drawFence = sp<Fence>::make(flushAndSubmit(context)); if (ATRACE_ENABLED()) { static gui::FenceMonitor sMonitor("RE Completion"); @@ -1169,11 +1161,11 @@ void SkiaRenderEngine::drawLayersInternal( } size_t SkiaRenderEngine::getMaxTextureSize() const { - return mGrContext->maxTextureSize(); + return mContext->getMaxTextureSize(); } size_t SkiaRenderEngine::getMaxViewportDims() const { - return mGrContext->maxRenderTargetSize(); + return mContext->getMaxRenderTargetSize(); } void SkiaRenderEngine::drawShadow(SkCanvas* canvas, @@ -1199,13 +1191,13 @@ void SkiaRenderEngine::onActiveDisplaySizeChanged(ui::Size size) { const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER; // start by resizing the current context - getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); + getActiveContext()->setResourceCacheLimit(maxResourceBytes); // if it is possible to switch contexts then we will resize the other context const bool originalProtectedState = mInProtectedContext; useProtectedContext(!mInProtectedContext); if (mInProtectedContext != originalProtectedState) { - getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); + getActiveContext()->setResourceCacheLimit(maxResourceBytes); // reset back to the initial context that was active when this method was called useProtectedContext(originalProtectedState); } @@ -1245,7 +1237,7 @@ void SkiaRenderEngine::dump(std::string& result) { {"skia", "Other"}, }; SkiaMemoryReporter gpuReporter(gpuResourceMap, true); - mGrContext->dumpMemoryStatistics(&gpuReporter); + mContext->dumpMemoryStatistics(&gpuReporter); StringAppendF(&result, "Skia's GPU Caches: "); gpuReporter.logTotals(result); gpuReporter.logOutput(result); @@ -1269,8 +1261,8 @@ void SkiaRenderEngine::dump(std::string& result) { StringAppendF(&result, "\n"); SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true); - if (mProtectedGrContext) { - mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter); + if (mProtectedContext) { + mProtectedContext->dumpMemoryStatistics(&gpuProtectedReporter); } StringAppendF(&result, "Skia's GPU Protected Caches: "); gpuProtectedReporter.logTotals(result); diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index e88d44cca6..9f892bd0f7 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -21,21 +21,21 @@ #include <sys/types.h> #include <GrBackendSemaphore.h> -#include <GrDirectContext.h> #include <SkSurface.h> #include <android-base/thread_annotations.h> #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sys/types.h> +#include <memory> #include <mutex> #include <unordered_map> #include "AutoBackendTexture.h" #include "GrContextOptions.h" #include "SkImageInfo.h" -#include "SkiaRenderEngine.h" #include "android-base/macros.h" +#include "compat/SkiaGpuContext.h" #include "debug/SkiaCapture.h" #include "filters/BlurFilter.h" #include "filters/LinearEffect.h" @@ -76,24 +76,25 @@ public: bool supportsProtectedContent() const override { return supportsProtectedContentImpl(); } - void ensureGrContextsCreated(); + void ensureContextsCreated(); + protected: // This is so backends can stop the generic rendering state first before // cleaning up backend-specific state void finishRenderingAndAbandonContext(); // Functions that a given backend (GLES, Vulkan) must implement - using Contexts = std::pair<sk_sp<GrDirectContext>, sk_sp<GrDirectContext>>; - virtual Contexts createDirectContexts(const GrContextOptions& options) = 0; + using Contexts = std::pair<unique_ptr<SkiaGpuContext>, unique_ptr<SkiaGpuContext>>; + virtual Contexts createContexts() = 0; virtual bool supportsProtectedContentImpl() const = 0; virtual bool useProtectedContextImpl(GrProtected isProtected) = 0; - virtual void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) = 0; - virtual base::unique_fd flushAndSubmit(GrDirectContext* context) = 0; + virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) = 0; + virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context) = 0; virtual void appendBackendSpecificInfoToDump(std::string& result) = 0; size_t getMaxTextureSize() const override final; size_t getMaxViewportDims() const override final; - GrDirectContext* getActiveGrContext(); + SkiaGpuContext* getActiveContext(); bool isProtected() const { return mInProtectedContext; } @@ -121,6 +122,8 @@ protected: int mTotalShadersCompiled = 0; }; + SkSLCacheMonitor mSkSLCacheMonitor; + private: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override final; @@ -183,12 +186,11 @@ private: // Mutex guarding rendering operations, so that internal state related to // rendering that is potentially modified by multiple threads is guaranteed thread-safe. mutable std::mutex mRenderingMutex; - SkSLCacheMonitor mSkSLCacheMonitor; // Graphics context used for creating surfaces and submitting commands - sk_sp<GrDirectContext> mGrContext; + unique_ptr<SkiaGpuContext> mContext; // Same as above, but for protected content (eg. DRM) - sk_sp<GrDirectContext> mProtectedGrContext; + unique_ptr<SkiaGpuContext> mProtectedContext; bool mInProtectedContext = false; }; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index f2f1b5d0ca..d854929960 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -21,12 +21,15 @@ #include "SkiaVkRenderEngine.h" +#include "compat/SkiaGpuContext.h" + #include <GrBackendSemaphore.h> #include <GrContextOptions.h> -#include <vk/GrVkExtensions.h> -#include <vk/GrVkTypes.h> +#include <GrDirectContext.h> #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> +#include <vk/GrVkExtensions.h> +#include <vk/GrVkTypes.h> #include <android-base/stringprintf.h> #include <gui/TraceUtils.h> @@ -82,7 +85,7 @@ using base::StringAppendF; std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); - engine->ensureGrContextsCreated(); + engine->ensureContextsCreated(); if (sVulkanInterface.isInitialized()) { ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__); @@ -103,16 +106,16 @@ SkiaVkRenderEngine::~SkiaVkRenderEngine() { finishRenderingAndAbandonContext(); } -SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts( - const GrContextOptions& options) { +SkiaRenderEngine::Contexts SkiaVkRenderEngine::createContexts() { sSetupVulkanInterface(); SkiaRenderEngine::Contexts contexts; - contexts.first = GrDirectContexts::MakeVulkan(sVulkanInterface.getBackendContext(), options); + contexts.first = SkiaGpuContext::MakeVulkan_Ganesh(sVulkanInterface.getGaneshBackendContext(), + mSkSLCacheMonitor); if (supportsProtectedContentImpl()) { - contexts.second = - GrDirectContexts::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(), - options); + contexts.second = SkiaGpuContext::MakeVulkan_Ganesh(sProtectedContentVulkanInterface + .getGaneshBackendContext(), + mSkSLCacheMonitor); } return contexts; @@ -139,7 +142,7 @@ static VulkanInterface& getVulkanInterface(bool protectedContext) { return sVulkanInterface; } -void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) { +void SkiaVkRenderEngine::waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) { if (fenceFd.get() < 0) return; int dupedFd = dup(fenceFd.get()); @@ -153,10 +156,11 @@ void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd VkSemaphore waitSemaphore = getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore); - grContext->wait(1, &beSemaphore, true /* delete after wait */); + context->grDirectContext()->wait(1, &beSemaphore, true /* delete after wait */); } -base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { +base::unique_fd SkiaVkRenderEngine::flushAndSubmit(SkiaGpuContext* context) { + sk_sp<GrDirectContext> grContext = context->grDirectContext(); VulkanInterface& vi = getVulkanInterface(isProtected()); VkSemaphore semaphore = vi.createExportableSemaphore(); diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index ca0dcbf4ae..28b7595d41 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -21,6 +21,7 @@ #include "SkiaRenderEngine.h" #include "VulkanInterface.h" +#include "compat/SkiaGpuContext.h" namespace android { namespace renderengine { @@ -72,11 +73,11 @@ public: protected: // Implementations of abstract SkiaRenderEngine functions specific to // rendering backend - virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options); + SkiaRenderEngine::Contexts createContexts() override; bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; - void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override; - base::unique_fd flushAndSubmit(GrDirectContext* context) override; + void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; + base::unique_fd flushAndSubmit(SkiaGpuContext* context) override; void appendBackendSpecificInfoToDump(std::string& result) override; private: diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp index 453cdc1196..2f09a382d7 100644 --- a/libs/renderengine/skia/VulkanInterface.cpp +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -30,7 +30,7 @@ namespace android { namespace renderengine { namespace skia { -GrVkBackendContext VulkanInterface::getBackendContext() { +GrVkBackendContext VulkanInterface::getGaneshBackendContext() { GrVkBackendContext backendContext; backendContext.fInstance = mInstance; backendContext.fPhysicalDevice = mPhysicalDevice; diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h index c3936d9869..512e62c8f2 100644 --- a/libs/renderengine/skia/VulkanInterface.h +++ b/libs/renderengine/skia/VulkanInterface.h @@ -40,7 +40,7 @@ public: void teardown(); // TODO: b/293371537 - Graphite variant (external/skia/include/gpu/vk/VulkanBackendContext.h) - GrVkBackendContext getBackendContext(); + GrVkBackendContext getGaneshBackendContext(); VkSemaphore createExportableSemaphore(); VkSemaphore importSemaphoreFromSyncFd(int syncFd); int exportSemaphoreSyncFd(VkSemaphore semaphore); diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp new file mode 100644 index 0000000000..51c6a6cd1c --- /dev/null +++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp @@ -0,0 +1,111 @@ +/* + * 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 "GaneshGpuContext.h" + +#include <include/core/SkImageInfo.h> +#include <include/core/SkSurface.h> +#include <include/core/SkTraceMemoryDump.h> +#include <include/gpu/GrDirectContext.h> +#include <include/gpu/GrTypes.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <include/gpu/ganesh/gl/GrGLDirectContext.h> +#include <include/gpu/ganesh/vk/GrVkDirectContext.h> +#include <include/gpu/gl/GrGLInterface.h> +#include <include/gpu/vk/GrVkBackendContext.h> + +#include <android-base/macros.h> +#include <log/log_main.h> + +namespace android::renderengine::skia { + +namespace { +// TODO: b/293371537 - Graphite variant. +static GrContextOptions ganeshOptions(GrContextOptions::PersistentCache& skSLCacheMonitor) { + GrContextOptions options; + options.fDisableDriverCorrectnessWorkarounds = true; + options.fDisableDistanceFieldPaths = true; + options.fReducedShaderVariations = true; + options.fPersistentCache = &skSLCacheMonitor; + return options; +} +} // namespace + +std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeGL_Ganesh( + sk_sp<const GrGLInterface> glInterface, + GrContextOptions::PersistentCache& skSLCacheMonitor) { + return std::make_unique<GaneshGpuContext>( + GrDirectContexts::MakeGL(glInterface, ganeshOptions(skSLCacheMonitor))); +} + +std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Ganesh( + const GrVkBackendContext& grVkBackendContext, + GrContextOptions::PersistentCache& skSLCacheMonitor) { + return std::make_unique<GaneshGpuContext>( + GrDirectContexts::MakeVulkan(grVkBackendContext, ganeshOptions(skSLCacheMonitor))); +} + +GaneshGpuContext::GaneshGpuContext(sk_sp<GrDirectContext> grContext) : mGrContext(grContext) { + LOG_ALWAYS_FATAL_IF(mGrContext.get() == nullptr, "GrDirectContext creation failed"); +} + +sk_sp<GrDirectContext> GaneshGpuContext::grDirectContext() { + return mGrContext; +} + +sk_sp<SkSurface> GaneshGpuContext::createRenderTarget(SkImageInfo imageInfo) { + constexpr int kSampleCount = 1; // enable AA + constexpr SkSurfaceProps* kProps = nullptr; + constexpr bool kMipmapped = false; + return SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kNo, imageInfo, kSampleCount, + kTopLeft_GrSurfaceOrigin, kProps, kMipmapped, + mGrContext->supportsProtectedContent()); +} + +size_t GaneshGpuContext::getMaxRenderTargetSize() const { + return mGrContext->maxRenderTargetSize(); +}; + +size_t GaneshGpuContext::getMaxTextureSize() const { + return mGrContext->maxTextureSize(); +}; + +bool GaneshGpuContext::isAbandoned() { + return mGrContext->abandoned(); +} + +void GaneshGpuContext::setResourceCacheLimit(size_t maxResourceBytes) { + mGrContext->setResourceCacheLimit(maxResourceBytes); +} + +void GaneshGpuContext::finishRenderingAndAbandonContext() { + mGrContext->flushAndSubmit(GrSyncCpu::kYes); + mGrContext->abandonContext(); +}; + +void GaneshGpuContext::purgeUnlockedScratchResources() { + mGrContext->purgeUnlockedResources(GrPurgeResourceOptions::kScratchResourcesOnly); +} + +void GaneshGpuContext::resetContextIfApplicable() { + mGrContext->resetContext(); // Only applicable to GL +}; + +void GaneshGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const { + mGrContext->dumpMemoryStatistics(traceMemoryDump); +} + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.h b/libs/renderengine/skia/compat/GaneshGpuContext.h new file mode 100644 index 0000000000..59001eccc2 --- /dev/null +++ b/libs/renderengine/skia/compat/GaneshGpuContext.h @@ -0,0 +1,51 @@ +/* + * 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 "SkiaGpuContext.h" + +#include <android-base/macros.h> + +namespace android::renderengine::skia { + +class GaneshGpuContext : public SkiaGpuContext { +public: + GaneshGpuContext(sk_sp<GrDirectContext> grContext); + ~GaneshGpuContext() override = default; + + sk_sp<GrDirectContext> grDirectContext() override; + + sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) override; + + size_t getMaxRenderTargetSize() const override; + size_t getMaxTextureSize() const override; + bool isAbandoned() override; + void setResourceCacheLimit(size_t maxResourceBytes) override; + + void finishRenderingAndAbandonContext() override; + void purgeUnlockedScratchResources() override; + void resetContextIfApplicable() override; + + void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const override; + +private: + DISALLOW_COPY_AND_ASSIGN(GaneshGpuContext); + + const sk_sp<GrDirectContext> mGrContext; +}; + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h new file mode 100644 index 0000000000..ba167f352d --- /dev/null +++ b/libs/renderengine/skia/compat/SkiaGpuContext.h @@ -0,0 +1,76 @@ +/* + * 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 + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" + +#include <include/core/SkSurface.h> +#include <include/gpu/GrDirectContext.h> +#include <include/gpu/gl/GrGLInterface.h> +#include <include/gpu/vk/GrVkBackendContext.h> + +#include <log/log.h> + +namespace android::renderengine::skia { + +/** + * Abstraction over Ganesh and Graphite's underlying context-like objects. + */ +class SkiaGpuContext { +public: + static std::unique_ptr<SkiaGpuContext> MakeGL_Ganesh( + sk_sp<const GrGLInterface> glInterface, + GrContextOptions::PersistentCache& skSLCacheMonitor); + + // TODO: b/293371537 - Graphite variant. + static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh( + const GrVkBackendContext& grVkBackendContext, + GrContextOptions::PersistentCache& skSLCacheMonitor); + + virtual ~SkiaGpuContext() = default; + + // TODO: b/293371537 - Maybe expose whether this SkiaGpuContext is using Ganesh or Graphite? + /** + * Only callable on Ganesh-backed instances of SkiaGpuContext, otherwise fatal. + */ + virtual sk_sp<GrDirectContext> grDirectContext() { + LOG_ALWAYS_FATAL("grDirectContext() called on a non-Ganesh instance of SkiaGpuContext!"); + } + + /** + * Notes: + * - The surface doesn't count against Skia's caching budgets. + * - Protected status is set to match the implementation's underlying context. + * - The origin of the surface in texture space corresponds to the top-left content pixel. + * - AA is always enabled. + */ + virtual sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) = 0; + + virtual bool isAbandoned() = 0; + virtual size_t getMaxRenderTargetSize() const = 0; + virtual size_t getMaxTextureSize() const = 0; + virtual void setResourceCacheLimit(size_t maxResourceBytes) = 0; + + virtual void finishRenderingAndAbandonContext() = 0; + virtual void purgeUnlockedScratchResources() = 0; + virtual void resetContextIfApplicable() = 0; // No-op outside of GL (&& Ganesh at this point.) + + virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const = 0; +}; + +} // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h index 9cddc757fc..180c92262f 100644 --- a/libs/renderengine/skia/filters/BlurFilter.h +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -21,6 +21,8 @@ #include <SkRuntimeEffect.h> #include <SkSurface.h> +#include "../compat/SkiaGpuContext.h" + using namespace std; namespace android { @@ -38,8 +40,9 @@ public: virtual ~BlurFilter(){} // Execute blur, saving it to a texture - virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, - const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0; + virtual sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius, + const sk_sp<SkImage> blurInput, + const SkRect& blurRect) const = 0; /** * Draw the blurred content (from the generate method) into the canvas. diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index e72c501336..c9499cbc24 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -42,14 +42,13 @@ static const float BLUR_SIGMA_SCALE = 0.57735f; GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {} -sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius, - const sk_sp<SkImage> input, const SkRect& blurRect) - const { +sk_sp<SkImage> GaussianBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius, + const sk_sp<SkImage> input, + const SkRect& blurRect) const { // Create blur surface with the bit depth and colorspace of the original surface SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale), std::ceil(blurRect.height() * kInputScale)); - sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, - skgpu::Budgeted::kNo, scaledInfo); + sk_sp<SkSurface> surface = context->createRenderTarget(scaledInfo); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h index a4febd2257..878ab21b36 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.h +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h @@ -37,9 +37,8 @@ public: virtual ~GaussianBlurFilter(){} // Execute blur, saving it to a texture - sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, + sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius, const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override; - }; } // namespace skia diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index 09f09a697a..7a070d7024 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -73,8 +73,7 @@ static sk_sp<SkImage> makeImage(SkSurface* surface, SkRuntimeShaderBuilder* buil return surface->makeImageSnapshot(); } -sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, - const uint32_t blurRadius, +sk_sp<SkImage> KawaseBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius, const sk_sp<SkImage> input, const SkRect& blurRect) const { LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__); @@ -108,12 +107,7 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix); blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale; - constexpr int kSampleCount = 1; - constexpr bool kMipmapped = false; - constexpr SkSurfaceProps* kProps = nullptr; - sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, scaledInfo, - kSampleCount, kTopLeft_GrSurfaceOrigin, - kProps, kMipmapped, input->isProtected()); + sk_sp<SkSurface> surface = context->createRenderTarget(scaledInfo); LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__); sk_sp<SkImage> tmpBlur = makeImage(surface.get(), &blurBuilder); diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h index 0ac5ac8866..429a5378a3 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.h +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h @@ -42,7 +42,7 @@ public: virtual ~KawaseBlurFilter(){} // Execute blur, saving it to a texture - sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, + sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius, const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override; private: diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index 976db7e430..758ace63ab 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -143,6 +143,10 @@ bool isPerfettoRegistered() { struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { struct PerfettoTeCategory* perfettoCategory = toCategory(category); + if (perfettoCategory == nullptr) { + return nullptr; + } + bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); return enabled ? perfettoCategory : nullptr; diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h index fe25130bd8..8ce61e7800 100644 --- a/services/inputflinger/tests/FakeWindowHandle.h +++ b/services/inputflinger/tests/FakeWindowHandle.h @@ -17,6 +17,7 @@ #pragma once #include <android-base/logging.h> +#include <input/InputConsumer.h> #include "../dispatcher/InputDispatcher.h" using android::base::Result; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b3b2ee2c9d..9375e92eac 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -33,6 +33,7 @@ #include <gtest/gtest.h> #include <input/BlockingQueue.h> #include <input/Input.h> +#include <input/InputConsumer.h> #include <input/PrintTools.h> #include <linux/input.h> #include <sys/epoll.h> |