Merge "SF: Move hotplug dispatch to Scheduler" into main
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index d4dbc45..c348833 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -20,12 +20,14 @@
#include <unistd.h>
#include <ctype.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <ftl/enum.h>
#include <gui/constants.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
+using android::base::GetProperty;
using android::base::StringPrintf;
namespace android {
@@ -96,21 +98,22 @@
// Treblized input device config files will be located /product/usr, /system_ext/usr,
// /odm/usr or /vendor/usr.
- // These files may also be in the com.android.input.config APEX.
- const char* rootsForPartition[]{
- "/product",
- "/system_ext",
- "/odm",
- "/vendor",
- "/apex/com.android.input.config/etc",
- getenv("ANDROID_ROOT"),
+ std::vector<std::string> pathPrefixes{
+ "/product/usr/",
+ "/system_ext/usr/",
+ "/odm/usr/",
+ "/vendor/usr/",
};
- for (size_t i = 0; i < size(rootsForPartition); i++) {
- if (rootsForPartition[i] == nullptr) {
- continue;
- }
- path = rootsForPartition[i];
- path += "/usr/";
+ // These files may also be in the APEX pointed by input_device.config_file.apex sysprop.
+ if (auto apex = GetProperty("input_device.config_file.apex", ""); !apex.empty()) {
+ pathPrefixes.push_back("/apex/" + apex + "/etc/usr/");
+ }
+ // ANDROID_ROOT may not be set on host
+ if (auto android_root = getenv("ANDROID_ROOT"); android_root != nullptr) {
+ pathPrefixes.push_back(std::string(android_root) + "/usr/");
+ }
+ for (const auto& prefix : pathPrefixes) {
+ path = prefix;
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
ALOGD("Probing for system provided input device configuration file: path='%s'",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f31884e..8858f0c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -54,6 +54,7 @@
#include "InputDispatcher.h"
#include "trace/InputTracer.h"
#include "trace/InputTracingPerfettoBackend.h"
+#include "trace/ThreadedBackend.h"
#define INDENT " "
#define INDENT2 " "
@@ -86,6 +87,15 @@
return input_flags::enable_input_event_tracing() && isUserdebugOrEng;
}
+// Create the input tracing backend that writes to perfetto from a single thread.
+std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled() {
+ if (!isInputTracingEnabled()) {
+ return nullptr;
+ }
+ return std::make_unique<trace::impl::ThreadedBackend<trace::impl::PerfettoBackend>>(
+ trace::impl::PerfettoBackend());
+}
+
template <class Entry>
void ensureEventTraced(const Entry& entry) {
if (!entry.traceTracker) {
@@ -832,9 +842,7 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
- : InputDispatcher(policy,
- isInputTracingEnabled() ? std::make_unique<trace::impl::PerfettoBackend>()
- : nullptr) {}
+ : InputDispatcher(policy, createInputTracingBackendIfEnabled()) {}
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 8a855c2..be09013 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -19,12 +19,17 @@
#include "InputTracer.h"
#include <android-base/logging.h>
-#include <utils/AndroidThreads.h>
namespace android::inputdispatcher::trace::impl {
namespace {
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {
+ using V::operator()...;
+};
+
TracedEvent createTracedEvent(const MotionEntry& e) {
return TracedMotionEvent{e.id,
e.eventTime,
@@ -59,19 +64,9 @@
// --- InputTracer ---
InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend)
- : mTracerThread(&InputTracer::threadLoop, this), mBackend(std::move(backend)) {}
-
-InputTracer::~InputTracer() {
- {
- std::scoped_lock lock(mLock);
- mThreadExit = true;
- }
- mThreadWakeCondition.notify_all();
- mTracerThread.join();
-}
+ : mBackend(std::move(backend)) {}
std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
- std::scoped_lock lock(mLock);
TracedEvent traced;
if (entry.type == EventEntry::Type::MOTION) {
@@ -89,7 +84,6 @@
void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
const InputTarget& target) {
- std::scoped_lock lock(mLock);
auto& cookieState = getState(cookie);
if (!cookieState) {
LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
@@ -98,127 +92,72 @@
}
void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
- {
- std::scoped_lock lock(mLock);
- auto& cookieState = getState(cookie);
- if (!cookieState) {
- LOG(FATAL) << "Traced event was already logged. "
- "eventProcessingComplete() was likely called more than once.";
- }
- mTraceQueue.emplace_back(std::move(*cookieState));
- cookieState.reset();
- } // release lock
+ auto& cookieState = getState(cookie);
+ if (!cookieState) {
+ LOG(FATAL) << "Traced event was already logged. "
+ "eventProcessingComplete() was likely called more than once.";
+ }
- mThreadWakeCondition.notify_all();
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
+ [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
+ cookieState->event);
+ cookieState.reset();
}
void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
const EventTrackerInterface* cookie) {
- {
- std::scoped_lock lock(mLock);
- const EventEntry& entry = *dispatchEntry.eventEntry;
+ const EventEntry& entry = *dispatchEntry.eventEntry;
- TracedEvent traced;
- if (entry.type == EventEntry::Type::MOTION) {
- const auto& motion = static_cast<const MotionEntry&>(entry);
- traced = createTracedEvent(motion);
- } else if (entry.type == EventEntry::Type::KEY) {
- const auto& key = static_cast<const KeyEntry&>(entry);
- traced = createTracedEvent(key);
- } else {
- LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
- }
+ TracedEvent traced;
+ if (entry.type == EventEntry::Type::MOTION) {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ traced = createTracedEvent(motion);
+ } else if (entry.type == EventEntry::Type::KEY) {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ traced = createTracedEvent(key);
+ } else {
+ LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+ }
- if (!cookie) {
- // This event was not tracked as an inbound event, so trace it now.
- mTraceQueue.emplace_back(traced);
- }
+ if (!cookie) {
+ // This event was not tracked as an inbound event, so trace it now.
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
+ [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
+ traced);
+ }
- // The vsyncId only has meaning if the event is targeting a window.
- const int32_t windowId = dispatchEntry.windowId.value_or(0);
- const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
+ // The vsyncId only has meaning if the event is targeting a window.
+ const int32_t windowId = dispatchEntry.windowId.value_or(0);
+ const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
- mDispatchTraceQueue.emplace_back(std::move(traced), dispatchEntry.deliveryTime,
- dispatchEntry.resolvedFlags, dispatchEntry.targetUid,
- vsyncId, windowId, dispatchEntry.transform,
- dispatchEntry.rawTransform);
- } // release lock
-
- mThreadWakeCondition.notify_all();
+ mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime,
+ dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId,
+ windowId, dispatchEntry.transform, dispatchEntry.rawTransform,
+ /*hmac=*/{}});
}
std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
- return static_cast<const EventTrackerImpl&>(cookie).mLockedState;
-}
-
-void InputTracer::threadLoop() {
- androidSetThreadName("InputTracer");
-
- std::vector<const EventState> eventsToTrace;
- std::vector<const WindowDispatchArgs> dispatchEventsToTrace;
-
- while (true) {
- { // acquire lock
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- // Wait until we need to process more events or exit.
- mThreadWakeCondition.wait(lock, [&]() REQUIRES(mLock) {
- return mThreadExit || !mTraceQueue.empty() || !mDispatchTraceQueue.empty();
- });
- if (mThreadExit) {
- return;
- }
-
- mTraceQueue.swap(eventsToTrace);
- mDispatchTraceQueue.swap(dispatchEventsToTrace);
- } // release lock
-
- // Trace the events into the backend without holding the lock to reduce the amount of
- // work performed in the critical section.
- writeEventsToBackend(eventsToTrace, dispatchEventsToTrace);
- eventsToTrace.clear();
- dispatchEventsToTrace.clear();
- }
-}
-
-void InputTracer::writeEventsToBackend(
- const std::vector<const EventState>& events,
- const std::vector<const WindowDispatchArgs>& dispatchEvents) {
- for (const auto& event : events) {
- if (auto* motion = std::get_if<TracedMotionEvent>(&event.event); motion != nullptr) {
- mBackend->traceMotionEvent(*motion);
- } else {
- mBackend->traceKeyEvent(std::get<TracedKeyEvent>(event.event));
- }
- }
-
- for (const auto& dispatchArgs : dispatchEvents) {
- mBackend->traceWindowDispatch(dispatchArgs);
- }
+ return static_cast<const EventTrackerImpl&>(cookie).mState;
}
// --- InputTracer::EventTrackerImpl ---
InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
- : mTracer(tracer), mLockedState(event) {}
+ : mTracer(tracer), mState(event) {}
InputTracer::EventTrackerImpl::~EventTrackerImpl() {
- {
- std::scoped_lock lock(mTracer.mLock);
- if (!mLockedState) {
- // This event has already been written to the trace as expected.
- return;
- }
- // We're still holding on to the state, which means it hasn't yet been written to the trace.
- // Write it to the trace now.
- // TODO(b/210460522): Determine why/where the event is being destroyed before
- // eventProcessingComplete() is called.
- mTracer.mTraceQueue.emplace_back(std::move(*mLockedState));
- mLockedState.reset();
- } // release lock
-
- mTracer.mThreadWakeCondition.notify_all();
+ if (!mState) {
+ // This event has already been written to the trace as expected.
+ return;
+ }
+ // We're still holding on to the state, which means it hasn't yet been written to the trace.
+ // Write it to the trace now.
+ // TODO(b/210460522): Determine why/where the event is being destroyed before
+ // eventProcessingComplete() is called.
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
+ [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
+ mState->event);
+ mState.reset();
}
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 9fe395d..c8b25c9 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -18,14 +18,7 @@
#include "InputTracerInterface.h"
-#include <android-base/thread_annotations.h>
-#include <gui/WindowInfo.h>
-
#include <memory>
-#include <mutex>
-#include <thread>
-#include <unordered_set>
-#include <vector>
#include "../Entry.h"
#include "InputTracingBackendInterface.h"
@@ -35,17 +28,16 @@
/**
* The tracer implementation for InputDispatcher.
*
- * InputTracer is thread-safe, so it can be called from any thread. Upon construction, InputTracer
- * will start its own thread that it uses for write events into the tracing backend. That is the
- * one and only thread that will interact with the tracing backend, since the Perfetto backend
- * uses thread-local storage.
+ * InputTracer's responsibility is to keep track of events as they are processed by InputDispatcher,
+ * and to write the events to the tracing backend when enough information is collected. InputTracer
+ * is not thread-safe.
*
* See the documentation in InputTracerInterface for the API surface.
*/
class InputTracer : public InputTracerInterface {
public:
explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>);
- ~InputTracer() override;
+ ~InputTracer() = default;
InputTracer(const InputTracer&) = delete;
InputTracer& operator=(const InputTracer&) = delete;
@@ -55,10 +47,6 @@
void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;
private:
- std::mutex mLock;
- std::thread mTracerThread;
- bool mThreadExit GUARDED_BY(mLock){false};
- std::condition_variable mThreadWakeCondition;
std::unique_ptr<InputTracingBackendInterface> mBackend;
// The state of a tracked event.
@@ -67,14 +55,12 @@
// TODO(b/210460522): Add additional args for tracking event sensitivity and
// dispatch target UIDs.
};
- std::vector<const EventState> mTraceQueue GUARDED_BY(mLock);
- using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
- std::vector<const WindowDispatchArgs> mDispatchTraceQueue GUARDED_BY(mLock);
- // Provides thread-safe access to the state from an event tracker cookie.
- std::optional<EventState>& getState(const EventTrackerInterface&) REQUIRES(mLock);
+ // Get the event state associated with a tracking cookie.
+ std::optional<EventState>& getState(const EventTrackerInterface&);
- // Implementation of the event tracker cookie.
+ // Implementation of the event tracker cookie. The cookie holds the event state directly for
+ // convenience to avoid the overhead of tracking the state separately in InputTracer.
class EventTrackerImpl : public EventTrackerInterface {
public:
explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
@@ -84,16 +70,10 @@
InputTracer& mTracer;
// This event tracker cookie will only hold the state as long as it has not been written
// to the trace. The state is released when the event is written to the trace.
- mutable std::optional<EventState> mLockedState;
+ mutable std::optional<EventState> mState;
- // Only allow InputTracer access to the locked state through getTrackerState() to ensure
- // that the InputTracer lock is held when this is accessed.
friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
};
-
- void threadLoop();
- void writeEventsToBackend(const std::vector<const EventState>& events,
- const std::vector<const WindowDispatchArgs>& dispatchEvents);
};
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index bc47817..b0eadfe 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -82,10 +82,10 @@
virtual ~InputTracingBackendInterface() = default;
/** Trace a KeyEvent. */
- virtual void traceKeyEvent(const TracedKeyEvent&) const = 0;
+ virtual void traceKeyEvent(const TracedKeyEvent&) = 0;
/** Trace a MotionEvent. */
- virtual void traceMotionEvent(const TracedMotionEvent&) const = 0;
+ virtual void traceMotionEvent(const TracedMotionEvent&) = 0;
/** Trace an event being sent to a window. */
struct WindowDispatchArgs {
@@ -99,7 +99,7 @@
ui::Transform rawTransform;
std::array<uint8_t, 32> hmac;
};
- virtual void traceWindowDispatch(const WindowDispatchArgs&) const = 0;
+ virtual void traceWindowDispatch(const WindowDispatchArgs&) = 0;
};
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index 4442ad8..46ad9e1 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -63,7 +63,7 @@
});
}
-void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const {
+void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
auto tracePacket = ctx.NewTracePacket();
auto* inputEvent = tracePacket->set_android_input_event();
@@ -72,7 +72,7 @@
});
}
-void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const {
+void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
auto tracePacket = ctx.NewTracePacket();
auto* inputEvent = tracePacket->set_android_input_event();
@@ -81,7 +81,7 @@
});
}
-void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) const {
+void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
auto tracePacket = ctx.NewTracePacket();
auto* inputEventProto = tracePacket->set_android_input_event();
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
index 2777cfe..fefcfb3 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -48,9 +48,9 @@
PerfettoBackend();
~PerfettoBackend() override = default;
- void traceKeyEvent(const TracedKeyEvent&) const override;
- void traceMotionEvent(const TracedMotionEvent&) const override;
- void traceWindowDispatch(const WindowDispatchArgs&) const override;
+ void traceKeyEvent(const TracedKeyEvent&) override;
+ void traceMotionEvent(const TracedMotionEvent&) override;
+ void traceWindowDispatch(const WindowDispatchArgs&) override;
class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> {
public:
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
new file mode 100644
index 0000000..a58d52a
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "InputTracer"
+
+#include "ThreadedBackend.h"
+
+#include "InputTracingPerfettoBackend.h"
+
+#include <android-base/logging.h>
+#include <utils/AndroidThreads.h>
+
+namespace android::inputdispatcher::trace::impl {
+
+namespace {
+
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {
+ using V::operator()...;
+};
+
+} // namespace
+
+// --- ThreadedBackend ---
+
+template <typename Backend>
+ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend)
+ : mTracerThread(&ThreadedBackend::threadLoop, this), mBackend(std::move(innerBackend)) {}
+
+template <typename Backend>
+ThreadedBackend<Backend>::~ThreadedBackend() {
+ {
+ std::scoped_lock lock(mLock);
+ mThreadExit = true;
+ }
+ mThreadWakeCondition.notify_all();
+ mTracerThread.join();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event) {
+ std::scoped_lock lock(mLock);
+ mQueue.emplace_back(event);
+ mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event) {
+ std::scoped_lock lock(mLock);
+ mQueue.emplace_back(event);
+ mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
+ std::scoped_lock lock(mLock);
+ mQueue.emplace_back(dispatchArgs);
+ mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::threadLoop() {
+ androidSetThreadName("InputTracer");
+
+ std::vector<std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>> events;
+
+ while (true) {
+ { // acquire lock
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ // Wait until we need to process more events or exit.
+ mThreadWakeCondition.wait(lock, [&]() REQUIRES(mLock) {
+ return mThreadExit || !mQueue.empty();
+ });
+ if (mThreadExit) {
+ return;
+ }
+
+ mQueue.swap(events);
+ } // release lock
+
+ // Trace the events into the backend without holding the lock to reduce the amount of
+ // work performed in the critical section.
+ for (const auto& event : events) {
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend.traceMotionEvent(e); },
+ [&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e); },
+ [&](const WindowDispatchArgs& args) {
+ mBackend.traceWindowDispatch(args);
+ }},
+ event);
+ }
+ events.clear();
+ }
+}
+
+// Explicit template instantiation for the PerfettoBackend.
+template class ThreadedBackend<PerfettoBackend>;
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
new file mode 100644
index 0000000..c42f896
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
@@ -0,0 +1,59 @@
+/*
+ * 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 "InputTracingPerfettoBackend.h"
+
+#include <android-base/thread_annotations.h>
+#include <mutex>
+#include <thread>
+#include <variant>
+#include <vector>
+
+namespace android::inputdispatcher::trace::impl {
+
+/**
+ * A wrapper around an InputTracingBackend implementation that writes to the inner tracing backend
+ * from a single new thread that it creates. The new tracing thread is started when the
+ * ThreadedBackend is created, and is stopped when it is destroyed. The ThreadedBackend is
+ * thread-safe.
+ */
+template <typename Backend>
+class ThreadedBackend : public InputTracingBackendInterface {
+public:
+ ThreadedBackend(Backend&& innerBackend);
+ ~ThreadedBackend() override;
+
+ void traceKeyEvent(const TracedKeyEvent&) override;
+ void traceMotionEvent(const TracedMotionEvent&) override;
+ void traceWindowDispatch(const WindowDispatchArgs&) override;
+
+private:
+ std::mutex mLock;
+ std::thread mTracerThread;
+ bool mThreadExit GUARDED_BY(mLock){false};
+ std::condition_variable mThreadWakeCondition;
+ Backend mBackend;
+ std::vector<std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>> mQueue
+ GUARDED_BY(mLock);
+
+ using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
+
+ void threadLoop();
+};
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index b7af356..4655ee8 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -163,7 +163,7 @@
// --- FakeInputTracingBackend ---
-void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) const {
+void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedEvents.emplace(event.id, event);
@@ -171,7 +171,7 @@
mTrace->mEventTracedCondition.notify_all();
}
-void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) const {
+void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedEvents.emplace(event.id, event);
@@ -179,7 +179,7 @@
mTrace->mEventTracedCondition.notify_all();
}
-void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) const {
+void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedWindowDispatches.push_back(args);
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
index 108419a..1b3613d 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.h
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -83,9 +83,9 @@
private:
std::shared_ptr<VerifyingTrace> mTrace;
- void traceKeyEvent(const trace::TracedKeyEvent& entry) const override;
- void traceMotionEvent(const trace::TracedMotionEvent& entry) const override;
- void traceWindowDispatch(const WindowDispatchArgs& entry) const override;
+ void traceKeyEvent(const trace::TracedKeyEvent& entry) override;
+ void traceMotionEvent(const trace::TracedMotionEvent& entry) override;
+ void traceWindowDispatch(const WindowDispatchArgs& entry) override;
};
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index cfa0339..776bcd3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -92,7 +92,7 @@
mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))),
mUpdateDeviceProductInfoOnHotplugReconnect(
sysprop::update_device_product_info_on_hotplug_reconnect(false)),
- mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, false)) {}
+ mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, true)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
: HWComposer(Hwc2::Composer::create(composerServiceName)) {}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index a145e59..ff88d71 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -17,7 +17,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <binder/IPCThreadState.h>
-#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/threads.h>
@@ -66,7 +65,7 @@
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = expectedVsyncTime;
- mVsync.scheduledFrameTime.reset();
+ mVsync.scheduledFrameTimeOpt.reset();
}
const auto vsyncId = VsyncId{mVsync.tokenManager->generateTokenForPredictions(
@@ -122,7 +121,7 @@
std::placeholders::_3),
"sf");
if (reschedule) {
- mVsync.scheduledFrameTime =
+ mVsync.scheduledFrameTimeOpt =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
.lastVsync = mVsync.lastCallbackTime.ns()});
@@ -140,7 +139,7 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
- mVsync.scheduledFrameTime =
+ mVsync.scheduledFrameTimeOpt =
mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
.lastVsync = mVsync.lastCallbackTime.ns()});
@@ -193,22 +192,20 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
- mVsync.scheduledFrameTime =
+ mVsync.scheduledFrameTimeOpt =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
.lastVsync = mVsync.lastCallbackTime.ns()});
}
-auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
+std::optional<scheduler::ScheduleResult> MessageQueue::getScheduledFrameResult() const {
if (mHandler->isFramePending()) {
- return Clock::now();
+ return scheduler::ScheduleResult{TimePoint::now(), mHandler->getExpectedVsyncTime()};
}
-
std::lock_guard lock(mVsync.mutex);
- if (const auto time = mVsync.scheduledFrameTime) {
- return Clock::time_point(std::chrono::nanoseconds(*time));
+ if (const auto scheduledFrameTimeline = mVsync.scheduledFrameTimeOpt) {
+ return scheduledFrameTimeline;
}
-
return std::nullopt;
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index edb424b..c5fc371 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -76,8 +76,7 @@
virtual void scheduleConfigure() = 0;
virtual void scheduleFrame() = 0;
- using Clock = std::chrono::steady_clock;
- virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0;
+ virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0;
};
namespace impl {
@@ -95,7 +94,9 @@
explicit Handler(MessageQueue& queue) : mQueue(queue) {}
void handleMessage(const Message& message) override;
- bool isFramePending() const;
+ virtual TimePoint getExpectedVsyncTime() const { return mExpectedVsyncTime.load(); }
+
+ virtual bool isFramePending() const;
virtual void dispatchFrame(VsyncId, TimePoint expectedVsyncTime);
};
@@ -124,7 +125,7 @@
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
TimePoint lastCallbackTime GUARDED_BY(mutex);
- std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex);
+ std::optional<scheduler::ScheduleResult> scheduledFrameTimeOpt GUARDED_BY(mutex);
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
@@ -150,7 +151,7 @@
void scheduleConfigure() override;
void scheduleFrame() override;
- std::optional<Clock::time_point> getScheduledFrameTime() const override;
+ std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 2cc6b34..09f75fd 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -109,9 +109,9 @@
void initVsync(frametimeline::TokenManager&, std::chrono::nanoseconds workDuration);
- using Impl::getScheduledFrameTime;
using Impl::setDuration;
+ using Impl::getScheduledFrameResult;
using Impl::scheduleConfigure;
using Impl::scheduleFrame;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index ed8f8fe..0c43ffb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -21,11 +21,15 @@
#include <string>
#include <ftl/mixins.h>
+#include <scheduler/Time.h>
#include <utils/Timers.h>
namespace android::scheduler {
-using ScheduleResult = std::optional<nsecs_t>;
+struct ScheduleResult {
+ TimePoint callbackTime;
+ TimePoint vsyncTime;
+};
enum class CancelResult { Cancelled, TooLate, Error };
@@ -124,10 +128,12 @@
*
* \param [in] token The callback to schedule.
* \param [in] scheduleTiming The timing information for this schedule call
- * \return The expected callback time if a callback was scheduled.
+ * \return The expected callback time if a callback was scheduled,
+ * along with VSYNC time for the callback scheduled.
* std::nullopt if the callback is not registered.
*/
- virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+ virtual std::optional<ScheduleResult> schedule(CallbackToken token,
+ ScheduleTiming scheduleTiming) = 0;
/*
* Update the timing information for a scheduled callback.
@@ -135,10 +141,12 @@
*
* \param [in] token The callback to schedule.
* \param [in] scheduleTiming The timing information for this schedule call
- * \return The expected callback time if a callback was scheduled.
+ * \return The expected callback time if a callback was scheduled,
+ * along with VSYNC time for the callback scheduled.
* std::nullopt if the callback is not registered.
*/
- virtual ScheduleResult update(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+ virtual std::optional<ScheduleResult> update(CallbackToken token,
+ ScheduleTiming scheduleTiming) = 0;
/* Cancels a scheduled callback, if possible.
*
@@ -168,10 +176,10 @@
VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
// See documentation for VSyncDispatch::schedule.
- ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
+ std::optional<ScheduleResult> schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
// See documentation for VSyncDispatch::update.
- ScheduleResult update(VSyncDispatch::ScheduleTiming scheduleTiming);
+ std::optional<ScheduleResult> update(VSyncDispatch::ScheduleTiming scheduleTiming);
// See documentation for VSyncDispatch::cancel.
CancelResult cancel();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index b92fa24..84ccf8e 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -38,9 +38,10 @@
namespace {
-nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
- const VSyncDispatch::ScheduleTiming& timing) {
- return nextVsyncTime - timing.readyDuration - timing.workDuration;
+ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime,
+ const VSyncDispatch::ScheduleTiming& timing) {
+ return {TimePoint::fromNs(nextVsyncTime - timing.readyDuration - timing.workDuration),
+ TimePoint::fromNs(nextVsyncTime)};
}
} // namespace
@@ -115,14 +116,15 @@
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
- return nextWakeupTime;
+ return ScheduleResult{TimePoint::fromNs(nextWakeupTime), TimePoint::fromNs(nextVsyncTime)};
}
-nsecs_t VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
+ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
mWorkloadUpdateInfo = timing;
const auto armedInfo = update(tracker, now, timing, mArmedInfo);
- return armedInfo.mActualWakeupTime;
+ return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
+ TimePoint::fromNs(armedInfo.mActualVsyncTime)};
}
bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
@@ -383,14 +385,14 @@
}
}
-ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
- ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::schedule(CallbackToken token,
+ ScheduleTiming scheduleTiming) {
std::lock_guard lock(mMutex);
return scheduleLocked(token, scheduleTiming);
}
-ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token,
- ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::scheduleLocked(
+ CallbackToken token, ScheduleTiming scheduleTiming) {
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
return {};
@@ -405,10 +407,7 @@
return callback->addPendingWorkloadUpdate(*mTracker, now, scheduleTiming);
}
- const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now);
- if (!result.has_value()) {
- return {};
- }
+ const auto result = callback->schedule(scheduleTiming, *mTracker, now);
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
@@ -417,7 +416,8 @@
return result;
}
-ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::update(CallbackToken token,
+ ScheduleTiming scheduleTiming) {
std::lock_guard lock(mMutex);
const auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
@@ -494,14 +494,16 @@
if (mToken) mDispatch->unregisterCallback(*mToken);
}
-ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncCallbackRegistration::schedule(
+ VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mToken) {
return std::nullopt;
}
return mDispatch->schedule(*mToken, scheduleTiming);
}
-ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncCallbackRegistration::update(
+ VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mToken) {
return std::nullopt;
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index b5ddd25..252c09c 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -69,7 +69,8 @@
// Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
// call to update()
- nsecs_t addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming);
+ ScheduleResult addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now,
+ VSyncDispatch::ScheduleTiming);
// Checks if there is a pending update to the workload, returning true if so.
bool hasPendingWorkloadUpdate() const;
@@ -128,8 +129,8 @@
CallbackToken registerCallback(Callback, std::string callbackName) final;
void unregisterCallback(CallbackToken) final;
- ScheduleResult schedule(CallbackToken, ScheduleTiming) final;
- ScheduleResult update(CallbackToken, ScheduleTiming) final;
+ std::optional<ScheduleResult> schedule(CallbackToken, ScheduleTiming) final;
+ std::optional<ScheduleResult> update(CallbackToken, ScheduleTiming) final;
CancelResult cancel(CallbackToken) final;
void dump(std::string&) const final;
@@ -147,7 +148,7 @@
void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate)
REQUIRES(mMutex);
void cancelTimer() REQUIRES(mMutex);
- ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
+ std::optional<ScheduleResult> scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 582a999..cf5f55d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2749,7 +2749,11 @@
// TODO(b/255601557) Update frameInterval per display
refreshArgs.frameInterval =
mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime());
- refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
+ const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+ const auto scheduledFrameTimeOpt = scheduledFrameResultOpt
+ ? std::optional{scheduledFrameResultOpt->callbackTime}
+ : std::nullopt;
+ refreshArgs.scheduledFrameTime = scheduledFrameTimeOpt;
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
// Store the present time just before calling to the composition engine so we could notify
// the scheduler.
@@ -4236,7 +4240,8 @@
auto hintStatus = data.hintStatus.load();
if (!expectedPresentWithinTimeout) {
- if (hintStatus != NotifyExpectedPresentHintStatus::Sent ||
+ if ((hintStatus != NotifyExpectedPresentHintStatus::Sent &&
+ hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnTx) ||
(timeoutOpt && timeoutOpt->ns() == 0)) {
// Send the hint immediately if timeout, as the hint gets
// delayed otherwise, as the frame is scheduled close
@@ -4245,11 +4250,16 @@
.compare_exchange_strong(hintStatus,
NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
scheduleNotifyExpectedPresentHint(displayId);
+ return;
}
- return;
}
}
+ if (hintStatus == NotifyExpectedPresentHintStatus::Sent &&
+ data.hintStatus.compare_exchange_strong(hintStatus,
+ NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
+ return;
+ }
if (hintStatus != NotifyExpectedPresentHintStatus::Start) {
return;
}
@@ -4257,7 +4267,8 @@
mScheduler->scheduleFrame();
}
-void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
+void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId,
+ VsyncId vsyncId) {
auto itr = mNotifyExpectedPresentMap.find(displayId);
if (itr == mNotifyExpectedPresentMap.end()) {
return;
@@ -4266,10 +4277,30 @@
const char* const whence = __func__;
const auto sendHint = [=, this]() {
auto& data = mNotifyExpectedPresentMap.at(displayId);
- data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent);
- const auto status =
- getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp,
- data.lastFrameInterval);
+ TimePoint expectedPresentTime = data.lastExpectedPresentTimestamp;
+ if (ftl::to_underlying(vsyncId) != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ const auto predictionOpt = mFrameTimeline->getTokenManager()->getPredictionsForToken(
+ ftl::to_underlying(vsyncId));
+ const auto expectedPresentTimeOnPredictor = TimePoint::fromNs(
+ predictionOpt ? predictionOpt->presentTime : expectedPresentTime.ns());
+ const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+ const auto expectedPresentTimeOnScheduler = scheduledFrameResultOpt.has_value()
+ ? scheduledFrameResultOpt->vsyncTime
+ : TimePoint::fromNs(0);
+ expectedPresentTime =
+ std::max(expectedPresentTimeOnPredictor, expectedPresentTimeOnScheduler);
+ }
+
+ if (expectedPresentTime < TimePoint::now()) {
+ expectedPresentTime =
+ mScheduler->getVsyncSchedule()->vsyncDeadlineAfter(TimePoint::now());
+ if (mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration >
+ mScheduler->getVsyncSchedule(displayId)->period()) {
+ expectedPresentTime += mScheduler->getVsyncSchedule(displayId)->period();
+ }
+ }
+ const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
+ data.lastFrameInterval);
if (status != NO_ERROR) {
ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
displayId.value);
@@ -4287,7 +4318,11 @@
}
}));
}
- sendHint();
+ auto scheduleHintOnPresent = NotifyExpectedPresentHintStatus::ScheduleOnPresent;
+ if (itr->second.hintStatus.compare_exchange_strong(scheduleHintOnPresent,
+ NotifyExpectedPresentHintStatus::Sent)) {
+ sendHint();
+ }
}
void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
@@ -5094,6 +5129,11 @@
const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
mTransactionHandler.queueTransaction(std::move(state));
+ for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
+ if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
+ scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
+ }
+ }
setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
return NO_ERROR;
}
@@ -8803,7 +8843,11 @@
return;
}
- mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
+ const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+ const auto scheduleFrameTimeOpt = scheduledFrameResultOpt
+ ? std::optional{scheduledFrameResultOpt->callbackTime}
+ : std::nullopt;
+ mRegionSamplingThread->onCompositionComplete(scheduleFrameTimeOpt);
}
void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7da352f..678be54 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1494,7 +1494,9 @@
std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap;
void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) override
REQUIRES(kMainThreadContext);
- void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId);
+ void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId,
+ VsyncId vsyncId = VsyncId{
+ FrameTimelineInfo::INVALID_VSYNC_ID});
void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
TimePoint expectedPresentTime, Fps frameInterval,
std::optional<Period> timeoutOpt);
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index d5ec654..3eabe1f 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -117,14 +117,16 @@
mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp);
}
+ static constexpr scheduler::ScheduleResult kScheduleResult{TimePoint::fromNs(0),
+ TimePoint::fromNs(0)};
AsyncCallRecorderWithCannedReturn<
scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
scheduler::VSyncDispatch::ScheduleTiming)>
- mVSyncCallbackScheduleRecorder{0};
+ mVSyncCallbackScheduleRecorder{kScheduleResult};
AsyncCallRecorderWithCannedReturn<
scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
scheduler::VSyncDispatch::ScheduleTiming)>
- mVSyncCallbackUpdateRecorder{0};
+ mVSyncCallbackUpdateRecorder{kScheduleResult};
AsyncCallRecorderWithCannedReturn<
scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback,
std::string)>
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index f5661fc..71f9f88 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -25,6 +25,7 @@
#include "FrameTimeline.h"
#include "Scheduler/MessageQueue.h"
#include "mock/MockVSyncDispatch.h"
+#include "utils/Timers.h"
namespace android {
@@ -49,6 +50,8 @@
using MessageQueue::Handler::Handler;
MOCK_METHOD(void, dispatchFrame, (VsyncId, TimePoint), (override));
+ MOCK_METHOD(bool, isFramePending, (), (const, override));
+ MOCK_METHOD(TimePoint, getExpectedVsyncTime, (), (const override));
};
explicit TestableMessageQueue(sp<MockHandler> handler)
@@ -94,13 +97,17 @@
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
.lastVsync = 0};
- EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
+ EXPECT_FALSE(mEventQueue.getScheduledFrameResult());
- EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+ const auto timePoint = TimePoint::fromNs(1234);
+ const auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
- ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
- EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+ const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+ ASSERT_TRUE(scheduledFrameResult);
+ EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+ EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
}
TEST_F(MessageQueueTest, commitTwice) {
@@ -109,17 +116,25 @@
.readyDuration = 0,
.lastVsync = 0};
- EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+ auto timePoint = TimePoint::fromNs(1234);
+ auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
- ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
- EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+ auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+ ASSERT_TRUE(scheduledFrameResult);
+ EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+ EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
- EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
+ timePoint = TimePoint::fromNs(4567);
+ scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
- ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
- EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+ scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+ ASSERT_TRUE(scheduledFrameResult);
+ EXPECT_EQ(4567, scheduledFrameResult->callbackTime.ns());
+ EXPECT_EQ(4567, scheduledFrameResult->vsyncTime.ns());
}
TEST_F(MessageQueueTest, commitTwiceWithCallback) {
@@ -128,11 +143,15 @@
.readyDuration = 0,
.lastVsync = 0};
- EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+ const auto timePoint = TimePoint::fromNs(1234);
+ auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
- ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
- EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+ auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+ ASSERT_TRUE(scheduledFrameResult);
+ EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+ EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
constexpr TimePoint kStartTime = TimePoint::fromNs(100);
constexpr TimePoint kEndTime = kStartTime + kDuration;
@@ -148,14 +167,15 @@
EXPECT_NO_FATAL_FAILURE(
mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns()));
- EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
+ EXPECT_FALSE(mEventQueue.getScheduledFrameResult());
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
.lastVsync = kPresentTime.ns()};
-
- EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
+ scheduleResult = scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)};
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback))
+ .WillOnce(Return(scheduleResult));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
@@ -167,9 +187,24 @@
.readyDuration = 0,
.lastVsync = 0};
- EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+ const auto scheduleResult =
+ scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)};
+ EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
+TEST_F(MessageQueueTest, scheduleResultWhenFrameIsPending) {
+ const auto timePoint = TimePoint::now();
+ EXPECT_CALL(*mEventQueue.mHandler, isFramePending()).WillOnce(Return(true));
+ EXPECT_CALL(*mEventQueue.mHandler, getExpectedVsyncTime()).WillRepeatedly(Return(timePoint));
+
+ const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+
+ ASSERT_TRUE(scheduledFrameResult);
+ EXPECT_NEAR(static_cast<double>(TimePoint::now().ns()),
+ static_cast<double>(scheduledFrameResult->callbackTime.ns()), ms2ns(1));
+ EXPECT_EQ(timePoint, scheduledFrameResult->vsyncTime);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 91b9018..20a3315 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -41,6 +41,33 @@
}
protected:
+ void setTransactionState() {
+ ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
+ TransactionInfo transaction;
+ mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays,
+ transaction.flags, transaction.applyToken,
+ transaction.inputWindowCommands,
+ TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp,
+ transaction.unCachedBuffers,
+ /*HasListenerCallbacks=*/false, transaction.callbacks,
+ transaction.id, transaction.mergedTransactionIds);
+ }
+
+ struct TransactionInfo {
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags = 0;
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ InputWindowCommands inputWindowCommands;
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = false;
+ FrameTimelineInfo frameTimelineInfo{};
+ std::vector<client_cache_t> unCachedBuffers;
+ uint64_t id = static_cast<uint64_t>(-1);
+ std::vector<uint64_t> mergedTransactionIds;
+ std::vector<ListenerCallbacks> callbacks;
+ };
+
struct Compositor final : ICompositor {
explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger)
: displayId(displayId), surfaceFlinger(surfaceFlinger) {}
@@ -102,7 +129,7 @@
};
TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) {
- auto expectedPresentTime = systemTime() + ms2ns(10);
+ auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
ASSERT_NO_FATAL_FAILURE(
mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId,
TimePoint::fromNs(expectedPresentTime),
@@ -120,7 +147,7 @@
}
TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) {
- auto expectedPresentTime = systemTime() + ms2ns(10);
+ auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
{
// Very first ExpectedPresent after idle, no previous timestamp.
EXPECT_CALL(*mComposer,
@@ -139,6 +166,10 @@
{
mCompositor->committed = false;
expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+ mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+ nextAnticipatedVSyncTimeFrom(_, _))
+ .WillRepeatedly(Return(expectedPresentTime));
EXPECT_CALL(*mComposer,
notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
.WillOnce(Return(Error::NONE));
@@ -154,6 +185,10 @@
}
{
expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+ mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+ nextAnticipatedVSyncTimeFrom(_, _))
+ .WillRepeatedly(Return(expectedPresentTime));
EXPECT_CALL(*mComposer,
notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
.WillOnce(Return(Error::NONE));
@@ -168,9 +203,8 @@
ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
}
}
-
TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) {
- auto expectedPresentTime = systemTime() + ms2ns(10);
+ auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
{
// Very first ExpectedPresent after idle, no previous timestamp
mCompositor->committed = false;
@@ -185,6 +219,27 @@
ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
}
{
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ EXPECT_TRUE(
+ mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime));
+ ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
+ mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42});
+ ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
+ {
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ // Hint sent with the setTransactionState
+ setTransactionState();
+ ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
+ }
+ }
+ {
// ExpectedPresentTime is after the timeoutNs
mCompositor->committed = true;
expectedPresentTime += 2 * kFrameInterval5HzNs;
@@ -194,7 +249,7 @@
kTimeoutNs);
EXPECT_TRUE(
mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime));
- ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
+ ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42});
// Present happens notifyExpectedPresentHintStatus is Start
ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId));
@@ -259,7 +314,7 @@
}
TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
- const auto now = systemTime();
+ const auto now = TimePoint::now().ns();
auto expectedPresentTime = now;
static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
@@ -298,6 +353,10 @@
TimePoint::fromNs(expectedPresentTime),
Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs);
+ EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+ mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+ nextAnticipatedVSyncTimeFrom(_, _))
+ .WillRepeatedly(Return(expectedPresentTime));
if (callNotifyExpectedPresentHint) {
mCompositor->committed = false;
ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId))
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index eb4e84e..9b70d92 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -247,7 +247,8 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(900, *result);
+ EXPECT_EQ(900, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
}
}
@@ -260,7 +261,8 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(900, *result);
+ EXPECT_EQ(900, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
@@ -279,12 +281,14 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(900, *result);
+ EXPECT_EQ(900, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result =
mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(700, *result);
+ EXPECT_EQ(700, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
@@ -332,7 +336,8 @@
.readyDuration = 0,
.lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod, *result);
+ EXPECT_EQ(mPeriod, result->callbackTime.ns());
+ EXPECT_EQ(workDuration + mPeriod, result->vsyncTime.ns());
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -344,7 +349,8 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod - 100, *result);
+ EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod, result->vsyncTime.ns());
EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
}
@@ -357,7 +363,8 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod - 100, *result);
+ EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod, result->vsyncTime.ns());
mMockClock.advanceBy(950);
EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
}
@@ -371,7 +378,8 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod - 100, *result);
+ EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod, result->vsyncTime.ns());
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -392,7 +400,8 @@
mDispatch->schedule(cb,
{.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod - 100, *result);
+ EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod, result->vsyncTime.ns());
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -625,19 +634,22 @@
.readyDuration = 0,
.lastVsync = timestamp - mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
result = mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
.lastVsync = timestamp});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
result = mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
.lastVsync = timestamp + mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+ EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
lastTarget = timestamp;
},
"oo");
@@ -726,10 +738,12 @@
auto result =
mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(900, *result);
+ EXPECT_EQ(900, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
}
// b/1450138150
@@ -741,12 +755,14 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.advanceBy(400);
result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1200, *result);
+ EXPECT_EQ(1200, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -763,12 +779,14 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.advanceBy(400);
result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(400, *result);
+ EXPECT_EQ(400, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -784,11 +802,13 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.advanceBy(400);
result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(602, *result);
+ EXPECT_EQ(602, result->callbackTime.ns());
+ EXPECT_EQ(1002, result->vsyncTime.ns());
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
@@ -796,12 +816,14 @@
auto result =
mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
result =
mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(900, *result);
+ EXPECT_EQ(900, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -812,12 +834,14 @@
auto result =
mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
result =
mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1100, *result);
+ EXPECT_EQ(1100, result->callbackTime.ns());
+ EXPECT_EQ(3000, result->vsyncTime.ns());
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
@@ -829,11 +853,13 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
advanceToNextCallback();
}
@@ -849,11 +875,13 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(0, *result);
+ EXPECT_EQ(0, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
}
@@ -899,14 +927,16 @@
auto result =
mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.setLag(100);
mMockClock.advanceBy(620);
result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1900, *result);
+ EXPECT_EQ(1900, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
mMockClock.advanceBy(80);
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -927,14 +957,16 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.setLag(100);
mMockClock.advanceBy(620);
result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1630, *result);
+ EXPECT_EQ(1630, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
mMockClock.advanceBy(80);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -954,14 +986,16 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.setLag(100);
mMockClock.advanceBy(620);
result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.advanceBy(80);
ASSERT_EQ(1, cb.mCalls.size());
@@ -982,10 +1016,12 @@
auto result =
mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1900, *result);
+ EXPECT_EQ(1900, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -1009,10 +1045,12 @@
auto result =
mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1900, *result);
+ EXPECT_EQ(1900, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -1045,10 +1083,12 @@
auto result =
mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(600, *result);
+ EXPECT_EQ(600, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(610, *result);
+ EXPECT_EQ(610, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.setLag(100);
mMockClock.advanceBy(700);
@@ -1072,7 +1112,8 @@
mDispatch->schedule(cb,
{.workDuration = 70, .readyDuration = 30, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(900, *result);
+ EXPECT_EQ(900, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1138,12 +1179,14 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.advanceBy(300);
result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(1200, *result);
+ EXPECT_EQ(1200, result->callbackTime.ns());
+ EXPECT_EQ(2000, result->vsyncTime.ns());
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1159,12 +1202,14 @@
auto result =
mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(500, *result);
+ EXPECT_EQ(500, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
mMockClock.advanceBy(300);
result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(300, *result);
+ EXPECT_EQ(300, result->callbackTime.ns());
+ EXPECT_EQ(1000, result->vsyncTime.ns());
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1197,9 +1242,11 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ const auto scheduleResult =
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -1220,9 +1267,11 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
- *mStubTracker.get(), now)
- .has_value());
+ const auto scheduleResult =
+ entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
+ *mStubTracker, now);
+ EXPECT_EQ(9500, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(10000, scheduleResult.vsyncTime.ns());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(9500));
@@ -1243,9 +1292,11 @@
},
mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ const auto scheduleResult =
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -1275,17 +1326,19 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- entry.update(*mStubTracker.get(), 0);
+ entry.update(*mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ const auto scheduleResult =
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
auto wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
- entry.update(*mStubTracker.get(), 0);
+ entry.update(*mStubTracker, 0);
wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(920));
@@ -1294,9 +1347,10 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ EXPECT_EQ(900,
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker.get(), 0)
+ .callbackTime.ns());
entry.update(*mStubTracker.get(), 0);
auto const wakeup = entry.wakeupTime();
@@ -1307,26 +1361,32 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ auto scheduleResult =
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+
+ EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(1800, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ scheduleResult = entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(1950, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
- *mStubTracker.get(), 0)
- .has_value());
+ scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
+ *mStubTracker, 0);
+ EXPECT_EQ(1800, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
}
@@ -1349,32 +1409,48 @@
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ auto scheduleResult =
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
entry.executing(); // 1000 is executing
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(1800, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
- VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ VSyncDispatchTimerQueueEntry entry("test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_EQ(900,
+ entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0)
+ .callbackTime.ns());
+ EXPECT_EQ(800,
+ entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0)
+ .callbackTime.ns());
+ EXPECT_EQ(950,
+ entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0)
+ .callbackTime.ns());
+ {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true);
+ EXPECT_EQ(0,
+ entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0)
+ .callbackTime.ns());
+ }
+ {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false);
+ EXPECT_EQ(800,
+ entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
+ *mStubTracker, 0)
+ .callbackTime.ns());
+ }
}
TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdateAndDontSkip) {
@@ -1389,7 +1465,7 @@
.readyDuration = 0,
.lastVsync = 400});
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
- entry.update(*mStubTracker.get(), 0);
+ entry.update(*mStubTracker, 0);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
}
@@ -1409,9 +1485,11 @@
},
mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
- *mStubTracker.get(), 0)
- .has_value());
+ const auto scheduleResult =
+ entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
+ *mStubTracker, 0);
+ EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+ EXPECT_EQ(mPeriod, scheduleResult.vsyncTime.ns());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
index dc32ff9..1dc2ef4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
@@ -29,8 +29,10 @@
MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
- MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
- MOCK_METHOD(scheduler::ScheduleResult, update, (CallbackToken, ScheduleTiming), (override));
+ MOCK_METHOD(std::optional<scheduler::ScheduleResult>, schedule, (CallbackToken, ScheduleTiming),
+ (override));
+ MOCK_METHOD(std::optional<scheduler::ScheduleResult>, update, (CallbackToken, ScheduleTiming),
+ (override));
MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));
};