Merge "libgui: Add wb_consumer_base_owns_bq flag" into main
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index c44e540..ef2fa4d 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -392,7 +392,16 @@
}
}
-Status ServiceManager::getService(const std::string& name, os::Service* outService) {
+Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
+
+ *outBinder = tryGetBinder(name, true);
+ // returns ok regardless of result for legacy reasons
+ return Status::ok();
+}
+
+Status ServiceManager::getService2(const std::string& name, os::Service* outService) {
SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 18bae68..0d666c6 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,7 +44,8 @@
~ServiceManager();
// getService will try to start any services it cannot find
- binder::Status getService(const std::string& name, os::Service* outService) override;
+ binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;
+ binder::Status getService2(const std::string& name, os::Service* outService) override;
binder::Status checkService(const std::string& name, os::Service* outService) override;
binder::Status addService(const std::string& name, const sp<IBinder>& binder,
bool allowIsolated, int32_t dumpPriority) override;
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 9d22641..95f459f 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -155,8 +155,11 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service outA;
- EXPECT_TRUE(sm->getService("foo", &outA).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &outA).isOk());
EXPECT_EQ(serviceA, outA.get<Service::Tag::binder>());
+ sp<IBinder> outBinderA;
+ EXPECT_TRUE(sm->getService("foo", &outBinderA).isOk());
+ EXPECT_EQ(serviceA, outBinderA);
// serviceA should be overwritten by serviceB
sp<IBinder> serviceB = getBinder();
@@ -164,8 +167,11 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service outB;
- EXPECT_TRUE(sm->getService("foo", &outB).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &outB).isOk());
EXPECT_EQ(serviceB, outB.get<Service::Tag::binder>());
+ sp<IBinder> outBinderB;
+ EXPECT_TRUE(sm->getService("foo", &outBinderB).isOk());
+ EXPECT_EQ(serviceB, outBinderB);
}
TEST(AddService, NoPermissions) {
@@ -188,16 +194,22 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service out;
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(service, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(service, outBinder);
}
TEST(GetService, NonExistant) {
auto sm = getPermissiveServiceManager();
Service out;
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(nullptr, outBinder);
}
TEST(GetService, NoPermissionsForGettingService) {
@@ -205,7 +217,7 @@
EXPECT_CALL(*access, getCallingContext()).WillRepeatedly(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*access, canFind(_, _)).WillRepeatedly(Return(false));
sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
@@ -214,22 +226,28 @@
Service out;
// returns nullptr but has OK status for legacy compatibility
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(nullptr, outBinder);
}
TEST(GetService, AllowedFromIsolated) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
EXPECT_CALL(*access, getCallingContext())
- // something adds it
- .WillOnce(Return(Access::CallingContext{}))
- // next call is from isolated app
- .WillOnce(Return(Access::CallingContext{
- .uid = AID_ISOLATED_START,
- }));
+ // something adds it
+ .WillOnce(Return(Access::CallingContext{}))
+ // next calls is from isolated app
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }))
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*access, canFind(_, _)).WillRepeatedly(Return(true));
sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
@@ -238,20 +256,26 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service out;
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(service, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(service, outBinder);
}
TEST(GetService, NotAllowedFromIsolated) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
EXPECT_CALL(*access, getCallingContext())
- // something adds it
- .WillOnce(Return(Access::CallingContext{}))
- // next call is from isolated app
- .WillOnce(Return(Access::CallingContext{
- .uid = AID_ISOLATED_START,
- }));
+ // something adds it
+ .WillOnce(Return(Access::CallingContext{}))
+ // next calls is from isolated app
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }))
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
// TODO(b/136023468): when security check is first, this should be called first
@@ -264,8 +288,11 @@
Service out;
// returns nullptr but has OK status for legacy compatibility
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(nullptr, outBinder);
}
TEST(ListServices, NoPermissions) {
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 0bf3cad..54f687b 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -33,10 +33,19 @@
sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() {
return mTheRealServiceManager;
}
+
binder::Status BackendUnifiedServiceManager::getService(const ::std::string& name,
- os::Service* _out) {
+ sp<IBinder>* _aidl_return) {
os::Service service;
- binder::Status status = mTheRealServiceManager->getService(name, &service);
+ binder::Status status = getService2(name, &service);
+ *_aidl_return = service.get<os::Service::Tag::binder>();
+ return status;
+}
+
+binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name,
+ os::Service* _out) {
+ os::Service service;
+ binder::Status status = mTheRealServiceManager->getService2(name, &service);
toBinderService(service, _out);
return status;
}
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 4715be4..f5d7e66 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -26,7 +26,8 @@
explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl);
sp<os::IServiceManager> getImpl();
- binder::Status getService(const ::std::string& name, os::Service* out) override;
+ binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
+ binder::Status getService2(const ::std::string& name, os::Service* out) override;
binder::Status checkService(const ::std::string& name, os::Service* out) override;
binder::Status addService(const ::std::string& name, const sp<IBinder>& service,
bool allowIsolated, int32_t dumpPriority) override;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 12a18f2..8b80aed 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -143,7 +143,7 @@
// mUnifiedServiceManager->getService so that it can be overridden in ServiceManagerHostShim.
virtual Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
Service service;
- Status status = mUnifiedServiceManager->getService(name, &service);
+ Status status = mUnifiedServiceManager->getService2(name, &service);
*_aidl_return = service.get<Service::Tag::binder>();
return status;
}
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ac95188..1d1f84f 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -60,9 +60,23 @@
* exists for legacy purposes.
*
* Returns null if the service does not exist.
+ *
+ * @deprecated TODO(b/355394904): Use getService2 instead.
*/
@UnsupportedAppUsage
- Service getService(@utf8InCpp String name);
+ @nullable IBinder getService(@utf8InCpp String name);
+
+ /**
+ * Retrieve an existing service called @a name from the
+ * service manager.
+ *
+ * This is the same as checkService (returns immediately) but
+ * exists for legacy purposes.
+ *
+ * Returns an enum Service that can be of different types. The
+ * enum value is null if the service does not exist.
+ */
+ Service getService2(@utf8InCpp String name);
/**
* Retrieve an existing service called @a name from the service
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 201dfbc..be99065 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -118,7 +118,12 @@
class ServiceManagerProxyToNative : public android::os::BnServiceManager {
public:
ServiceManagerProxyToNative(const sp<android::os::IServiceManager>& impl) : mImpl(impl) {}
- android::binder::Status getService(const std::string&, android::os::Service*) override {
+ android::binder::Status getService(const std::string&,
+ android::sp<android::IBinder>*) override {
+ // We can't send BpBinder for regular binder over RPC.
+ return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+ }
+ android::binder::Status getService2(const std::string&, android::os::Service*) override {
// We can't send BpBinder for regular binder over RPC.
return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
}
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 5a9644b..b97cee3 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,6 +1,8 @@
# Bug component: 1075131
+carlosmr@google.com
chrisforbes@google.com
+jshargo@google.com
jreck@google.com
file:/services/surfaceflinger/OWNERS
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index f269f10..9d44cc9 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -83,3 +83,11 @@
bug: "354273690"
is_fixed_read_only: true
} # wb_surface_connect_methods
+
+flag {
+ name: "bq_consumer_attach_callback"
+ namespace: "core_graphics"
+ description: "Controls IProducerListener to have consumer side attach callback"
+ bug: "353202582"
+ is_fixed_read_only: true
+} # bq_consumer_attach_callback
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 1aed8a8..93785f6 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -72,7 +72,7 @@
inline std::optional<std::string> getDeviceTypeAssociation() const {
return mAssociatedDeviceType;
}
- inline std::optional<DisplayViewport> getAssociatedViewport() const {
+ inline virtual std::optional<DisplayViewport> getAssociatedViewport() const {
return mAssociatedViewport;
}
inline bool hasMic() const { return mHasMic; }
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 2108488..3fc370c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -25,9 +25,6 @@
namespace android {
-class CursorButtonAccumulator;
-class CursorScrollAccumulator;
-
/* Keeps track of cursor movements. */
class CursorMotionAccumulator {
public:
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 846eced..b27d02d 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -17,6 +17,7 @@
#include "CursorInputMapper.h"
#include <list>
+#include <optional>
#include <string>
#include <tuple>
#include <variant>
@@ -93,38 +94,6 @@
return v;
}
-/**
- * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
- *
- * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
- * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
- * device doesn't set its associated viewport when it's configured.
- *
- * TODO(b/319217713): work out a way to avoid this fake.
- */
-class ViewportFakingInputDeviceContext : public InputDeviceContext {
-public:
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- std::optional<DisplayViewport> viewport)
- : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
-
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- ui::Rotation orientation)
- : ViewportFakingInputDeviceContext(device, eventHubId,
- createPrimaryViewport(orientation)) {}
-
- std::optional<DisplayViewport> getAssociatedViewport() const override {
- return mAssociatedViewport;
- }
-
- void setViewport(const std::optional<DisplayViewport>& viewport) {
- mAssociatedViewport = viewport;
- }
-
-private:
- std::optional<DisplayViewport> mAssociatedViewport;
-};
-
} // namespace
namespace input_flags = com::android::input::flags;
@@ -541,8 +510,9 @@
// need to be rotated.
mPropertyMap.addProperty("cursor.mode", "navigation");
mPropertyMap.addProperty("cursor.orientationAware", "1");
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
@@ -558,8 +528,9 @@
// Since InputReader works in the un-rotated coordinate space, only devices that are not
// orientation-aware are affected by display rotation.
mPropertyMap.addProperty("cursor.mode", "navigation");
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0)));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
@@ -570,7 +541,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
std::list<NotifyArgs> args =
mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
@@ -583,7 +555,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1));
@@ -595,7 +568,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0));
@@ -649,8 +623,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
// Ensure input events are generated for the secondary display.
@@ -670,8 +644,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the primary display as the display on which the pointer should be shown.
// Associate the InputDevice with the secondary display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
// With PointerChoreographer enabled, there could be a PointerController for the associated
// display even if it is different from the pointer display. So the mapper should generate an
@@ -1027,8 +1001,8 @@
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -1066,9 +1040,8 @@
mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
// Don't associate the device with the display yet.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID,
- /*viewport=*/std::nullopt);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -1082,7 +1055,7 @@
ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f);
// Now associate the device with the display, and verify that acceleration is disabled.
- deviceContext.setViewport(primaryViewport);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport));
args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
args.clear();
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index d51c708..5a3d79d 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -198,6 +198,7 @@
: InputDevice(context, id, generation, identifier) {}
MOCK_METHOD(uint32_t, getSources, (), (const, override));
+ MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const));
MOCK_METHOD(bool, isEnabled, (), ());
MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
diff --git a/services/inputflinger/tests/JoystickInputMapper_test.cpp b/services/inputflinger/tests/JoystickInputMapper_test.cpp
index ec70192..adebd72 100644
--- a/services/inputflinger/tests/JoystickInputMapper_test.cpp
+++ b/services/inputflinger/tests/JoystickInputMapper_test.cpp
@@ -16,11 +16,13 @@
#include "JoystickInputMapper.h"
+#include <list>
#include <optional>
#include <EventHub.h>
#include <NotifyArgs.h>
#include <ftl/flags.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
#include <linux/input-event-codes.h>
@@ -28,76 +30,54 @@
#include "InputMapperTest.h"
#include "TestConstants.h"
+#include "TestEventMatchers.h"
namespace android {
-namespace {
-
using namespace ftl::flag_operators;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::Return;
+using testing::VariantWith;
-} // namespace
-
-class JoystickInputMapperTest : public InputMapperTest {
+class JoystickInputMapperTest : public InputMapperUnitTest {
protected:
- static const int32_t RAW_X_MIN;
- static const int32_t RAW_X_MAX;
- static const int32_t RAW_Y_MIN;
- static const int32_t RAW_Y_MAX;
-
- static constexpr ui::LogicalDisplayId VIRTUAL_DISPLAY_ID = ui::LogicalDisplayId{1};
- static const char* const VIRTUAL_DISPLAY_UNIQUE_ID;
-
void SetUp() override {
- InputMapperTest::SetUp(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL);
- }
- void prepareAxes() {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
- }
+ InputMapperUnitTest::SetUp();
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL));
- void processAxis(JoystickInputMapper& mapper, int32_t axis, int32_t value) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, axis, value);
- }
+ // The mapper requests info on all ABS axis IDs, including ones which aren't actually used
+ // (e.g. in the range from 0x0b (ABS_BRAKE) to 0x0f (ABS_HAT0X)), so just return nullopt for
+ // all axes we don't explicitly set up below.
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_))
+ .WillRepeatedly(Return(std::nullopt));
- void processSync(JoystickInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- }
-
- void prepareVirtualDisplay(ui::Rotation orientation) {
- setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, /*width=*/400, /*height=*/500, orientation,
- VIRTUAL_DISPLAY_UNIQUE_ID, /*physicalPort=*/std::nullopt,
- ViewportType::VIRTUAL);
+ setupAxis(ABS_X, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0);
+ setupAxis(ABS_Y, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0);
}
};
-const int32_t JoystickInputMapperTest::RAW_X_MIN = -32767;
-const int32_t JoystickInputMapperTest::RAW_X_MAX = 32767;
-const int32_t JoystickInputMapperTest::RAW_Y_MIN = -32767;
-const int32_t JoystickInputMapperTest::RAW_Y_MAX = 32767;
-const char* const JoystickInputMapperTest::VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
-
TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
- prepareAxes();
- JoystickInputMapper& mapper = constructAndAddMapper<JoystickInputMapper>();
+ DisplayViewport viewport;
+ viewport.displayId = ui::LogicalDisplayId{1};
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(viewport));
+ mMapper = createInputMapper<JoystickInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
-
- prepareVirtualDisplay(ui::ROTATION_0);
+ std::list<NotifyArgs> out;
// Send an axis event
- processAxis(mapper, ABS_X, 100);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
+ out = process(EV_ABS, ABS_X, 100);
+ ASSERT_THAT(out, IsEmpty());
+ out = process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId))));
// Send another axis event
- processAxis(mapper, ABS_Y, 100);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
+ out = process(EV_ABS, ABS_Y, 100);
+ ASSERT_THAT(out, IsEmpty());
+ out = process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId))));
}
} // namespace android
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
index a796c49..6607bc7 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -78,36 +78,6 @@
return v;
}
-/**
- * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
- *
- * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
- * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
- * device doesn't set its associated viewport when it's configured.
- *
- * TODO(b/319217713): work out a way to avoid this fake.
- */
-class ViewportFakingInputDeviceContext : public InputDeviceContext {
-public:
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- std::optional<DisplayViewport> viewport)
- : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
-
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId)
- : ViewportFakingInputDeviceContext(device, eventHubId, createPrimaryViewport()) {}
-
- std::optional<DisplayViewport> getAssociatedViewport() const override {
- return mAssociatedViewport;
- }
-
- void setViewport(const std::optional<DisplayViewport>& viewport) {
- mAssociatedViewport = viewport;
- }
-
-private:
- std::optional<DisplayViewport> mAssociatedViewport;
-};
-
} // namespace
namespace vd_flags = android::companion::virtualdevice::flags;
@@ -138,8 +108,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the secondary display as the associated viewport of the mapper.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<RotaryEncoderInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
// Ensure input events are generated for the secondary display.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 6a67ac5..2e1f938 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -188,12 +188,13 @@
postMessage(sp<ConfigureHandler>::make(mCompositor));
}
-void MessageQueue::scheduleFrame() {
+void MessageQueue::scheduleFrame(Duration workDurationSlack) {
SFTRACE_CALL();
std::lock_guard lock(mVsync.mutex);
+ const auto workDuration = Duration(mVsync.workDuration.get() - workDurationSlack);
mVsync.scheduledFrameTimeOpt =
- mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ mVsync.registration->schedule({.workDuration = workDuration.ns(),
.readyDuration = 0,
.lastVsync = mVsync.lastCallbackTime.ns()});
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index c5fc371..ba1efbe 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -74,7 +74,7 @@
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0;
virtual void scheduleConfigure() = 0;
- virtual void scheduleFrame() = 0;
+ virtual void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) = 0;
virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0;
};
@@ -149,7 +149,7 @@
void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override;
void scheduleConfigure() override;
- void scheduleFrame() override;
+ void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) override;
std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ee7eda1..04491a2 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -452,7 +452,7 @@
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
- const auto minFramePeriod = minFramePeriodLocked().ns();
+ const auto minFramePeriod = minFramePeriodLocked();
auto prev = lastConfirmedPresentTime.ns();
for (auto& current : mPastExpectedPresentTimes) {
@@ -463,10 +463,10 @@
1e6f);
}
- const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod;
+ const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns();
if (minPeriodViolation) {
SFTRACE_NAME("minPeriodViolation");
- current = TimePoint::fromNs(prev + minFramePeriod);
+ current = TimePoint::fromNs(prev + minFramePeriod.ns());
prev = current.ns();
} else {
break;
@@ -477,7 +477,7 @@
const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
if (phase > 0ns) {
for (auto& timeline : mTimelines) {
- timeline.shiftVsyncSequence(phase);
+ timeline.shiftVsyncSequence(phase, minFramePeriod);
}
mPastExpectedPresentTimes.clear();
return phase;
@@ -778,8 +778,15 @@
return vsyncSequence.seq % divisor == 0;
}
-void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
+void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase, Period minFramePeriod) {
if (mLastVsyncSequence) {
+ const auto renderRate = mRenderRateOpt.value_or(Fps::fromPeriodNsecs(mIdealPeriod.ns()));
+ const auto threshold = mIdealPeriod.ns() / 2;
+ if (renderRate.getPeriodNsecs() - phase.ns() + threshold >= minFramePeriod.ns()) {
+ SFTRACE_FORMAT_INSTANT("Not-Adjusting vsync by %.2f",
+ static_cast<float>(phase.ns()) / 1e6f);
+ return;
+ }
SFTRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
mLastVsyncSequence->vsyncTime += phase.ns();
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 9e1c90b..6c8a2f2 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -104,7 +104,7 @@
void freeze(TimePoint lastVsync);
std::optional<TimePoint> validUntil() const { return mValidUntil; }
bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
- void shiftVsyncSequence(Duration phase);
+ void shiftVsyncSequence(Duration phase, Period minFramePeriod);
void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
enum class VsyncOnTimeline {
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 38cb446..2185bb0 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -55,15 +55,11 @@
std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
- // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
- TimePoint pastVsyncTime(Period minFramePeriod) const;
-
- // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
- const FenceTimePtr& presentFenceForPreviousFrame() const {
- return mPresentFences.front().fenceTime;
- }
+ // Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead.
+ const FenceTimePtr& presentFenceForPreviousFrame() const;
bool isFramePending() const { return mFramePending; }
+ bool wouldBackpressureHwc() const { return mWouldBackpressureHwc; }
bool didMissFrame() const { return mFrameMissed; }
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
FrameTime lastSignaledFrameTime() const { return mLastSignaledFrameTime; }
@@ -72,7 +68,7 @@
explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
- bool wouldPresentEarly(Period minFramePeriod) const;
+ bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const;
// Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
@@ -81,8 +77,7 @@
void addFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime,
TimePoint expectedPresentTime) {
- mFenceWithFenceTimes.next() = {std::move(presentFence), presentFenceTime,
- expectedPresentTime};
+ mPresentFences.next() = {std::move(presentFence), presentFenceTime, expectedPresentTime};
}
VsyncId mVsyncId;
@@ -94,8 +89,9 @@
TracedOrdinal<bool> mFrameMissed;
TracedOrdinal<bool> mHwcFrameMissed;
TracedOrdinal<bool> mGpuFrameMissed;
+ bool mWouldBackpressureHwc = false;
- struct FenceWithFenceTime {
+ struct PresentFence {
sp<Fence> fence = Fence::NO_FENCE;
FenceTimePtr fenceTime = FenceTime::NO_FENCE;
TimePoint expectedPresentTime = TimePoint();
@@ -106,9 +102,10 @@
// VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
// `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
// signaled by now (unless that frame missed).
- FenceWithFenceTime presentFenceForPastVsync(Period minFramePeriod) const;
- std::array<FenceWithFenceTime, 2> mPresentFences;
- utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes;
+ std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence(
+ Period vsyncPeriod, Period minFramePeriod) const;
+ std::array<PresentFence, 2> mPresentFencesLegacy;
+ utils::RingBuffer<PresentFence, 5> mPresentFences;
FrameTime mLastSignaledFrameTime;
@@ -120,19 +117,6 @@
static_assert(N > 1);
return expectedFrameDuration() > (N - 1) * minFramePeriod;
}
-
- FenceWithFenceTime pastVsyncTimePtr() const {
- FenceWithFenceTime pastFenceWithFenceTime;
- for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) {
- const auto& fenceWithFenceTime = mFenceWithFenceTimes[i];
- // TODO(b/354007767) Fix the below condition to avoid frame drop
- if (fenceWithFenceTime.expectedPresentTime > mFrameBeginTime) {
- return pastFenceWithFenceTime;
- }
- pastFenceWithFenceTime = fenceWithFenceTime;
- }
- return pastFenceWithFenceTime;
- }
};
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
@@ -155,7 +139,7 @@
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
- std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod,
+ std::optional<TimePoint> computeEarliestPresentTime(Period vsyncPeriod, Period minFramePeriod,
Duration hwcMinWorkDuration);
// TODO(b/241285191): Merge with FrameTargeter::endFrame.
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 1d248fb..8adf2a6 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -18,8 +18,10 @@
#include <common/trace.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/IVsyncSource.h>
+#include <utils/Log.h>
namespace android::scheduler {
+using namespace std::chrono_literals;
FrameTarget::FrameTarget(const std::string& displayLabel)
: mFramePending("PrevFramePending " + displayLabel, false),
@@ -27,32 +29,50 @@
mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
-TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const {
- // TODO(b/267315508): Generalize to N VSYNCs.
- const int shift = static_cast<int>(targetsVsyncsAhead<2>(minFramePeriod));
- return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
-}
-
-FrameTarget::FenceWithFenceTime FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
- if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
- return pastVsyncTimePtr();
+std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence>
+FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const {
+ if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
+ return {true, mPresentFencesLegacy[i]};
}
- const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
- return mPresentFences[i];
+
+ bool wouldBackpressure = true;
+ auto expectedPresentTime = mExpectedPresentTime;
+ for (size_t i = mPresentFences.size(); i != 0; --i) {
+ const auto& fence = mPresentFences[i - 1];
+
+ if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) {
+ wouldBackpressure = false;
+ }
+
+ if (fence.expectedPresentTime <= mFrameBeginTime) {
+ return {wouldBackpressure, fence};
+ }
+
+ expectedPresentTime = fence.expectedPresentTime;
+ }
+ return {wouldBackpressure, PresentFence{}};
}
-bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
- // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
- // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
-
- // TODO(b/267315508): Generalize to N VSYNCs.
- const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter();
- if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) {
+bool FrameTarget::wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const {
+ if (targetsVsyncsAhead<3>(minFramePeriod)) {
return true;
}
- const auto fence = presentFenceForPastVsync(minFramePeriod).fenceTime;
- return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ const auto [wouldBackpressure, fence] =
+ expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
+
+ return !wouldBackpressure ||
+ (fence.fenceTime->isValid() &&
+ fence.fenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING);
+}
+
+const FenceTimePtr& FrameTarget::presentFenceForPreviousFrame() const {
+ if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ return mPresentFences.back().fenceTime;
+ }
+
+ return mPresentFencesLegacy.front().fenceTime;
}
void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
@@ -86,27 +106,39 @@
}
if (!mSupportsExpectedPresentTime) {
- mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration);
+ mEarliestPresentTime =
+ computeEarliestPresentTime(vsyncPeriod, minFramePeriod, args.hwcMinWorkDuration);
}
SFTRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
- FenceWithFenceTime pastPresentFence = presentFenceForPastVsync(minFramePeriod);
+ const auto [wouldBackpressure, fence] =
+ expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
// In cases where the present fence is about to fire, give it a small grace period instead of
// giving up on the frame.
- //
- // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
- // approximately equal, not whether backpressure propagation is enabled.
- const int graceTimeForPresentFenceMs = static_cast<int>(
- mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
+ const int graceTimeForPresentFenceMs = [&] {
+ const bool considerBackpressure =
+ mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu);
+
+ if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ return static_cast<int>(considerBackpressure);
+ }
+
+ if (!wouldBackpressure || !considerBackpressure) {
+ return 0;
+ }
+
+ return static_cast<int>((std::abs(fence.expectedPresentTime.ns() - mFrameBeginTime.ns()) <=
+ Duration(1ms).ns()));
+ }();
// Pending frames may trigger backpressure propagation.
const auto& isFencePending = *isFencePendingFuncPtr;
- mFramePending = pastPresentFence.fenceTime != FenceTime::NO_FENCE &&
- isFencePending(pastPresentFence.fenceTime, graceTimeForPresentFenceMs);
+ mFramePending = fence.fenceTime != FenceTime::NO_FENCE &&
+ isFencePending(fence.fenceTime, graceTimeForPresentFenceMs);
// A frame is missed if the prior frame is still pending. If no longer pending, then we still
// count the frame as missed if the predicted present time was further in the past than when the
@@ -114,10 +146,10 @@
// than a typical frame duration, but should not be so small that it reports reasonable drift as
// a missed frame.
mFrameMissed = mFramePending || [&] {
- const nsecs_t pastPresentTime = pastPresentFence.fenceTime->getSignalTime();
+ const nsecs_t pastPresentTime = fence.fenceTime->getSignalTime();
if (pastPresentTime < 0) return false;
mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
- .expectedPresentTime = pastPresentFence.expectedPresentTime};
+ .expectedPresentTime = fence.expectedPresentTime};
const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
}();
@@ -128,11 +160,14 @@
if (mFrameMissed) mFrameMissedCount++;
if (mHwcFrameMissed) mHwcFrameMissedCount++;
if (mGpuFrameMissed) mGpuFrameMissedCount++;
+
+ mWouldBackpressureHwc = mFramePending && wouldBackpressure;
}
-std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod,
+std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period vsyncPeriod,
+ Period minFramePeriod,
Duration hwcMinWorkDuration) {
- if (wouldPresentEarly(minFramePeriod)) {
+ if (wouldPresentEarly(vsyncPeriod, minFramePeriod)) {
return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
}
return {};
@@ -151,8 +186,8 @@
if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime);
} else {
- mPresentFences[1] = mPresentFences[0];
- mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
+ mPresentFencesLegacy[1] = mPresentFencesLegacy[0];
+ mPresentFencesLegacy[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
}
return presentFenceTime;
}
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 190d062..6f4e1f1 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -53,12 +53,13 @@
const auto& target() const { return mTargeter.target(); }
- bool wouldPresentEarly(Period minFramePeriod) const {
- return target().wouldPresentEarly(minFramePeriod);
+ bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const {
+ return target().wouldPresentEarly(vsyncPeriod, minFramePeriod);
}
- FrameTarget::FenceWithFenceTime presentFenceForPastVsync(Period minFramePeriod) const {
- return target().presentFenceForPastVsync(minFramePeriod);
+ std::pair<bool /*wouldBackpressure*/, FrameTarget::PresentFence> expectedSignaledPresentFence(
+ Period vsyncPeriod, Period minFramePeriod) const {
+ return target().expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
}
struct Frame {
@@ -173,7 +174,6 @@
}
TEST_F(FrameTargeterTest, recallsPastVsync) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{111};
TimePoint frameBeginTime(1000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -181,16 +181,72 @@
constexpr Duration kFrameDuration = 13ms;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
+ FenceTimePtr fence;
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
+ kRefreshRate);
+ fence = frame.end();
+ }
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, fence);
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ ASSERT_TRUE(wouldBackpressure);
+ EXPECT_EQ(presentFence.fenceTime, fence);
+ }
+}
+
+TEST_F(FrameTargeterTest, wouldBackpressureAfterTime) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
+ }
+ {
+ frameBeginTime += kPeriod;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_FALSE(wouldBackpressure);
+ }
+}
+
+TEST_F(FrameTargeterTest, wouldBackpressureAfterTimeLegacy) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
+ }
+ {
+ frameBeginTime += kPeriod;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
}
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -198,101 +254,66 @@
constexpr Duration kFrameDuration = 10ms;
FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr currentFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
-
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, previousFence);
-
- previousFence = fence;
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, previousFence);
+ previousFence = currentFence;
+ currentFence = frame.end();
}
}
-TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) {
+TEST_F(FrameTargeterTest, recallsPastVsyncFiveVsyncsAhead) {
SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- constexpr Duration kFrameDuration = 10ms;
+ constexpr Duration kFrameDuration = 40ms;
- FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr firstFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
const auto fence = frame.end();
-
- const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
- EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
- EXPECT_EQ(presentFenceForPastVsync(kFrameDuration).fenceTime, previousFence);
-
- frameBeginTime += kPeriod;
- previousFence = fence;
+ if (firstFence == FenceTime::NO_FENCE) {
+ firstFence = fence;
+ }
}
+
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, firstFence);
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
- constexpr Fps kPeakRefreshRate = 240_Hz;
+ constexpr Fps kVsyncRate = 240_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Period kVsyncPeriod = kVsyncRate.getPeriod();
constexpr Duration kFrameDuration = 10ms;
FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr currentFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
- kPeakRefreshRate);
- const auto fence = frame.end();
-
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, previousFence);
-
- previousFence = fence;
- }
-}
-
-TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAheadVrr) {
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
-
- VsyncId vsyncId{222};
- TimePoint frameBeginTime(2000ms);
- constexpr Fps kRefreshRate = 120_Hz;
- constexpr Fps kPeakRefreshRate = 240_Hz;
- constexpr Period kPeriod = kRefreshRate.getPeriod();
- constexpr Duration kFrameDuration = 10ms;
-
- FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
- for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
- kPeakRefreshRate);
- const auto fence = frame.end();
-
- const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
- EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
- EXPECT_EQ(presentFenceForPastVsync(kFrameDuration).fenceTime, previousFence);
-
- frameBeginTime += kPeriod;
- previousFence = fence;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ EXPECT_EQ(expectedSignaledPresentFence(kVsyncPeriod, kPeriod).second.fenceTime,
+ previousFence);
+ previousFence = currentFence;
+ currentFence = frame.end();
}
}
TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
constexpr Period kPeriod = (60_Hz).getPeriod();
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, FenceTime::NO_FENCE);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, FenceTime::NO_FENCE);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
}
TEST_F(FrameTargeterTest, detectsEarlyPresent) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -300,20 +321,57 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// `finalFrame` would present early, so it has an earliest present time.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentAfterLongPeriod) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
+ }
+
+ // The target is early if the past present fence was signaled.
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
+
+ frameBeginTime += 10 * kPeriod;
+
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, so it has an earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_NE(std::nullopt, target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
@@ -322,7 +380,6 @@
// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
// when there is expected present time support.
TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -330,26 +387,30 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// `finalFrame` would present early, but we have expected present time support, so it has no
// earliest present time.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_EQ(std::nullopt, target().earliestPresentTime());
}
TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{444};
TimePoint frameBeginTime(4000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -357,17 +418,21 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
// The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
{ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
@@ -375,66 +440,21 @@
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// The target is early if the past present fence was signaled.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_NE(std::nullopt, target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
-TEST_F(FrameTargeterTest, detectsEarlyPresentNVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
- VsyncId vsyncId{444};
- TimePoint frameBeginTime(4000ms);
- Fps refreshRate = 120_Hz;
- Period period = refreshRate.getPeriod();
-
- // The target is not early while past present fences are pending.
- for (int n = 5; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- EXPECT_FALSE(wouldPresentEarly(period));
- EXPECT_FALSE(target().earliestPresentTime());
- }
-
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- auto fence = frame.end();
- frameBeginTime += period;
- fence->signalForTest(frameBeginTime.ns());
-
- // The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(wouldPresentEarly(period));
- EXPECT_FALSE(target().earliestPresentTime());
-
- { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); }
-
- Frame oneEarlyPresentFrame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- // The target is early if the past present fence was signaled.
- EXPECT_TRUE(wouldPresentEarly(period));
- ASSERT_NE(std::nullopt, target().earliestPresentTime());
- EXPECT_EQ(*target().earliestPresentTime(),
- target().expectedPresentTime() - period - kHwcMinWorkDuration);
-
- fence = oneEarlyPresentFrame.end();
- frameBeginTime += period;
- fence->signalForTest(frameBeginTime.ns());
-
- // Change rate to track frame more than 2 vsyncs ahead
- refreshRate = 144_Hz;
- period = refreshRate.getPeriod();
- Frame onePresentEarlyFrame(this, vsyncId++, frameBeginTime, 16ms, refreshRate, refreshRate);
- // The target is not early as last frame as the past frame is tracked for pending.
- EXPECT_FALSE(wouldPresentEarly(period));
-}
-
TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
TimePoint frameBeginTime(5000ms);
constexpr Fps kRefreshRate = 144_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
+ { const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); }
// The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_TRUE(target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 22a8993..299f70b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2161,12 +2161,12 @@
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
-void SurfaceFlinger::scheduleCommit(FrameHint hint) {
+void SurfaceFlinger::scheduleCommit(FrameHint hint, Duration workDurationSlack) {
if (hint == FrameHint::kActive) {
mScheduler->resetIdleTimer();
}
mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
- mScheduler->scheduleFrame();
+ mScheduler->scheduleFrame(workDurationSlack);
}
void SurfaceFlinger::scheduleComposite(FrameHint hint) {
@@ -2620,13 +2620,16 @@
}
}
- if (pacesetterFrameTarget.isFramePending()) {
+ if (pacesetterFrameTarget.wouldBackpressureHwc()) {
if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
if (FlagManager::getInstance().vrr_config()) {
mScheduler->getVsyncSchedule()->getTracker().onFrameMissed(
pacesetterFrameTarget.expectedPresentTime());
}
- scheduleCommit(FrameHint::kNone);
+ const Duration slack = FlagManager::getInstance().allow_n_vsyncs_in_targeter()
+ ? TimePoint::now() - pacesetterFrameTarget.frameBeginTime()
+ : Duration::fromNs(0);
+ scheduleCommit(FrameHint::kNone, slack);
return false;
}
}
@@ -2913,6 +2916,9 @@
// Now that the current frame has been presented above, PowerAdvisor needs the present time
// of the previous frame (whose fence is signaled by now) to determine how long the HWC had
// waited on that fence to retire before presenting.
+ // TODO(b/355238809) `presentFenceForPreviousFrame` might not always be signaled (e.g. on
+ // devices
+ // where HWC does not block on the previous present fence). Revise this assumtion.
const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 89ade4e..157b722 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -273,7 +273,7 @@
enum class FrameHint { kNone, kActive };
// Schedule commit of transactions on the main thread ahead of the next VSYNC.
- void scheduleCommit(FrameHint);
+ void scheduleCommit(FrameHint, Duration workDurationSlack = Duration::fromNs(0));
// As above, but also force composite regardless if transactions were committed.
void scheduleComposite(FrameHint);
// As above, but also force dirty geometry to repaint.
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h
index 198e7b2..215472b 100644
--- a/services/surfaceflinger/Utils/RingBuffer.h
+++ b/services/surfaceflinger/Utils/RingBuffer.h
@@ -43,8 +43,10 @@
}
T& front() { return (*this)[0]; }
+ const T& front() const { return (*this)[0]; }
T& back() { return (*this)[size() - 1]; }
+ const T& back() const { return (*this)[size() - 1]; }
T& operator[](size_t index) {
return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
index 5c742d7..866eb08 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
@@ -28,6 +28,7 @@
namespace android {
+using testing::_;
using testing::DoAll;
using testing::Mock;
using testing::SetArgPointee;
@@ -91,7 +92,7 @@
PrintToStringParamName);
TEST_P(FrameRateSelectionStrategyTest, SetAndGet) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
@@ -104,7 +105,7 @@
}
TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
@@ -128,7 +129,7 @@
}
TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
@@ -169,7 +170,7 @@
}
TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 9899d42..4705dd1 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -35,7 +35,7 @@
#include "mock/MockVsyncController.h"
namespace android {
-
+using testing::_;
using testing::DoAll;
using testing::Mock;
using testing::SetArgPointee;
@@ -93,7 +93,7 @@
namespace {
TEST_P(SetFrameRateTest, SetAndGet) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -104,7 +104,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParent) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -129,7 +129,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -168,7 +168,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChild) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -193,7 +193,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -232,7 +232,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -262,7 +262,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -293,7 +293,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
@@ -352,7 +352,7 @@
}
TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index e5f2a91..2d3ebb4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -97,7 +97,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
@@ -129,7 +129,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) {
@@ -159,7 +159,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index f8ad8e1..df8f68f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -38,7 +38,7 @@
// Call Expectations
// Destroying the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
// --------------------------------------------------------------------
// Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 897f9a0..aef467a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -48,7 +48,7 @@
TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1);
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
constexpr HWDisplayId displayId1 = 456;
mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED);
@@ -73,7 +73,7 @@
.WillOnce(Return(Error::NONE));
// A single commit should be scheduled for both configure calls.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
mFlinger.configure();
@@ -116,7 +116,7 @@
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
mFlinger.configure();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index eaf4684..5231965 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -28,7 +28,7 @@
TEST_F(InitializeDisplaysTest, initializesDisplays) {
// Scheduled by the display transaction, and by powering on each display.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(3);
EXPECT_CALL(static_cast<mock::VSyncTracker&>(
mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 83e2f98..fed7b2e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -271,7 +271,7 @@
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index f063809..0814e3d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -62,7 +62,7 @@
}
MOCK_METHOD(void, scheduleConfigure, (), (override));
- MOCK_METHOD(void, scheduleFrame, (), (override));
+ MOCK_METHOD(void, scheduleFrame, (Duration), (override));
MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index e13fe49..fab1f6d 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -105,7 +105,7 @@
void NotPlacedOnTransactionQueue(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transaction;
setupSingle(transaction, flags,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -129,7 +129,7 @@
void PlaceOnTransactionQueue(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
@@ -155,7 +155,7 @@
void BlockedByPriorTransaction(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
nsecs_t time = systemTime();
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(2);
// transaction that should go on the pending thread
TransactionInfo transactionA;
@@ -217,7 +217,7 @@
TEST_F(TransactionApplicationTest, AddToPendingQueue) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
@@ -238,7 +238,7 @@
TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 8690dba..7c678bd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -1055,6 +1055,36 @@
EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000));
EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000));
}
+
+TEST_F(VSyncPredictorTest, adjustsOnlyMinFrameViolatingVrrTimeline) {
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig{.minFrameIntervalNs =
+ static_cast<int32_t>(minFrameRate.getPeriodNsecs())};
+ ftl::NonNull<DisplayModePtr> mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig));
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
+ auto lastConfirmedSignalTime = TimePoint::fromNs(1500);
+ auto lastConfirmedExpectedPresentTime = TimePoint::fromNs(1000);
+ vrrTracker.onFrameBegin(TimePoint::fromNs(2000),
+ {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime});
+ EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 1500));
+
+ minFrameRate = Fps::fromPeriodNsecs(2000);
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ lastConfirmedSignalTime = TimePoint::fromNs(2500);
+ lastConfirmedExpectedPresentTime = TimePoint::fromNs(2500);
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000),
+ {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime});
+ // Enough time without adjusting vsync to present with new rate on time, no need of adjustment
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 3500));
+}
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues