diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/android/keycodes.h | 38 | ||||
-rw-r--r-- | include/android/performance_hint.h | 1 | ||||
-rw-r--r-- | include/android/surface_control.h | 63 | ||||
-rw-r--r-- | include/input/CoordinateFilter.h | 54 | ||||
-rw-r--r-- | include/input/InputConsumerNoResampling.h | 15 | ||||
-rw-r--r-- | include/input/InputDevice.h | 3 | ||||
-rw-r--r-- | include/input/OneEuroFilter.h | 101 | ||||
-rw-r--r-- | include/input/Resampler.h | 41 |
8 files changed, 307 insertions, 9 deletions
diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 79cdbcaf7b..495e0bdb1f 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -843,6 +843,44 @@ enum { AKEYCODE_EMOJI_PICKER = 317, /** Take Screenshot */ AKEYCODE_SCREENSHOT = 318, + /** To start dictate to an input field */ + AKEYCODE_DICTATE = 319, + /** AC New */ + AKEYCODE_NEW = 320, + /** AC Close */ + AKEYCODE_CLOSE = 321, + /** To toggle 'Do Not Disturb' mode */ + AKEYCODE_DO_NOT_DISTURB = 322, + /** To Print */ + AKEYCODE_PRINT = 323, + /** To Lock the screen */ + AKEYCODE_LOCK = 324, + /** To toggle fullscreen mode (on the current application) */ + AKEYCODE_FULLSCREEN = 325, + /** F13 key */ + AKEYCODE_F13 = 326, + /** F14 key */ + AKEYCODE_F14 = 327, + /** F15 key */ + AKEYCODE_F15 = 328, + /** F16 key */ + AKEYCODE_F16 = 329, + /** F17 key */ + AKEYCODE_F17 = 330, + /** F18 key */ + AKEYCODE_F18 = 331, + /** F19 key */ + AKEYCODE_F19 = 332, + /** F20 key */ + AKEYCODE_F20 = 333, + /** F21 key */ + AKEYCODE_F21 = 334, + /** F22 key */ + AKEYCODE_F22 = 335, + /** F23 key */ + AKEYCODE_F23 = 336, + /** F24 key */ + AKEYCODE_F24 = 337, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 3f32a5abb3..3486e9b1d7 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -52,7 +52,6 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -#include <android/api-level.h> #include <stdbool.h> #include <stdint.h> #include <unistd.h> diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 8d61e772cc..fe38e86015 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -763,6 +763,69 @@ void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* _No __INTRODUCED_IN(31); /** + * Sets the intended frame rate for the given \a surface_control. + * + * On devices that are capable of running the display at different frame rates, + * the system may choose a display refresh rate to better match this surface's frame + * rate. Usage of this API won't introduce frame rate throttling, or affect other + * aspects of the application's frame production pipeline. However, because the system + * may change the display refresh rate, calls to this function may result in changes + * to Choreographer callback timings, and changes to the time interval at which the + * system releases buffers back to the application. + * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * + * See ASurfaceTransaction_clearFrameRate(). + * + * Available since API level 36. + * + * \param desiredMinRate The desired minimum frame rate (inclusive) for the surface, specifying that + * the surface prefers the device render rate to be at least `desiredMinRate`. + * + * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMinRate` = 0 to indicate the surface has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to 0. + * + * \param desiredMaxRate The desired maximum frame rate (inclusive) for the surface, specifying that + * the surface prefers the device render rate to be at most `desiredMaxRate`. + * + * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMaxRate` = positive infinity to indicate the surface has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to `desiredMinRate`. + * + * \param fixedSourceRate The "fixed source" frame rate of the surface if the content has an + * inherently fixed frame rate, e.g. a video that has a specific frame rate. + * + * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + * <p>Setting the fixed source rate can also be used together with a desired + * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still + * means the surface's content has a fixed frame rate of `fixedSourceRate`, but additionally + * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an + * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth + * animation on the same surface which looks good when drawing within a frame rate range such as + * [`desiredMinRate`, `desiredMaxRate`] = [60,120]. + * + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface + * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such + * as a black screen for a second or two. + */ +void ASurfaceTransaction_setFrameRateParams( + ASurfaceTransaction* _Nonnull transaction, ASurfaceControl* _Nonnull surface_control, + float desiredMinRate, float desiredMaxRate, float fixedSourceRate, + ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36); + +/** * Clears the frame rate which is set for \a surface_control. * * This is equivalent to calling diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h new file mode 100644 index 0000000000..f36472dc8c --- /dev/null +++ b/include/input/CoordinateFilter.h @@ -0,0 +1,54 @@ +/** + * 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 <chrono> + +#include <input/Input.h> +#include <input/OneEuroFilter.h> + +namespace android { + +/** + * Pair of OneEuroFilters that independently filter X and Y coordinates. Both filters share the same + * constructor's parameters. The minimum cutoff frequency is the base cutoff frequency, that is, the + * resulting cutoff frequency in the absence of signal's speed. Likewise, beta is a scaling factor + * of the signal's speed that sets how much the signal's speed contributes to the resulting cutoff + * frequency. The adaptive cutoff frequency criterion is f_c = f_c_min + β|̇x_filtered| + */ +class CoordinateFilter { +public: + explicit CoordinateFilter(float minCutoffFreq, float beta); + + /** + * Filters in place only the AXIS_X and AXIS_Y fields from coords. Each call to filter must + * provide a timestamp strictly greater than the timestamp of the previous call. The first time + * this method is invoked no filtering takes place. Subsequent calls do overwrite `coords` with + * filtered data. + * + * @param timestamp The timestamps at which to filter. It must be greater than the one passed in + * the previous call. + * @param coords Coordinates to be overwritten by the corresponding filtered coordinates. + */ + void filter(std::chrono::duration<float> timestamp, PointerCoords& coords); + +private: + OneEuroFilter mXFilter; + OneEuroFilter mYFilter; +}; + +} // namespace android
\ No newline at end of file diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 228347d818..2e346bb7cd 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -211,16 +211,17 @@ private: * `consumeBatchedInputEvents`. */ std::map<DeviceId, std::queue<InputMessage>> mBatches; + /** - * Creates a MotionEvent by consuming samples from the provided queue. If one message has - * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is - * assumed that messages are queued in chronological order. In other words, only events that - * occurred prior to the adjustedFrameTime will be consumed. - * @param requestedFrameTime the time up to which to consume events. - * @param messages the queue of messages to consume from + * Creates a MotionEvent by consuming samples from the provided queue. Consumes all messages + * with eventTime <= requestedFrameTime - resampleLatency, where `resampleLatency` is latency + * introduced by the resampler. Assumes that messages are queued in chronological order. + * @param requestedFrameTime The time up to which consume messages, as given by the inequality + * above. If std::nullopt, everything in messages will be consumed. + * @param messages the queue of messages to consume from. */ std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent( - const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages); + const std::optional<nsecs_t> requestedFrameTime, std::queue<InputMessage>& messages); /** * Consumes the batched input events, optionally allowing the caller to specify a device id diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 6a248ef188..6b45dd39dc 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -131,8 +131,9 @@ enum class InputDeviceLightType : int32_t { PLAYER_ID = 1, KEYBOARD_BACKLIGHT = 2, KEYBOARD_MIC_MUTE = 3, + KEYBOARD_VOLUME_MUTE = 4, - ftl_last = KEYBOARD_MIC_MUTE + ftl_last = KEYBOARD_VOLUME_MUTE }; enum class InputDeviceLightCapability : uint32_t { diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h new file mode 100644 index 0000000000..a0168e4f91 --- /dev/null +++ b/include/input/OneEuroFilter.h @@ -0,0 +1,101 @@ +/** + * 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 <chrono> +#include <optional> + +#include <input/Input.h> + +namespace android { + +/** + * Low pass filter with adaptive low pass frequency based on the signal's speed. The signal's cutoff + * frequency is determined by f_c = f_c_min + β|̇x_filtered|. Refer to + * https://dl.acm.org/doi/10.1145/2207676.2208639 for details on how the filter works and how to + * tune it. + */ +class OneEuroFilter { +public: + /** + * Default cutoff frequency of the filtered signal's speed. 1.0 Hz is the value in the filter's + * paper. + */ + static constexpr float kDefaultSpeedCutoffFreq = 1.0; + + OneEuroFilter() = delete; + + explicit OneEuroFilter(float minCutoffFreq, float beta, + float speedCutoffFreq = kDefaultSpeedCutoffFreq); + + OneEuroFilter(const OneEuroFilter&) = delete; + OneEuroFilter& operator=(const OneEuroFilter&) = delete; + OneEuroFilter(OneEuroFilter&&) = delete; + OneEuroFilter& operator=(OneEuroFilter&&) = delete; + + /** + * Returns the filtered value of rawPosition. Each call to filter must provide a timestamp + * strictly greater than the timestamp of the previous call. The first time the method is + * called, it returns the value of rawPosition. Any subsequent calls provide a filtered value. + * + * @param timestamp The timestamp at which to filter. It must be strictly greater than the one + * provided in the previous call. + * @param rawPosition Position to be filtered. + */ + float filter(std::chrono::duration<float> timestamp, float rawPosition); + +private: + /** + * Minimum cutoff frequency. This is the constant term in the adaptive cutoff frequency + * criterion. Units are Hertz. + */ + const float mMinCutoffFreq; + + /** + * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the + * filtered signal's speed. The data member is dimensionless, that is, it does not have units. + */ + const float mBeta; + + /** + * Cutoff frequency of the signal's speed. This is the cutoff frequency applied to the filtering + * of the signal's speed. Units are Hertz. + */ + const float mSpeedCutoffFreq; + + /** + * The timestamp from the previous call. Units are seconds. + */ + std::optional<std::chrono::duration<float>> mPrevTimestamp; + + /** + * The raw position from the previous call. + */ + std::optional<float> mPrevRawPosition; + + /** + * The filtered velocity from the previous call. Units are position per second. + */ + std::optional<float> mPrevFilteredVelocity; + + /** + * The filtered position from the previous call. + */ + std::optional<float> mPrevFilteredPosition; +}; + +} // namespace android diff --git a/include/input/Resampler.h b/include/input/Resampler.h index 6d95ca7e86..155097732c 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -19,11 +19,13 @@ #include <array> #include <chrono> #include <iterator> +#include <map> #include <optional> #include <vector> #include <android-base/logging.h> #include <ftl/mixins.h> +#include <input/CoordinateFilter.h> #include <input/Input.h> #include <input/InputTransport.h> #include <input/RingBuffer.h> @@ -293,4 +295,43 @@ private: inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; +/** + * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters + * the X and Y coordinates with a pair of One Euro filters. + */ +class FilteredLegacyResampler final : public Resampler { +public: + /** + * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro + * filters. + */ + explicit FilteredLegacyResampler(float minCutoffFreq, float beta); + + void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent, + const InputMessage* futureMessage) override; + + std::chrono::nanoseconds getResampleLatency() const override; + +private: + LegacyResampler mResampler; + + /** + * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mMinCutoffFreq; + + /** + * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mBeta; + + /* + * Note: an associative array with constant insertion and lookup times would be more efficient. + * When this was implemented, there was no container with these properties. + */ + std::map<int32_t /*pointerId*/, CoordinateFilter> mFilteredPointers; +}; + } // namespace android |