Merge "Align DisplayEventReceiver events"
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index 7324ead..e174c8e 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -33,7 +33,8 @@
const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
-DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false) {
+DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
+ log_duration_(true) {
assert(!tmp_root.empty());
deleteTempFiles(tmp_root_);
}
@@ -99,6 +100,26 @@
}
}
+void DumpPool::setLogDuration(bool log_duration) {
+ log_duration_ = log_duration;
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func);
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func, out_fd);
+}
+
std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
auto tmp_file_ptr = std::make_unique<TmpFile>();
std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
index 266d519..a4ea875 100644
--- a/cmds/dumpstate/DumpPool.h
+++ b/cmds/dumpstate/DumpPool.h
@@ -29,26 +29,32 @@
namespace os {
namespace dumpstate {
+class DumpPoolTest;
+
/*
* A thread pool with the fixed number of threads to execute multiple dump tasks
- * simultaneously for the dumpstate. The dump task is a callable function
- * included a file descriptor as a parameter, and the task could dump results to
- * that fd. For example:
+ * simultaneously for the dumpstate. The dump task is a callable function. It
+ * could include a file descriptor as a parameter to redirect dump results, if
+ * it needs to output results to the bugreport. This can avoid messing up
+ * bugreport's results when multiple dump tasks are running at the same time.
+ * Takes an example below for the usage of the DumpPool:
*
- * void DumpXXXX(int out_fd) {
+ * void DumpFoo(int out_fd) {
* dprintf(out_fd, "Dump result to out_fd ...");
* }
* ...
* DumpPool pool(tmp_root);
- * pool.enqueueTask("TaskName", &DumpXXXX, std::placeholders::_1);
+ * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
* ...
* pool.waitForTask("TaskName");
*
- * DumpXXXX is a callable function included a out_fd parameter. Using the
- * enqueueTask method in DumpPool to enqueue the task to the pool. The
- * std::placeholders::_1 is placeholder for DumpPool to pass a fd argument.
+ * DumpFoo is a callable function included a out_fd parameter. Using the
+ * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
+ * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
*/
class DumpPool {
+ friend class android::os::dumpstate::DumpPoolTest;
+
public:
/*
* Creates a thread pool.
@@ -72,17 +78,40 @@
void shutdown();
/*
- * Adds a task with a task name into the queue of the thread pool.
+ * Adds a task into the queue of the thread pool.
*
- * |task_name| The name of the task.
- * |f| Callable function to execute the task. This function must
- * include a parameter of file descriptor to output dump result.
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
* |args| A list of arguments.
+ *
+ * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
*/
- template<class F, class... Args> void enqueueTask(const std::string& task_name,
- F&& f, Args&&... args) {
- auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
- futures_map_[task_name] = post(func);
+ template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(void)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Adds a task into the queue of the thread pool. The task takes a file
+ * descriptor as a parameter to redirect dump results to a temporary file.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
+ * argument needs to be included here.
+ */
+ template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(int)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
if (threads_.empty()) {
start();
}
@@ -111,13 +140,15 @@
using Task = std::packaged_task<std::string()>;
using Future = std::shared_future<std::string>;
- template<class T> Future post(T dump_func) {
+ template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
+
+ template<class T> Future post(const std::string& task_name, T dump_func) {
Task packaged_task([=]() {
std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
if (!tmp_file_ptr) {
return std::string("");
}
- std::invoke(dump_func, tmp_file_ptr->fd.get());
+ invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
fsync(tmp_file_ptr->fd.get());
return std::string(tmp_file_ptr->path);
});
@@ -138,12 +169,21 @@
void setThreadName(const pthread_t thread, int id);
void loop();
+ /*
+ * For test purpose only. Enables or disables logging duration of the task.
+ *
+ * |log_duration| if true, DurationReporter is initiated to log duration of
+ * the task.
+ */
+ void setLogDuration(bool log_duration);
+
private:
static const int MAX_THREAD_COUNT = 4;
/* A path to a temporary folder for threads to create temporary files. */
std::string tmp_root_;
bool shutdown_;
+ bool log_duration_; // For test purpose only, the default value is true.
std::mutex lock_; // A lock for the tasks_.
std::condition_variable condition_variable_;
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4b69607..eeaa5a3 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -180,6 +180,7 @@
std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
+int PropertiesHelper::parallel_run_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -202,6 +203,14 @@
return unroot_ == 1;
}
+bool PropertiesHelper::IsParallelRun() {
+ if (parallel_run_ == -1) {
+ parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run",
+ /* default_value = */true) ? 1 : 0;
+ }
+ return parallel_run_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b7ac25c..b099443 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -176,10 +176,18 @@
*/
static bool IsUnroot();
+ /*
+ * Whether or not the parallel run is enabled. Setting the system property
+ * 'dumpstate.parallel_run' to false to disable it, otherwise it returns
+ * true by default.
+ */
+ static bool IsParallelRun();
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
+ static int parallel_run_;
};
/*
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index af41643..7d195b4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -95,6 +95,7 @@
using ::android::hardware::dumpstate::V1_1::toString;
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
+using ::std::placeholders::_1;
// TODO: remove once moved to namespace
using android::defaultServiceManager;
@@ -113,6 +114,7 @@
using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
// Keep in sync with
@@ -196,8 +198,26 @@
func_ptr(__VA_ARGS__); \
RETURN_IF_USER_DENIED_CONSENT();
+// Runs func_ptr, and logs a duration report after it's finished.
+#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...) \
+ { \
+ DurationReporter duration_reporter_in_macro(log_title); \
+ func_ptr(__VA_ARGS__); \
+ }
+
+// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report
+// is output after a slow function is finished.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
+// Names of parallel tasks, they are used for the DumpPool to identify the dump
+// task and the log title of the duration report.
+static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
+
namespace android {
namespace os {
namespace {
@@ -762,8 +782,9 @@
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
- PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ options_->args.c_str(), options_->bugreport_mode.c_str());
printf("\n");
}
@@ -1680,7 +1701,18 @@
time_t logcat_ts = time(nullptr);
/* collect stack traces from Dalvik and native processes (needs root) */
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ // One thread is enough since we only need to enqueue DumpTraces here.
+ dump_pool_->start(/* thread_counts = */1);
+
+ // DumpTraces takes long time, post it to the another thread in the
+ // pool, if pool is available
+ dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
+ &dump_traces_path);
+ }
/* Run some operations that require root. */
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1723,6 +1755,15 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ dump_pool_->waitForTask(DUMP_TRACES_TASK);
+
+ // Current running thread in the pool is the root user also. Shutdown
+ // the pool and restart later to ensure all threads in the pool could
+ // drop the root user.
+ dump_pool_->shutdown();
+ }
if (!DropRootUser()) {
return Dumpstate::RunStatus::ERROR;
}
@@ -1881,8 +1922,6 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- DurationReporter duration_reporter("DUMP TRACES");
-
const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
@@ -2554,6 +2593,7 @@
*/
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
const std::string& calling_package) {
+ DurationReporter duration_reporter("RUN INTERNAL", /* logcat_only = */true);
LogDumpOptions(*options_);
if (!options_->ValidateOptions()) {
MYLOGE("Invalid options specified\n");
@@ -2715,6 +2755,13 @@
// Don't buffer stdout
setvbuf(stdout, nullptr, _IONBF, 0);
+ // Enable the parallel run if the client requests to output to a file.
+ EnableParallelRunIfNeeded();
+ // Using scope guard to make sure the dump pool can be shut down correctly.
+ auto scope_guard_to_shutdown_pool = android::base::make_scope_guard([=]() {
+ ShutdownDumpPool();
+ });
+
// NOTE: there should be no stdout output until now, otherwise it would break the header.
// In particular, DurationReport objects should be created passing 'title, NULL', so their
// duration is logged into MYLOG instead.
@@ -2881,6 +2928,23 @@
android::os::UnlinkAndLogOnError(path_);
}
+void Dumpstate::EnableParallelRunIfNeeded() {
+ // The thread pool needs to create temporary files to receive dump results.
+ // That's why we only enable it when the bugreport client chooses to output
+ // to a file.
+ if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
+ return;
+ }
+ dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+}
+
+void Dumpstate::ShutdownDumpPool() {
+ if (dump_pool_) {
+ dump_pool_->shutdown();
+ dump_pool_ = nullptr;
+ }
+}
+
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
MYLOGD("User denied consent; deleting files and returning\n");
CleanupTmpFiles();
@@ -2999,8 +3063,9 @@
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose)
- : title_(title), logcat_only_(logcat_only), verbose_(verbose) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose,
+ int duration_fd) : title_(title), logcat_only_(logcat_only), verbose_(verbose),
+ duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
}
@@ -3014,7 +3079,8 @@
}
if (!logcat_only_) {
// Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
+ dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
+ elapsed, title_.c_str());
}
}
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0d25d30..d400dc7 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,7 @@
#include <ziparchive/zip_writer.h>
#include "DumpstateUtil.h"
+#include "DumpPool.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -75,7 +76,7 @@
class DurationReporter {
public:
explicit DurationReporter(const std::string& title, bool logcat_only = false,
- bool verbose = false);
+ bool verbose = false, int duration_fd = STDOUT_FILENO);
~DurationReporter();
@@ -84,6 +85,7 @@
bool logcat_only_;
bool verbose_;
uint64_t started_;
+ int duration_fd_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
@@ -193,7 +195,7 @@
* that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
*/
class Dumpstate {
- friend class DumpstateTest;
+ friend class android::os::dumpstate::DumpstateTest;
public:
enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -490,6 +492,9 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
+
// A callback to IncidentCompanion service, which checks user consent for sharing the
// bugreport with the calling app. If the user has not responded yet to the dialog it will
// be neither confirmed nor denied.
@@ -528,6 +533,10 @@
// but leaves the log file alone.
void CleanupTmpFiles();
+ // Create the thread pool to enable the parallel run function.
+ void EnableParallelRunIfNeeded();
+ void ShutdownDumpPool();
+
RunStatus HandleUserConsentDenied();
// Copies bugreport artifacts over to the caller's directories provided there is user consent or
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index d0a4826..b3cb434 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -97,6 +97,10 @@
PropertiesHelper::unroot_ = unroot;
}
+ void SetParallelRun(bool parallel_run) const {
+ PropertiesHelper::parallel_run_ = parallel_run;
+ }
+
bool IsStandalone() const {
return calls_ == 1;
}
@@ -544,6 +548,10 @@
ds.options_.reset(new Dumpstate::DumpOptions());
}
+ void TearDown() {
+ ds.ShutdownDumpPool();
+ }
+
// Runs a command and capture `stdout` and `stderr`.
int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
@@ -571,6 +579,10 @@
ds.progress_.reset(new Progress(initial_max, progress, 1.2));
}
+ void EnableParallelRunIfNeeded() {
+ ds.EnableParallelRunIfNeeded();
+ }
+
std::string GetProgressMessage(int progress, int max,
int old_max = 0, bool update_progress = true) {
EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
@@ -1009,6 +1021,27 @@
ds.listener_.clear();
}
+TEST_F(DumpstateTest, DumpPool_withOutputToFileAndParallelRunEnabled_notNull) {
+ ds.options_->use_socket = false;
+ SetParallelRun(true);
+ EnableParallelRunIfNeeded();
+ EXPECT_TRUE(ds.options_->OutputToFile());
+ EXPECT_TRUE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
+ ds.options_->use_socket = true;
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.options_->OutputToFile());
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+ SetParallelRun(false);
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
class DumpstateServiceTest : public DumpstateBaseTest {
public:
DumpstateService dss;
@@ -1623,6 +1656,7 @@
class DumpPoolTest : public DumpstateBaseTest {
public:
void SetUp() {
+ dump_pool_ = std::make_unique<DumpPool>(kTestDataPath);
DumpstateBaseTest::SetUp();
CreateOutputFile();
}
@@ -1662,12 +1696,16 @@
return count;
}
+ void setLogDuration(bool log_duration) {
+ dump_pool_->setLogDuration(log_duration);
+ }
+
+ std::unique_ptr<DumpPool> dump_pool_;
android::base::unique_fd out_fd_;
std::string out_path_;
};
-TEST_F(DumpPoolTest, EnqueueTask) {
- DumpPool pool(kTestDataPath);
+TEST_F(DumpPoolTest, EnqueueTaskWithFd) {
auto dump_func_1 = [](int out_fd) {
dprintf(out_fd, "A");
};
@@ -1678,19 +1716,37 @@
auto dump_func_3 = [](int out_fd) {
dprintf(out_fd, "C");
};
- pool.enqueueTask(/* task_name = */"1", dump_func_1, std::placeholders::_1);
- pool.enqueueTask(/* task_name = */"2", dump_func_2, std::placeholders::_1);
- pool.enqueueTask(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+ setLogDuration(/* log_duration = */false);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
- pool.waitForTask("1", "", out_fd_.get());
- pool.waitForTask("2", "", out_fd_.get());
- pool.waitForTask("3", "", out_fd_.get());
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->waitForTask("2", "", out_fd_.get());
+ dump_pool_->waitForTask("3", "", out_fd_.get());
+ dump_pool_->shutdown();
std::string result;
ReadFileToString(out_path_, &result);
EXPECT_THAT(result, StrEq("A\nB\nC\n"));
EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
- pool.shutdown();
+}
+
+TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) {
+ bool run_1 = false;
+ auto dump_func_1 = [&]() {
+ run_1 = true;
+ };
+
+ dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_TRUE(run_1);
+ EXPECT_THAT(result, StrEq("------ 0.000s was the duration of '1' ------\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
}
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index be0c062..17015a4 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -189,8 +189,7 @@
filegroup {
name: "installd_aidl",
srcs: [
- "binder/android/os/IInstalld.aidl",
- "binder/android/os/storage/CrateMetadata.aidl",
+ "binder/**/*.aidl",
],
path: "binder",
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7014c8..eb1bbd9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -420,33 +420,6 @@
return true;
}
-binder::Status InstalldNativeService::createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- ATRACE_BEGIN("createAppDataBatched");
- binder::Status ret;
- for (size_t i = 0; i < uuids->size(); i++) {
- std::optional<std::string> packageName = packageNames->at(i);
- if (!packageName) {
- continue;
- }
- ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
- seInfos[i], targetSdkVersions[i], _aidl_return);
- if (!ret.isOk()) {
- ATRACE_END();
- return ret;
- }
- }
- ATRACE_END();
- return ok();
-}
-
binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
@@ -528,6 +501,38 @@
return ok();
}
+
+binder::Status InstalldNativeService::createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ int64_t ceDataInode = -1;
+ auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
+ args.seInfo, args.targetSdkVersion, &ceDataInode);
+ _aidl_return->ceDataInode = ceDataInode;
+ _aidl_return->exceptionCode = status.exceptionCode();
+ _aidl_return->exceptionMessage = status.exceptionMessage();
+ return ok();
+}
+
+binder::Status InstalldNativeService::createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ std::vector<android::os::CreateAppDataResult> results;
+ for (auto arg : args) {
+ android::os::CreateAppDataResult result;
+ createAppData(arg, &result);
+ results.push_back(result);
+ }
+ *_aidl_return = results;
+ return ok();
+}
+
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 9819327..4966b96 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -44,15 +44,18 @@
int32_t userSerial, int32_t flags);
binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
- binder::Status createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return);
+
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+
+ binder::Status createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return);
+ binder::Status createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return);
+
binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
new file mode 100644
index 0000000..96d7faa
--- /dev/null
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable CreateAppDataArgs {
+ @nullable @utf8InCpp String uuid;
+ @utf8InCpp String packageName;
+ int userId;
+ int flags;
+ int appId;
+ @utf8InCpp String seInfo;
+ int targetSdkVersion;
+}
diff --git a/cmds/installd/binder/android/os/CreateAppDataResult.aidl b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
new file mode 100644
index 0000000..3b8fa6b
--- /dev/null
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable CreateAppDataResult {
+ long ceDataInode;
+ int exceptionCode;
+ @utf8InCpp String exceptionMessage;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index eeda6c5..2538e22 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -21,11 +21,9 @@
void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
- long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
- int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
- long createAppDataBatched(in @nullable @utf8InCpp String[] uuids,
- in @nullable @utf8InCpp String[] packageNames, in int userId, int flags, in int[] appIds,
- in @utf8InCpp String[] seInfos, in int[] targetSdkVersions);
+ android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
+ android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
+
void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, int flags, int appId, @utf8InCpp String seInfo);
void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 7127110..622604f 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -21,4 +21,8 @@
// this cannot be the same as the module name.
stem: "rustBinderTestServiceBinary",
test_harness: false,
+ // TODO(b/164473602): Remove this setting and add the module to `data`
+ // attribute of rustBinderTest.
+ auto_gen_config: false,
+ test_suites: ["general-tests"],
}
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 50f6289..5e785b6 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -251,7 +251,7 @@
for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
key.bucket = i;
if (findMapEntry(gTisMapFd, &key, vals.data())) {
- if (errno != ENOENT) return {};
+ if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {};
continue;
}
@@ -362,7 +362,7 @@
time_key_t key = {.uid = uid};
for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
- if (errno != ENOENT) return {};
+ if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {};
continue;
}
auto offset = key.bucket * CPUS_PER_ENTRY;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 79c3654..672ffae 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -407,6 +407,23 @@
mImageManager = std::make_unique<ImageManager>(this);
mImageManager->initThread();
mDrawingBuffer = createFramebuffer();
+ sp<GraphicBuffer> buf =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder");
+
+ const status_t err = buf->initCheck();
+ if (err != OK) {
+ ALOGE("Error allocating placeholder buffer: %d", err);
+ return;
+ }
+ mPlaceholderBuffer = buf.get();
+ EGLint attributes[] = {
+ EGL_NONE,
+ };
+ mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ mPlaceholderBuffer, attributes);
+ ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
+ eglGetError());
}
GLESRenderEngine::~GLESRenderEngine() {
@@ -421,6 +438,7 @@
eglDestroyImageKHR(mEGLDisplay, expired);
DEBUG_EGL_IMAGE_TRACKER_DESTROY();
}
+ eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEGLDisplay);
@@ -587,6 +605,9 @@
}
void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+ for (int i = 0; i < count; ++i) {
+ mTextureView.erase(names[i]);
+ }
glDeleteTextures(count, names);
}
@@ -644,6 +665,7 @@
}
bindExternalTextureImage(texName, *cachedImage->second);
+ mTextureView.insert_or_assign(texName, buffer->getId());
}
// Wait for the new buffer to be ready.
@@ -885,7 +907,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-bool GLESRenderEngine::cleanupPostRender() {
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
ATRACE_CALL();
if (mPriorResourcesCleaned ||
@@ -894,6 +916,30 @@
return false;
}
+ // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
+ // not need to keep memory around if we don't need to do so.
+ if (mode == CleanupMode::CLEAN_ALL) {
+ // TODO: SurfaceFlinger memory utilization may benefit from resetting
+ // texture bindings as well. Assess if it does and there's no performance regression
+ // when rebinding the same image data to the same texture, and if so then its mode
+ // behavior can be tweaked.
+ if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ for (auto [textureName, bufferId] : mTextureView) {
+ if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+ static_cast<GLeglImageOES>(mPlaceholderImage));
+ mTextureView[textureName] = std::nullopt;
+ checkErrors();
+ }
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mImageCache.clear();
+ }
+ }
+
// Bind the texture to placeholder so that backing image data can be freed.
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
@@ -1618,6 +1664,16 @@
return cachedImage != mImageCache.end();
}
+bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end();
+}
+
+std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end() ? entry->second : std::nullopt;
+}
+
bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 245bfd7..2c6eae2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -75,7 +75,7 @@
const std::vector<const LayerSettings*>& layers,
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
- bool cleanupPostRender() override;
+ bool cleanupPostRender(CleanupMode mode) override;
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
// Creates an output image for rendering to
@@ -86,6 +86,12 @@
// Test-only methods
// Returns true iff mImageCache contains an image keyed by bufferId
bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ // Returns true iff texName was previously generated by RenderEngine and was
+ // not destroyed.
+ bool isTextureNameKnownForTesting(uint32_t texName);
+ // Returns the buffer ID of the content bound to texName, or nullopt if no
+ // such mapping exists.
+ std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
// Returns true iff mFramebufferImageCache contains an image keyed by bufferId
bool isFramebufferImageCachedForTesting(uint64_t bufferId)
EXCLUDES(mFramebufferImageCacheMutex);
@@ -224,6 +230,8 @@
// Cache of GL images that we'll store per GraphicBuffer ID
std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+ std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
+
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
@@ -237,6 +245,11 @@
// ensure that we align on a word. Allocating 16 bytes will provide a
// guarantee that we don't clobber memory.
uint32_t mPlaceholderDrawBuffer[4];
+ // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
+ // instead these are intended for cleaning up texture memory with the
+ // GL_TEXTURE_EXTERNAL_OES target.
+ ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
+ EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
sp<Fence> mLastDrawFence;
// Store a separate boolean checking if prior resources were cleaned up, as
// devices that don't support native sync fences can't rely on a last draw
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index b137023..09a0f65 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -120,14 +120,25 @@
// Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+ enum class CleanupMode {
+ CLEAN_OUTPUT_RESOURCES,
+ CLEAN_ALL,
+ };
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
// resources used by the most recently drawn frame. If the frame is still
// being drawn, then this call is silently ignored.
//
+ // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
+ // output framebuffer are cleaned up, including the sibling texture.
+ //
+ // If mode is CLEAN_ALL, then we also cleanup resources related to any input
+ // buffers.
+ //
// Returns true if resources were cleaned up, and false if we didn't need to
// do any work.
- virtual bool cleanupPostRender() = 0;
+ virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
// queries
virtual size_t getMaxTextureSize() const = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index d0343ba..e03dd58 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -56,7 +56,7 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
- MOCK_METHOD0(cleanupPostRender, bool());
+ MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 8ab2746..ba17143 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -83,6 +83,7 @@
}
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
+ EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
}
}
@@ -1422,10 +1423,44 @@
if (fd >= 0) {
sync_wait(fd, -1);
}
-
// Only cleanup the first time.
- EXPECT_TRUE(sRE->cleanupPostRender());
- EXPECT_FALSE(sRE->cleanupPostRender());
+ EXPECT_TRUE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+ EXPECT_FALSE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ base::unique_fd fence;
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+
+ const int fd = fence.get();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ }
+
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ uint32_t texName = layer.source.buffer.textureName;
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+
+ EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+
+ // Now check that our view of memory is good.
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
+ EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
}
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 69a0e19..97c7442 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -178,14 +178,20 @@
}
TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
- EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false));
- status_t result = mThreadedRE->cleanupPostRender();
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(false));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
ASSERT_EQ(false, result);
}
TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
- EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true));
- status_t result = mThreadedRE->cleanupPostRender();
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(true));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
ASSERT_EQ(true, result);
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index ad61718..d4184fd 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -361,14 +361,14 @@
return resultFuture.get();
}
-bool RenderEngineThreaded::cleanupPostRender() {
+bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
std::promise<bool> resultPromise;
std::future<bool> resultFuture = resultPromise.get_future();
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::cleanupPostRender");
- bool returnValue = instance.cleanupPostRender();
+ bool returnValue = instance.cleanupPostRender(mode);
resultPromise.set_value(returnValue);
});
}
@@ -400,4 +400,4 @@
} // namespace threaded
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index ec18e1f..86a49e9 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -62,7 +62,7 @@
bool isProtected() const override;
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
- bool cleanupPostRender() override;
+ bool cleanupPostRender(CleanupMode mode) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
@@ -94,4 +94,4 @@
};
} // namespace threaded
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 4250daa..dc8e587 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -27,11 +27,15 @@
#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
#include <time.h>
+#include <vndksupport/linker.h>
#include "Loader.h"
namespace angle {
+constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
+constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
+
static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
@@ -101,11 +105,22 @@
bool initializeAnglePlatform(EGLDisplay dpy) {
// Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = ns,
- };
- void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ void* so = nullptr;
+ if (ns) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+ so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
+ } else {
+ // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
+ so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
+ }
+ if (!so) {
+ ALOGE("%s failed to dlopen %s!", __FUNCTION__, kAngleEs2Lib);
+ return false;
+ }
+
angleGetDisplayPlatform =
reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index 245edb8..c78322e 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -25,6 +25,7 @@
#include <libbpf_android.h>
#include <log/log.h>
#include <unistd.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <unordered_map>
@@ -128,4 +129,24 @@
}
}
+void GpuMem::traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+ uint64_t size)>& callback) {
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) return;
+ uint64_t key = res.value();
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ callback(systemTime(), gpu_id, pid, size);
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+}
+
} // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index 49a9f95..de691e2 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -20,6 +20,8 @@
#include <utils/String16.h>
#include <utils/Vector.h>
+#include <functional>
+
namespace android {
class GpuMem {
@@ -33,27 +35,9 @@
void dump(const Vector<String16>& args, std::string* result);
bool isInitialized() { return mInitialized.load(); }
- // Traverse the map and send each value read back to the callback function.
- // Used for tracing.
- template <typename lambda>
- void traceGpuMemTotals(lambda tracerCallback) {
- auto res = mGpuMemTotalMap.getFirstKey();
- if (!res.ok()) return;
- uint64_t key = res.value();
- while (true) {
- uint32_t gpu_id = key >> 32;
- uint32_t pid = key;
-
- res = mGpuMemTotalMap.readValue(key);
- if (!res.ok()) break;
- uint64_t size = res.value();
-
- tracerCallback(gpu_id, pid, size);
- res = mGpuMemTotalMap.getNextKey(key);
- if (!res.ok()) break;
- key = res.value();
- }
- }
+ // Traverse the gpu memory total map to feed the callback function.
+ void traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+ uint64_t size)>& callback);
private:
// Friend class for testing.
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index abaf30a..45e8367 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -41,6 +41,8 @@
constexpr uint64_t TEST_PROC_VAL_1 = 234;
constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
constexpr uint64_t TEST_PROC_VAL_2 = 345;
+constexpr uint32_t TEST_KEY_MASK = 0x1 | 0x2 | 0x4;
+constexpr uint32_t TEST_KEY_COUNT = 3;
class GpuMemTest : public testing::Test {
public:
@@ -143,5 +145,37 @@
TEST_PROC_VAL_2)));
}
+TEST_F(GpuMemTest, traverseGpuMemTotals) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ static uint32_t sMask = 0;
+ static uint32_t sCount = 0;
+ mGpuMem->traverseGpuMemTotals([](int64_t, uint32_t gpuId, uint32_t pid, uint64_t size) {
+ const uint64_t key = ((uint64_t)gpuId << 32) | pid;
+ switch (key) {
+ case TEST_GLOBAL_KEY:
+ EXPECT_EQ(size, TEST_GLOBAL_VAL);
+ sMask |= 0x1;
+ break;
+ case TEST_PROC_KEY_1:
+ EXPECT_EQ(size, TEST_PROC_VAL_1);
+ sMask |= 0x2;
+ break;
+ case TEST_PROC_KEY_2:
+ EXPECT_EQ(size, TEST_PROC_VAL_2);
+ sMask |= 0x4;
+ break;
+ }
+ sCount++;
+ });
+
+ EXPECT_EQ(sMask, TEST_KEY_MASK);
+ EXPECT_EQ(sCount, TEST_KEY_COUNT);
+}
+
} // namespace
} // namespace android
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 9e01a64..6366e1d 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -23,9 +23,7 @@
#include <gpumem/GpuMem.h>
#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
#include <unistd.h>
-#include <utils/Timers.h>
-#include <algorithm>
#include <thread>
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
@@ -79,10 +77,10 @@
ALOGE("Cannot trace without GpuMem initialization");
return;
}
- mGpuMem->traceGpuMemTotals([](uint32_t gpuId, uint32_t pid, uint64_t size) {
+ mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) {
GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
- packet->set_timestamp(systemTime());
+ packet->set_timestamp(ts);
auto* event = packet->set_gpu_mem_total_event();
event->set_gpu_id(gpuId);
event->set_pid(pid);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e452187..e341af1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -169,7 +169,6 @@
"Scheduler/LayerInfo.cpp",
"Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
- "Scheduler/PhaseOffsets.cpp",
"Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
@@ -178,6 +177,7 @@
"Scheduler/VSyncPredictor.cpp",
"Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
+ "Scheduler/VsyncConfiguration.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
deleted file mode 100644
index fe2e406..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PhaseOffsets.h"
-
-#include <cutils/properties.h>
-
-#include <optional>
-
-#include "SurfaceFlingerProperties.h"
-
-namespace {
-
-std::optional<nsecs_t> getProperty(const char* name) {
- char value[PROPERTY_VALUE_MAX];
- property_get(name, value, "-1");
- if (const int i = atoi(value); i != -1) return i;
- return std::nullopt;
-}
-
-bool fpsEqualsWithMargin(float fpsA, float fpsB) {
- static constexpr float MARGIN = 0.01f;
- return std::abs(fpsA - fpsB) <= MARGIN;
-}
-
-std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->getFps());
- }
-
- return refreshRates;
-}
-
-} // namespace
-
-namespace android::scheduler {
-
-PhaseConfiguration::~PhaseConfiguration() = default;
-
-namespace impl {
-
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- sysprop::vsync_event_phase_offset_ns(1000000),
- sysprop::vsync_sf_event_phase_offset_ns(1000000),
- getProperty("debug.sf.early_phase_offset_ns"),
- getProperty("debug.sf.early_gl_phase_offset_ns"),
- getProperty("debug.sf.early_app_phase_offset_ns"),
- getProperty("debug.sf.early_gl_app_phase_offset_ns"),
- // Below defines the threshold when an offset is considered to be negative,
- // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
- // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
- // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
- // vsync.
- getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())) {}
-
-PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs,
- std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs,
- std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
- : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
- mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
- mEarlySfOffsetNs(earlySfOffsetNs),
- mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
- mEarlyAppOffsetNs(earlyAppOffsetNs),
- mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
- mThresholdForNextVsync(thresholdForNextVsync),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-void PhaseOffsets::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
- "next VSYNC threshold: %9" PRId64 " ns\n",
- late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
- mThresholdForNextVsync);
-}
-
-std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto& refreshRate : refreshRates) {
- offsets.emplace(refreshRate,
- getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
- }
- return offsets;
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
- if (fps > 65.0f) {
- return getHighFpsOffsets(vsyncPeriod);
- } else {
- return getDefaultOffsets(vsyncPeriod);
- }
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
- return {
- {
- mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
- ? mSfVSyncPhaseOffsetNs
- : mSfVSyncPhaseOffsetNs - vsyncDuration,
-
- mVSyncPhaseOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
- const auto highFpsLateAppOffsetNs =
- getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
- const auto highFpsLateSfOffsetNs =
- getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
-
- const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
- const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- const auto highFpsEarlyGlAppOffsetNs =
- getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-
- return {
- {
- highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
- ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
- mThresholdForNextVsync
- ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsLateSfOffsetNs < mThresholdForNextVsync
- ? highFpsLateSfOffsetNs
- : highFpsLateSfOffsetNs - vsyncDuration,
-
- highFpsLateAppOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
- [&fps](const std::pair<float, Offsets>& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
-}
-
-static void validateSysprops() {
- const auto validatePropertyBool = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
- };
-
- validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
- "duration");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
- "duration");
-
- const auto validateProperty = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
- "%s is set to %" PRId64 " but expecting duration", prop,
- getProperty(prop).value_or(-1));
- };
-
- validateProperty("debug.sf.early_phase_offset_ns");
- validateProperty("debug.sf.early_gl_phase_offset_ns");
- validateProperty("debug.sf.early_app_phase_offset_ns");
- validateProperty("debug.sf.early_gl_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-}
-
-static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
-}
-
-static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000
- : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
-}
-
-PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
- return Offsets{
- {
- mSfEarlyDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
- },
- {
- mSfEarlyGlDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
- },
- {
- mSfDuration < vsyncDuration
- ? sfDurationToOffset(mSfDuration, vsyncDuration)
- : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
- },
- };
-}
-
-std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto fps : refreshRates) {
- offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
- }
- return offsets;
-}
-
-PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- getProperty("debug.sf.late.sf.duration").value_or(-1),
- getProperty("debug.sf.late.app.duration").value_or(-1),
- getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
- getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
- validateSysprops();
-}
-
-PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
- nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
- nsecs_t appEarlyGlDuration)
- : mSfDuration(sfDuration),
- mAppDuration(appDuration),
- mSfEarlyDuration(sfEarlyDuration),
- mAppEarlyDuration(appEarlyDuration),
- mSfEarlyGlDuration(sfEarlyGlDuration),
- mAppEarlyGlDuration(appEarlyGlDuration),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
-}
-
-void PhaseDurations::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
- " ns\n"
- " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64
- " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
- " ns\n"
- " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64
- " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
- " ns\n"
- " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
- " ns\n",
- late.app,
-
- late.sf,
-
- mAppDuration, mSfDuration,
-
- early.app, early.sf,
-
- mAppEarlyDuration, mSfEarlyDuration,
-
- earlyGl.app,
-
- earlyGl.sf,
-
- mAppEarlyGlDuration, mSfEarlyGlDuration);
-}
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
deleted file mode 100644
index 0ae9fef..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019 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 <unordered_map>
-
-#include "RefreshRateConfigs.h"
-#include "VsyncModulator.h"
-
-namespace android::scheduler {
-
-/*
- * This class encapsulates offsets for different refresh rates. Depending
- * on what refresh rate we are using, and wheter we are composing in GL,
- * different offsets will help us with latency. This class keeps track of
- * which mode the device is on, and returns approprate offsets when needed.
- */
-class PhaseConfiguration {
-public:
- using Offsets = VsyncModulator::OffsetsConfig;
-
- virtual ~PhaseConfiguration();
-
- virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
-
- virtual void setRefreshRateFps(float fps) = 0;
-
- virtual void dump(std::string& result) const = 0;
-};
-
-namespace impl {
-
-/*
- * This is the old implementation of phase offsets and considered as deprecated.
- * PhaseDurations is the new implementation.
- */
-class PhaseOffsets : public scheduler::PhaseConfiguration {
-public:
- PhaseOffsets(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs,
- nsecs_t thresholdForNextVsync);
- std::unordered_map<float, Offsets> initializeOffsets(
- const std::vector<float>& refreshRates) const;
- Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
- Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
-
- const nsecs_t mVSyncPhaseOffsetNs;
- const nsecs_t mSfVSyncPhaseOffsetNs;
- const std::optional<nsecs_t> mEarlySfOffsetNs;
- const std::optional<nsecs_t> mEarlyGlSfOffsetNs;
- const std::optional<nsecs_t> mEarlyAppOffsetNs;
- const std::optional<nsecs_t> mEarlyGlAppOffsetNs;
- const nsecs_t mThresholdForNextVsync;
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-/*
- * Class that encapsulates the phase offsets for SurfaceFlinger and App.
- * The offsets are calculated from durations for each one of the (late, early, earlyGL)
- * offset types.
- */
-class PhaseDurations : public scheduler::PhaseConfiguration {
-public:
- PhaseDurations(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
- nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
-
-private:
- std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
- PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
-
- const nsecs_t mSfDuration;
- const nsecs_t mAppDuration;
-
- const nsecs_t mSfEarlyDuration;
- const nsecs_t mAppEarlyDuration;
-
- const nsecs_t mSfEarlyGlDuration;
- const nsecs_t mAppEarlyGlDuration;
-
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
new file mode 100644
index 0000000..aac2569
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VsyncConfiguration.h"
+
+#include <cutils/properties.h>
+
+#include <optional>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace {
+
+std::optional<nsecs_t> getProperty(const char* name) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get(name, value, "-1");
+ if (const int i = atoi(value); i != -1) return i;
+ return std::nullopt;
+}
+
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+ static constexpr float MARGIN = 0.01f;
+ return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->getFps());
+ }
+
+ return refreshRates;
+}
+
+} // namespace
+
+namespace android::scheduler::impl {
+
+VsyncConfiguration::VsyncConfiguration(float currentFps) : mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(float fps) const {
+ const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+ [&fps](const std::pair<float, VsyncConfigSet>& candidateFps) {
+ return fpsEqualsWithMargin(fps, candidateFps.first);
+ });
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+void VsyncConfiguration::initializeOffsets(const std::vector<float>& refreshRates) {
+ for (const auto fps : refreshRates) {
+ mOffsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+ }
+}
+
+void VsyncConfiguration::dump(std::string& result) const {
+ const auto [early, earlyGpu, late] = getCurrentConfigs();
+ using base::StringAppendF;
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
+ " ns\n"
+ " app duration: %9lld ns\t SF duration: %9lld ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
+ " ns\n"
+ " early app duration: %9lld ns\t early SF duration: %9lld ns\n"
+ " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
+ " ns\n"
+ " GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n",
+ late.appOffset, late.sfOffset,
+
+ late.appWorkDuration.count(), late.sfWorkDuration.count(),
+
+ early.appOffset, early.sfOffset,
+
+ early.appWorkDuration.count(), early.sfWorkDuration.count(),
+
+ earlyGpu.appOffset, earlyGpu.sfOffset,
+
+ earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
+}
+
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
+ sysprop::vsync_event_phase_offset_ns(1000000),
+ sysprop::vsync_sf_event_phase_offset_ns(1000000),
+ getProperty("debug.sf.early_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_phase_offset_ns"),
+ getProperty("debug.sf.early_app_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000),
+ getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000),
+ getProperty("debug.sf.high_fps_early_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"),
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(
+ const std::vector<float>& refreshRates, float currentFps, nsecs_t vsyncPhaseOffsetNs,
+ nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+ : VsyncConfiguration(currentFps),
+ mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+ mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+ mEarlySfOffsetNs(earlySfOffsetNs),
+ mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs),
+ mEarlyAppOffsetNs(earlyAppOffsetNs),
+ mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs),
+ mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs),
+ mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs),
+ mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs),
+ mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
+ mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
+ mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
+ mThresholdForNextVsync(thresholdForNextVsync) {
+ initializeOffsets(refreshRates);
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+ if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
+ return getHighFpsOffsets(vsyncDuration);
+ } else {
+ return getDefaultOffsets(vsyncDuration);
+ }
+}
+
+namespace {
+std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) {
+ return std::chrono::nanoseconds(vsyncDuration - sfOffset);
+}
+
+std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset,
+ nsecs_t vsyncDuration) {
+ auto duration = vsyncDuration + (sfOffset - appOffset);
+ if (duration < vsyncDuration) {
+ duration += vsyncDuration;
+ }
+
+ return std::chrono::nanoseconds(duration);
+}
+} // namespace
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset =
+ mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mSfVSyncPhaseOffsetNs
+ : mSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mVSyncPhaseOffsetNs;
+
+ return {
+ .early = {.sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)},
+ .earlyGpu = {.sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset,
+ vsyncDuration)},
+ .late = {.sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)},
+ };
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or(
+ mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mHighFpsSfVSyncPhaseOffsetNs
+ : mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs;
+
+ return {
+ .early =
+ {
+ .sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset,
+ vsyncDuration),
+ },
+ .earlyGpu =
+ {
+ .sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset,
+ earlyGpuSfOffset, vsyncDuration),
+ },
+ .late =
+ {
+ .sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration),
+ },
+ };
+}
+
+static void validateSysprops() {
+ const auto validatePropertyBool = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+ };
+
+ validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ const auto validateProperty = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+ "%s is set to %" PRId64 " but expecting duration", prop,
+ getProperty(prop).value_or(-1));
+ };
+
+ validateProperty("debug.sf.early_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_phase_offset_ns");
+ validateProperty("debug.sf.early_app_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+namespace {
+nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - sfDuration.count() % vsyncDuration;
+}
+
+nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration,
+ std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration;
+}
+} // namespace
+
+WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+ const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto appDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration)
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration);
+ const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration);
+ const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration);
+ const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration);
+ const auto sfDuration = sfDurationFixup(mSfDuration);
+ const auto appDuration = appDurationFixup(mAppDuration);
+
+ return {
+ .early =
+ {
+
+ .sfOffset = sfEarlyDuration.count() < vsyncDuration
+ ? sfDurationToOffset(sfEarlyDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration,
+ vsyncDuration),
+
+ .sfWorkDuration = sfEarlyDuration,
+ .appWorkDuration = appEarlyDuration,
+ },
+ .earlyGpu =
+ {
+
+ .sfOffset = sfEarlyGpuDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyGpuDuration,
+ sfEarlyGpuDuration, vsyncDuration),
+ .sfWorkDuration = sfEarlyGpuDuration,
+ .appWorkDuration = appEarlyGpuDuration,
+ },
+ .late =
+ {
+
+ .sfOffset = sfDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfDuration, vsyncDuration)
+ : sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration,
+
+ .appOffset =
+ appDurationToOffset(appDuration, sfDuration, vsyncDuration),
+
+ .sfWorkDuration = sfDuration,
+ .appWorkDuration = appDuration,
+ },
+ };
+}
+
+WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
+ getProperty("debug.sf.late.sf.duration").value_or(-1),
+ getProperty("debug.sf.late.app.duration").value_or(-1),
+ getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+ getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+ validateSysprops();
+}
+
+WorkDuration::WorkDuration(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration,
+ nsecs_t appEarlyGpuDuration)
+ : VsyncConfiguration(currentFps),
+ mSfDuration(sfDuration),
+ mAppDuration(appDuration),
+ mSfEarlyDuration(sfEarlyDuration),
+ mAppEarlyDuration(appEarlyDuration),
+ mSfEarlyGpuDuration(sfEarlyGpuDuration),
+ mAppEarlyGpuDuration(appEarlyGpuDuration) {
+ initializeOffsets(refreshRates);
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
new file mode 100644
index 0000000..c27a25d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 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 <unordered_map>
+
+#include "RefreshRateConfigs.h"
+#include "VsyncModulator.h"
+
+namespace android::scheduler {
+
+/*
+ * This class encapsulates vsync configurations for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class VsyncConfiguration {
+public:
+ using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
+
+ virtual ~VsyncConfiguration() = default;
+ virtual VsyncConfigSet getCurrentConfigs() const = 0;
+ virtual VsyncConfigSet getConfigsForRefreshRate(float fps) const = 0;
+
+ virtual void setRefreshRateFps(float fps) = 0;
+
+ virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+
+/*
+ * This is a common implementation for both phase offsets and durations.
+ * PhaseOffsets and WorkDuration derive from this class and implement the
+ * constructOffsets method
+ */
+class VsyncConfiguration : public scheduler::VsyncConfiguration {
+public:
+ explicit VsyncConfiguration(float currentFps);
+
+ // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+ VsyncConfigSet getConfigsForRefreshRate(float fps) const override;
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ VsyncConfigSet getCurrentConfigs() const override {
+ return getConfigsForRefreshRate(mRefreshRateFps);
+ }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+protected:
+ void initializeOffsets(const std::vector<float>& refreshRates);
+ virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+
+ std::unordered_map<float, VsyncConfigSet> mOffsets;
+ std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * WorkDuration is the new implementation.
+ */
+class PhaseOffsets : public VsyncConfiguration {
+public:
+ explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+
+ const nsecs_t mVSyncPhaseOffsetNs;
+ const nsecs_t mSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mEarlySfOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuAppOffsetNs;
+
+ const nsecs_t mHighFpsVSyncPhaseOffsetNs;
+ const nsecs_t mHighFpsSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlySfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuAppOffsetNs;
+
+ const nsecs_t mThresholdForNextVsync;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGpu)
+ * offset types.
+ */
+class WorkDuration : public VsyncConfiguration {
+public:
+ explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+
+protected:
+ // Used for unit tests
+ WorkDuration(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+ nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
+
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGpuDuration;
+ const nsecs_t mAppEarlyGpuDuration;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 7a1b7e4..1f821be 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -35,18 +35,19 @@
const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
-VsyncModulator::VsyncModulator(const OffsetsConfig& config, Now now)
- : mOffsetsConfig(config),
+VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
+ : mVsyncConfigSet(config),
mNow(now),
mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
-VsyncModulator::Offsets VsyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
+VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
std::lock_guard<std::mutex> lock(mMutex);
- mOffsetsConfig = config;
- return updateOffsetsLocked();
+ mVsyncConfigSet = config;
+ return updateVsyncConfigLocked();
}
-VsyncModulator::OffsetsOpt VsyncModulator::setTransactionSchedule(TransactionSchedule schedule) {
+VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(
+ TransactionSchedule schedule) {
switch (schedule) {
case Schedule::EarlyStart:
ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
@@ -76,29 +77,29 @@
return std::nullopt;
}
mTransactionSchedule = schedule;
- return updateOffsets();
+ return updateVsyncConfig();
}
-VsyncModulator::OffsetsOpt VsyncModulator::onTransactionCommit() {
+VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
mLastTransactionCommitTime = mNow();
if (mTransactionSchedule == Schedule::Late) return std::nullopt;
mTransactionSchedule = Schedule::Late;
- return updateOffsets();
+ return updateVsyncConfig();
}
-VsyncModulator::OffsetsOpt VsyncModulator::onRefreshRateChangeInitiated() {
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() {
if (mRefreshRateChangePending) return std::nullopt;
mRefreshRateChangePending = true;
- return updateOffsets();
+ return updateVsyncConfig();
}
-VsyncModulator::OffsetsOpt VsyncModulator::onRefreshRateChangeCompleted() {
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() {
if (!mRefreshRateChangePending) return std::nullopt;
mRefreshRateChangePending = false;
- return updateOffsets();
+ return updateVsyncConfig();
}
-VsyncModulator::OffsetsOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
+VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
bool updateOffsetsNeeded = false;
if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
@@ -117,40 +118,40 @@
}
if (!updateOffsetsNeeded) return std::nullopt;
- return updateOffsets();
+ return updateVsyncConfig();
}
-VsyncModulator::Offsets VsyncModulator::getOffsets() const {
+VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
std::lock_guard<std::mutex> lock(mMutex);
- return mOffsets;
+ return mVsyncConfig;
}
-const VsyncModulator::Offsets& VsyncModulator::getNextOffsets() const {
+const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
// Early offsets are used if we're in the middle of a refresh rate
// change, or if we recently begin a transaction.
if (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd ||
mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
- return mOffsetsConfig.early;
+ return mVsyncConfigSet.early;
} else if (mEarlyGpuFrames > 0) {
- return mOffsetsConfig.earlyGpu;
+ return mVsyncConfigSet.earlyGpu;
} else {
- return mOffsetsConfig.late;
+ return mVsyncConfigSet.late;
}
}
-VsyncModulator::Offsets VsyncModulator::updateOffsets() {
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
std::lock_guard<std::mutex> lock(mMutex);
- return updateOffsetsLocked();
+ return updateVsyncConfigLocked();
}
-VsyncModulator::Offsets VsyncModulator::updateOffsetsLocked() {
- const Offsets& offsets = getNextOffsets();
- mOffsets = offsets;
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+ const VsyncConfig& offsets = getNextVsyncConfig();
+ mVsyncConfig = offsets;
if (mTraceDetailedInfo) {
- const bool isEarly = &offsets == &mOffsetsConfig.early;
- const bool isEarlyGpu = &offsets == &mOffsetsConfig.earlyGpu;
- const bool isLate = &offsets == &mOffsetsConfig.late;
+ const bool isEarly = &offsets == &mVsyncConfigSet.early;
+ const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
+ const bool isLate = &offsets == &mVsyncConfigSet.late;
ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index f920bd2..355a14a 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -48,62 +48,69 @@
// This may keep early offsets for an extra frame, but avoids a race with transaction commit.
static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
- // Phase offsets for SF and app deadlines from VSYNC.
- struct Offsets {
- nsecs_t sf;
- nsecs_t app;
+ // Phase offsets and work durations for SF and app deadlines from VSYNC.
+ struct VsyncConfig {
+ nsecs_t sfOffset;
+ nsecs_t appOffset;
+ std::chrono::nanoseconds sfWorkDuration;
+ std::chrono::nanoseconds appWorkDuration;
- bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
- bool operator!=(const Offsets& other) const { return !(*this == other); }
+ bool operator==(const VsyncConfig& other) const {
+ return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+ sfWorkDuration == other.sfWorkDuration &&
+ appWorkDuration == other.appWorkDuration;
+ }
+
+ bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
};
- using OffsetsOpt = std::optional<Offsets>;
+ using VsyncConfigOpt = std::optional<VsyncConfig>;
- struct OffsetsConfig {
- Offsets early; // Used for early transactions, and during refresh rate change.
- Offsets earlyGpu; // Used during GPU composition.
- Offsets late; // Default.
+ struct VsyncConfigSet {
+ VsyncConfig early; // Used for early transactions, and during refresh rate change.
+ VsyncConfig earlyGpu; // Used during GPU composition.
+ VsyncConfig late; // Default.
- bool operator==(const OffsetsConfig& other) const {
+ bool operator==(const VsyncConfigSet& other) const {
return early == other.early && earlyGpu == other.earlyGpu && late == other.late;
}
- bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
+ bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
};
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
using Now = TimePoint (*)();
- explicit VsyncModulator(const OffsetsConfig&, Now = Clock::now);
+ explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
- Offsets getOffsets() const EXCLUDES(mMutex);
+ VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
- [[nodiscard]] Offsets setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
+ [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
// Changes offsets in response to transaction flags or commit.
- [[nodiscard]] OffsetsOpt setTransactionSchedule(TransactionSchedule);
- [[nodiscard]] OffsetsOpt onTransactionCommit();
+ [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule);
+ [[nodiscard]] VsyncConfigOpt onTransactionCommit();
// Called when we send a refresh rate change to hardware composer, so that
// we can move into early offsets.
- [[nodiscard]] OffsetsOpt onRefreshRateChangeInitiated();
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();
// Called when we detect from VSYNC signals that the refresh rate changed.
// This way we can move out of early offsets if no longer necessary.
- [[nodiscard]] OffsetsOpt onRefreshRateChangeCompleted();
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();
- [[nodiscard]] OffsetsOpt onDisplayRefresh(bool usedGpuComposition);
+ [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
private:
- const Offsets& getNextOffsets() const REQUIRES(mMutex);
- [[nodiscard]] Offsets updateOffsets() EXCLUDES(mMutex);
- [[nodiscard]] Offsets updateOffsetsLocked() REQUIRES(mMutex);
+ const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
mutable std::mutex mMutex;
- OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
+ VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);
- Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
+ VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};
using Schedule = TransactionSchedule;
std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b35c68d..f6028d5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -122,8 +122,8 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
@@ -948,9 +948,10 @@
const nsecs_t period = hwConfig->getVsyncPeriod();
config.refreshRate = 1e9f / period;
- const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
- config.appVsyncOffset = offsets.late.app;
- config.sfVsyncOffset = offsets.late.sf;
+ const auto vsyncConfigSet =
+ mVsyncConfiguration->getConfigsForRefreshRate(config.refreshRate);
+ config.appVsyncOffset = vsyncConfigSet.late.appOffset;
+ config.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
config.configGroup = hwConfig->getConfigGroup();
// This is how far in advance a buffer must be queued for
@@ -1789,8 +1790,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- return mVsyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
}
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1822,7 +1823,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
// Inflate the expected present time if we're targetting the next vsync.
- return mVsyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? presentTime
+ : presentTime + stats.vsyncPeriod;
}
void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
@@ -2182,12 +2184,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
? (stats.vsyncPeriod -
- (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
- : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
+ (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod))
+ : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod);
- // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
+ // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -2197,7 +2199,7 @@
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
// Snapping also allows an app to precisely calculate
- // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
+ // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2977,16 +2979,18 @@
currentConfig, hal::PowerMode::OFF);
mRefreshRateStats->setConfigMode(currentConfig);
- mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
- mVsyncModulator.emplace(mPhaseConfiguration->getCurrentOffsets());
+ mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+ mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
// start the EventThread
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+ mScheduler->createConnection("app",
+ mVsyncConfiguration->getCurrentConfigs().late.appOffset,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
+ mScheduler->createConnection("sf",
+ mVsyncConfiguration->getCurrentConfigs().late.sfOffset,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
@@ -3007,16 +3011,21 @@
mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
vsyncPeriod);
+ static auto ignorePresentFences =
+ base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
+ mScheduler->setIgnorePresentFences(
+ ignorePresentFences ||
+ getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
}
void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- setPhaseOffsets(mVsyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()));
+ mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()));
}
-void SurfaceFlinger::setPhaseOffsets(const VsyncModulator::Offsets& offsets) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, offsets.app);
- mScheduler->setPhaseOffset(mSfConnectionHandle, offsets.sf);
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, config.appOffset);
+ mScheduler->setPhaseOffset(mSfConnectionHandle, config.sfOffset);
}
void SurfaceFlinger::commitTransaction() {
@@ -4456,7 +4465,7 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseConfiguration->dump(result);
+ mVsyncConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3c24158..24837b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -608,7 +608,7 @@
void initScheduler(PhysicalDisplayId primaryDisplayId);
void updatePhaseConfiguration(const RefreshRate&);
- void setPhaseOffsets(const VsyncModulator::Offsets&);
+ void setVsyncConfig(const VsyncModulator::VsyncConfig&);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -1214,7 +1214,7 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
+ std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
// Optional to defer construction until PhaseConfiguration is created.
std::optional<scheduler::VsyncModulator> mVsyncModulator;
@@ -1226,10 +1226,10 @@
hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
template <typename... Args,
- typename Handler = VsyncModulator::OffsetsOpt (VsyncModulator::*)(Args...)>
+ typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
void modulateVsync(Handler handler, Args... args) {
- if (const auto offsets = (*mVsyncModulator.*handler)(args...)) {
- setPhaseOffsets(*offsets);
+ if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+ setVsyncConfig(*config);
}
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 025d1a4..54ce60e 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -39,8 +39,8 @@
#include "DisplayHardware/ComposerHal.h"
#include "Scheduler/DispSync.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
namespace android::surfaceflinger {
@@ -58,10 +58,10 @@
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
const scheduler::RefreshRateConfigs& refreshRateConfigs) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
- return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
} else {
return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 1757fa8..04af8b0 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,7 @@
std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs&) override;
std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 1d710c4..784a77b 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -55,7 +55,7 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseConfiguration;
+class VsyncConfiguration;
class RefreshRateConfigs;
} // namespace scheduler
@@ -70,7 +70,7 @@
virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs&) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 4a59c19..7cc9cef 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -46,7 +46,6 @@
"LayerHistoryTest.cpp",
"LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
- "PhaseOffsetsTest.cpp",
"PromiseTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
@@ -64,6 +63,7 @@
"VsyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
+ "VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
deleted file mode 100644
index b50ddf5..0000000
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 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 <gmock/gmock.h>
-
-#include "Scheduler/PhaseOffsets.h"
-
-namespace android::scheduler {
-
-struct FakePhaseOffsets : PhaseConfiguration {
- static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
-
- Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
-
- Offsets getCurrentOffsets() const override {
- return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
- }
-
- void setRefreshRateFps(float) override {}
- void dump(std::string&) const override {}
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
new file mode 100644
index 0000000..4cd1e0a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 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 <gmock/gmock.h>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+namespace android::scheduler {
+
+struct FakePhaseOffsets : VsyncConfiguration {
+ static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+ static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
+
+ VsyncConfigSet getConfigsForRefreshRate(float) const override { return getCurrentConfigs(); }
+
+ VsyncConfigSet getCurrentConfigs() const override {
+ return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS}};
+ }
+
+ void setRefreshRateFps(float) override {}
+ void dump(std::string&) const override {}
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
deleted file mode 100644
index 96bb74b..0000000
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
-#include <gmock/gmock.h>
-#include <log/log.h>
-#include <thread>
-
-#include "Scheduler/PhaseOffsets.h"
-
-using namespace testing;
-
-namespace android::scheduler {
-
-class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
-public:
- TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
- nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
- : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
- sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
- appEarlyGlDuration) {}
-};
-
-class PhaseDurationTest : public testing::Test {
-protected:
- PhaseDurationTest()
- : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
- 21'000'000) {}
-
- ~PhaseDurationTest() = default;
-
- TestablePhaseOffsetsAsDurations mPhaseDurations;
-};
-
-/* ------------------------------------------------------------------------
- * Test cases
- */
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseDurations.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 6'166'667);
-
- EXPECT_EQ(offsets.late.app, 2'333'334);
-
- EXPECT_EQ(offsets.early.sf, 666'667);
-
- EXPECT_EQ(offsets.early.app, 833'334);
-
- EXPECT_EQ(offsets.earlyGpu.sf, 3'166'667);
-
- EXPECT_EQ(offsets.earlyGpu.app, 15'500'001);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseDurations.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 611'111);
-
- EXPECT_EQ(offsets.late.app, 2'333'333);
-
- EXPECT_EQ(offsets.early.sf, -4'888'889);
-
- EXPECT_EQ(offsets.early.app, 833'333);
-
- EXPECT_EQ(offsets.earlyGpu.sf, -2'388'889);
-
- EXPECT_EQ(offsets.earlyGpu.app, 9'944'444);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
- TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
-
- auto validateOffsets = [](auto& offsets) {
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGpu.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGpu.app, 1'000'000);
- };
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
- auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
- currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 57'527'208);
-
- EXPECT_EQ(offsets.late.app, 37'027'208);
-
- EXPECT_EQ(offsets.early.sf, 52'027'208);
-
- EXPECT_EQ(offsets.early.app, 35'527'208);
-
- EXPECT_EQ(offsets.earlyGpu.sf, 54'527'208);
-
- EXPECT_EQ(offsets.earlyGpu.app, 33'527'208);
-}
-
-TEST(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- struct PhaseOffsets : impl::PhaseOffsets {
- using impl::PhaseOffsets::PhaseOffsets;
- static PhaseOffsets get() {
- return {{60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {}, 10'000'000};
- }
- };
-
- auto offsets = PhaseOffsets::get().getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGpu.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGpu.app, 1'000'000);
-}
-
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index df15516..a1049d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -29,7 +29,7 @@
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
-#include "FakePhaseOffsets.h"
+#include "FakeVsyncConfiguration.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
@@ -77,7 +77,7 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
@@ -216,9 +216,9 @@
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/hal::PowerMode::OFF);
- mFlinger->mPhaseConfiguration =
- mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
- mFlinger->mVsyncModulator.emplace(mFlinger->mPhaseConfiguration->getCurrentOffsets());
+ mFlinger->mVsyncConfiguration =
+ mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
constexpr bool kUseContentDetectionV2 = false;
mScheduler =
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
new file mode 100644
index 0000000..72ee6db
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class TestableWorkDuration : public impl::WorkDuration {
+public:
+ TestableWorkDuration(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::WorkDuration({60.0f, 90.0f}, currentFps, sfDuration, appDuration, sfEarlyDuration,
+ appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
+};
+
+class WorkDurationTest : public testing::Test {
+protected:
+ WorkDurationTest()
+ : mWorkDuration(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+ 21'000'000) {}
+
+ ~WorkDurationTest() = default;
+
+ TestableWorkDuration mWorkDuration;
+};
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
+ mWorkDuration.setRefreshRateFps(60.0f);
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'334);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 666'667);
+ EXPECT_EQ(offsets.early.appOffset, 833'334);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'166'667);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 15'500'001);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
+ mWorkDuration.setRefreshRateFps(90.0f);
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 611'111);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'333);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, -4'888'889);
+ EXPECT_EQ(offsets.early.appOffset, 833'333);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, -2'388'889);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 9'944'444);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
+ TestableWorkDuration phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, vsyncPeriod);
+ };
+
+ const auto testForRefreshRate = [&](float refreshRate) {
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(refreshRate);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentConfigs();
+ auto offsets = phaseOffsetsWithDefaultValues.getConfigsForRefreshRate(refreshRate);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets,
+ std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / refreshRate)));
+ };
+
+ testForRefreshRate(90.0f);
+ testForRefreshRate(60.0f);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
+ EXPECT_EQ(offsets.late.appOffset, 37'027'208);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 52'027'208);
+ EXPECT_EQ(offsets.early.appOffset, 35'527'208);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 54'527'208);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 33'527'208);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs,
+ nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+ nsecs_t thresholdForNextVsync)
+ : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+ earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+ earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+ highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
+ highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
+ highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets{2'000'000, 6'000'000, 7'000'000, 8'000'000, 3'000'000,
+ 4'000'000, 2'000'000, 1'000'000, 2'000'000, 3'000'000,
+ 3'000'000, 4'000'000, 10'000'000};
+};
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 62'027'208ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 61'027'208ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 60'027'208ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 72'027'208ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 20'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 2'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index d7093c6..106da81 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -22,13 +22,19 @@
namespace android::scheduler {
class VsyncModulatorTest : public testing::Test {
- enum Offsets {
- SF_LATE,
- APP_LATE,
- SF_EARLY,
- APP_EARLY,
- SF_EARLY_GPU,
- APP_EARLY_GPU,
+ enum {
+ SF_OFFSET_LATE,
+ APP_OFFSET_LATE,
+ SF_DURATION_LATE,
+ APP_DURATION_LATE,
+ SF_OFFSET_EARLY,
+ APP_OFFSET_EARLY,
+ SF_DURATION_EARLY,
+ APP_DURATION_EARLY,
+ SF_OFFSET_EARLY_GPU,
+ APP_OFFSET_EARLY_GPU,
+ SF_DURATION_EARLY_GPU,
+ APP_DURATION_EARLY_GPU,
};
static VsyncModulator::TimePoint Now() {
@@ -41,25 +47,30 @@
VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES;
using Schedule = scheduler::TransactionSchedule;
+ using nanos = std::chrono::nanoseconds;
+ const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
+ nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
+ const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+ nanos(SF_DURATION_EARLY),
+ nanos(APP_DURATION_EARLY)};
+ const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
+ nanos(SF_DURATION_EARLY_GPU),
+ nanos(APP_DURATION_EARLY_GPU)};
- const VsyncModulator::Offsets kEarly{SF_EARLY, APP_EARLY};
- const VsyncModulator::Offsets kEarlyGpu{SF_EARLY_GPU, APP_EARLY_GPU};
- const VsyncModulator::Offsets kLate{SF_LATE, APP_LATE};
-
- const VsyncModulator::OffsetsConfig mOffsets = {kEarly, kEarlyGpu, kLate};
+ const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate};
VsyncModulator mVsyncModulator{mOffsets, Now};
- void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setPhaseOffsets(mOffsets)); }
+ void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); }
};
-#define CHECK_COMMIT(result, offsets) \
+#define CHECK_COMMIT(result, configs) \
EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
- EXPECT_EQ(offsets, mVsyncModulator.getOffsets());
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());
-#define CHECK_REFRESH(N, result, offsets) \
+#define CHECK_REFRESH(N, result, configs) \
for (int i = 0; i < N; i++) { \
EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
- EXPECT_EQ(offsets, mVsyncModulator.getOffsets()); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig()); \
}
TEST_F(VsyncModulatorTest, Late) {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 997b332..4068a16 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1029,9 +1029,7 @@
// conditionally add VK_GOOGLE_display_timing if present timestamps are
// supported by the driver:
- const std::string timestamp_property("service.sf.present_timestamp");
- android::base::WaitForPropertyCreation(timestamp_property);
- if (android::base::GetBoolProperty(timestamp_property, true)) {
+ if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
loader_extensions.push_back({
VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});