Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fbc67977 authored by Jeff Brown's avatar Jeff Brown Committed by Android Git Automerger
Browse files

am d5358874: am 5ced76a1: Coalesce input events that arrive faster than 333Hz. (DO NOT MERGE)

* commit 'd5358874':
  Coalesce input events that arrive faster than 333Hz. (DO NOT MERGE)
parents e79397df d5358874
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -27,8 +27,14 @@

namespace android {

#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1))
#define MIN_HISTORY_DEPTH 20

// Must be at least sizeof(InputMessage) + sufficient space for pointer data
static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384;
static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP(
        sizeof(InputMessage) + MIN_HISTORY_DEPTH
                * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)),
        4096);

// Signal sent by the producer to the consumer to inform it that a new message is
// available to be consumed in the shared memory buffer.
+17 −0
Original line number Diff line number Diff line
@@ -539,7 +539,24 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                iev.type, iev.code, iev.value);

#ifdef HAVE_POSIX_CLOCKS
                        // Use the time specified in the event instead of the current time
                        // so that downstream code can get more accurate estimates of
                        // event dispatch latency from the time the event is enqueued onto
                        // the evdev client buffer.
                        //
                        // The event's timestamp fortuitously uses the same monotonic clock
                        // time base as the rest of Android.  The kernel event device driver
                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
                        // system call that also queries ktime_get_ts().
                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;
                        LOGV("event time %lld, now %lld", event->when, now);
#else
                        event->when = now;
#endif
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->scanCode = iev.code;
+72 −19
Original line number Diff line number Diff line
@@ -76,6 +76,22 @@ const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
// before considering it stale and dropping it.
const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec

// Motion samples that are received within this amount of time are simply coalesced
// when batched instead of being appended.  This is done because some drivers update
// the location of pointers one at a time instead of all at once.
// For example, when there are 10 fingers down, the input dispatcher may receive 10
// samples in quick succession with only one finger's location changed in each sample.
//
// This value effectively imposes an upper bound on the touch sampling rate.
// Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct
// samples to become available 5-20ms apart but individual finger reports can trickle
// in over a period of 2-4ms or so.
//
// Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough,
// a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce
// significant quantization noise on current hardware.
const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz


static inline nsecs_t now() {
    return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -2385,21 +2401,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
                    continue;
                }

                if (motionEntry->action != action
                        || motionEntry->pointerCount != pointerCount
                        || motionEntry->isInjected()) {
                if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) {
                    // Last motion event in the queue for this device and source is
                    // not compatible for appending new samples.  Stop here.
                    goto NoBatchingOrStreaming;
                }

                // The last motion event is a move and is compatible for appending.
                // Do the batching magic.
                mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
#if DEBUG_BATCHING
                LOGD("Appended motion sample onto batch for most recent "
                        "motion event for this device in the inbound queue.");
#endif
                batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
                        "most recent motion event for this device and source in the inbound queue");
                return; // done!
            }

@@ -2413,19 +2423,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
                    && mPendingEvent->type == EventEntry::TYPE_MOTION) {
                MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent);
                if (motionEntry->deviceId == deviceId && motionEntry->source == source) {
                    if (motionEntry->action != action
                            || motionEntry->pointerCount != pointerCount
                            || motionEntry->isInjected()) {
                        // Pending event is not compatible for appending new samples.  Stop here.
                    if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) {
                        // Pending motion event is for this device and source but it is
                        // not compatible for appending new samples.  Stop here.
                        goto NoBatchingOrStreaming;
                    }

                    // The pending motion event is a move and is compatible for appending.
                    // Do the batching magic.
                    mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
#if DEBUG_BATCHING
                    LOGD("Appended motion sample onto batch for the pending motion event.");
#endif
                    batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
                            "pending motion event");
                    mLock.unlock();
                    return; // done!
                }
@@ -2485,7 +2491,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
                    mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
#if DEBUG_BATCHING
                    LOGD("Appended motion sample onto batch for most recently dispatched "
                            "motion event for this device in the outbound queues.  "
                            "motion event for this device and source in the outbound queues.  "
                            "Attempting to stream the motion sample.");
#endif
                    nsecs_t currentTime = now();
@@ -2514,6 +2520,36 @@ NoBatchingOrStreaming:;
    }
}

void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime,
        int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) {
    // Combine meta states.
    entry->metaState |= metaState;

    // Coalesce this sample if not enough time has elapsed since the last sample was
    // initially appended to the batch.
    MotionSample* lastSample = entry->lastSample;
    long interval = eventTime - lastSample->eventTimeBeforeCoalescing;
    if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) {
        uint32_t pointerCount = entry->pointerCount;
        for (uint32_t i = 0; i < pointerCount; i++) {
            lastSample->pointerCoords[i].copyFrom(pointerCoords[i]);
        }
        lastSample->eventTime = eventTime;
#if DEBUG_BATCHING
        LOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart",
                eventDescription, interval * 0.000001f);
#endif
        return;
    }

    // Append the sample.
    mAllocator.appendMotionSample(entry, eventTime, pointerCoords);
#if DEBUG_BATCHING
    LOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart",
            eventDescription, interval * 0.000001f);
#endif
}

void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
        uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
@@ -3572,6 +3608,7 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec
    entry->downTime = downTime;
    entry->pointerCount = pointerCount;
    entry->firstSample.eventTime = eventTime;
    entry->firstSample.eventTimeBeforeCoalescing = eventTime;
    entry->firstSample.next = NULL;
    entry->lastSample = & entry->firstSample;
    for (uint32_t i = 0; i < pointerCount; i++) {
@@ -3678,6 +3715,7 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
        nsecs_t eventTime, const PointerCoords* pointerCoords) {
    MotionSample* sample = mMotionSamplePool.alloc();
    sample->eventTime = eventTime;
    sample->eventTimeBeforeCoalescing = eventTime;
    uint32_t pointerCount = motionEntry->pointerCount;
    for (uint32_t i = 0; i < pointerCount; i++) {
        sample->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -3707,6 +3745,21 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const {
    return count;
}

bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount,
        const int32_t* pointerIds) const {
    if (this->action != action
            || this->pointerCount != pointerCount
            || this->isInjected()) {
        return false;
    }
    for (uint32_t i = 0; i < pointerCount; i++) {
        if (this->pointerIds[i] != pointerIds[i]) {
            return false;
        }
    }
    return true;
}


// --- InputDispatcher::InputState ---

+12 −2
Original line number Diff line number Diff line
@@ -375,7 +375,7 @@ private:

        bool dispatchInProgress; // initially false, set to true while dispatching

        inline bool isInjected() { return injectionState != NULL; }
        inline bool isInjected() const { return injectionState != NULL; }
    };

    struct ConfigurationChangedEntry : EventEntry {
@@ -405,7 +405,8 @@ private:
    struct MotionSample {
        MotionSample* next;

        nsecs_t eventTime;
        nsecs_t eventTime; // may be updated during coalescing
        nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing
        PointerCoords pointerCoords[MAX_POINTERS];
    };

@@ -427,6 +428,10 @@ private:
        MotionSample* lastSample;

        uint32_t countSamples() const;

        // Checks whether we can append samples, assuming the device id and source are the same.
        bool canAppendSamples(int32_t action, uint32_t pointerCount,
                const int32_t* pointerIds) const;
    };

    // Tracks the progress of dispatching a particular event to a particular connection.
@@ -767,6 +772,11 @@ private:
    void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
            nsecs_t* nextWakeupTime);

    // Batches a new sample onto a motion entry.
    // Assumes that the we have already checked that we can append samples.
    void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState,
            const PointerCoords* pointerCoords, const char* eventDescription);

    // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
    bool enqueueInboundEventLocked(EventEntry* entry);