SF: update mInputFlinger on main thread am: 58fbf9fb2c
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/13165039
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Iaadcaf40d980ad9c92bb404193da37614c576a0d
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index a4b00f8..1429bc8 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -171,6 +171,7 @@
{ OPT, "events/clk/clk_disable/enable" },
{ OPT, "events/clk/clk_enable/enable" },
{ OPT, "events/power/cpu_frequency_limits/enable" },
+ { OPT, "events/power/suspend_resume/enable" },
} },
{ "membus", "Memory Bus Utilization", 0, {
{ REQ, "events/memory_bus/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index f1426b6..6e460a0 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -49,6 +49,8 @@
chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable
chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable
+ chmod 0666 /sys/kernel/tracing/events/power/suspend_resume/enable
chmod 0666 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
chmod 0666 /sys/kernel/tracing/events/cpufreq_interactive/enable
chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index f4d4f93..39f3f05 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1594,6 +1594,8 @@
static void DumpstateRadioCommon() {
DumpIpTablesAsRoot();
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+
if (!DropRootUser()) {
return;
}
@@ -1604,6 +1606,7 @@
DoKmsg();
DumpIpAddrAndRules();
dump_route_tables();
+ DumpHals();
RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
CommandOptions::WithTimeout(10).Build());
@@ -1673,8 +1676,6 @@
RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
- DumpHals();
-
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index caac2e8..856b545 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -876,6 +876,33 @@
auto scope_guard = android::base::make_scope_guard(deleter);
+ if (storageFlags & FLAG_STORAGE_DE) {
+ auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
+ auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
+ auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
+ snapshotId, package_name);
+
+ int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
+ if (rc != 0) {
+ return error(rc, "Failed to create folder " + to);
+ }
+
+ rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
+ if (rc != 0) {
+ return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
+ }
+
+ // Check if we have data to copy.
+ if (access(from.c_str(), F_OK) == 0) {
+ rc = copy_directory_recursive(from.c_str(), to.c_str());
+ }
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ clear_de_on_exit = true;
+ return res;
+ }
+ }
+
// The app may not have any data at all, in which case it's OK to skip here.
auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
if (access(from_ce.c_str(), F_OK) != 0) {
@@ -901,30 +928,6 @@
LOG(WARNING) << "Failed to clear code_cache of app " << packageName;
}
- if (storageFlags & FLAG_STORAGE_DE) {
- auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
- auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
- snapshotId, package_name);
-
- int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
- if (rc != 0) {
- return error(rc, "Failed to create folder " + to);
- }
-
- rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
- if (rc != 0) {
- return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
- }
-
- rc = copy_directory_recursive(from.c_str(), to.c_str());
- if (rc != 0) {
- res = error(rc, "Failed copying " + from + " to " + to);
- clear_de_on_exit = true;
- return res;
- }
- }
-
if (storageFlags & FLAG_STORAGE_CE) {
auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
@@ -2136,26 +2139,6 @@
return *_aidl_return ? ok() : error("viewcompiler failed");
}
-binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- const char* instruction_set = instructionSet.c_str();
-
- char boot_marker_path[PKG_PATH_MAX];
- sprintf(boot_marker_path,
- "%s/%s/%s/.booting",
- android_data_dir.c_str(),
- DALVIK_CACHE,
- instruction_set);
-
- ALOGV("mark_boot_complete : %s", boot_marker_path);
- if (unlink(boot_marker_path) != 0) {
- return error(StringPrintf("Failed to unlink %s", boot_marker_path));
- }
- return ok();
-}
-
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
const std::unique_ptr<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 2b7bf33..eba966d 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -123,7 +123,6 @@
int32_t uid);
binder::Status removeIdmap(const std::string& overlayApkPath);
binder::Status rmPackageDir(const std::string& packageDir);
- binder::Status markBootComplete(const std::string& instructionSet);
binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
int64_t cacheReservedBytes, int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 26e9984..b795c02 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -75,7 +75,6 @@
void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
void removeIdmap(@utf8InCpp String overlayApkPath);
void rmPackageDir(@utf8InCpp String packageDir);
- void markBootComplete(@utf8InCpp String instructionSet);
void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
long cacheReservedBytes, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index dbb4f22..5fb13a5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -336,6 +336,10 @@
? "dalvik.vm.dex2oat-threads"
: "dalvik.vm.boot-dex2oat-threads";
std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s");
+ const char* cpu_set_property = post_bootcomplete
+ ? "dalvik.vm.dex2oat-cpu-set"
+ : "dalvik.vm.boot-dex2oat-cpu-set";
+ std::string dex2oat_cpu_set_arg = MapPropertyToArg(cpu_set_property, "--cpu-set=%s");
std::string bootclasspath;
char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
@@ -507,6 +511,7 @@
AddArg(image_block_size_arg);
AddArg(dex2oat_compiler_filter_arg);
AddArg(dex2oat_threads_arg);
+ AddArg(dex2oat_cpu_set_arg);
AddArg(dex2oat_swap_fd);
AddArg(dex2oat_image_fd);
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index de7b249..9bc5fc8 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -478,6 +478,10 @@
"-j",
false,
cmd);
+ AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
+ "--cpu-set=",
+ false,
+ cmd);
AddCompilerOptionFromSystemProperty(
StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
"--instruction-set-variant=",
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index ad7791e..50f117d 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -40,7 +40,6 @@
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
- <feature name="android.software.print" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 4365a3c..d23e3b7 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -37,7 +37,6 @@
virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
- virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
};
@@ -51,8 +50,7 @@
enum {
SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_INPUT_CHANNEL_TRANSACTION,
- UNREGISTER_INPUT_CHANNEL_TRANSACTION,
- TRANSFER_TOUCH_FOCUS
+ UNREGISTER_INPUT_CHANNEL_TRANSACTION
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 857bbf9..2978b53 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -49,9 +49,10 @@
}
status_t append(const char* txt, size_t len) {
+ if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
if ((len+bufferPos) > bufferSize) {
+ if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((len+bufferPos)*3)/2;
- if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow
void* b = realloc(buffer, newSize);
if (!b) return NO_MEMORY;
buffer = (char*)b;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 5ad3027..11441ec 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -520,8 +520,10 @@
const sp<ProcessState> proc(ProcessState::self());
// grow objects
if (mObjectsCapacity < mObjectsSize + numObjects) {
+ if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow
+ if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mObjectsSize + numObjects)*3)/2;
- if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
binder_size_t *objects =
(binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == (binder_size_t*)nullptr) {
@@ -1399,8 +1401,10 @@
if (err != NO_ERROR) return err;
}
if (!enoughObjects) {
+ if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow
+ if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mObjectsSize+2)*3)/2;
- if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == nullptr) return NO_MEMORY;
mObjects = objects;
@@ -2685,6 +2689,8 @@
return BAD_VALUE;
}
+ if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
+ if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 40f6b43..0477801 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,7 +16,9 @@
#include <set>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <dumputils/dump_utils.h>
#include <log/log.h>
@@ -46,6 +48,8 @@
static const char* hal_interfaces_to_dump[] {
"android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
+ "android.hardware.audio@5.0::IDevicesFactory",
+ "android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.drm@1.0::IDrmFactory",
@@ -60,16 +64,40 @@
"android.hardware.sensors@1.0::ISensors",
"android.hardware.thermal@2.0::IThermal",
"android.hardware.vr@1.0::IVr",
+ "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+ "android.hardware.automotive.vehicle@2.0::IVehicle",
+ "android.hardware.automotive.evs@1.0::IEvsCamera",
NULL,
};
-bool should_dump_hal_interface(const char* interface) {
+/* list of extra hal interfaces to dump containing process during native dumps */
+// This is filled when dumpstate is called.
+static std::set<const std::string> extra_hal_interfaces_to_dump;
+
+static void read_extra_hals_to_dump_from_property() {
+ // extra hals to dump are already filled
+ if (extra_hal_interfaces_to_dump.size() > 0) {
+ return;
+ }
+ std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
+ std::vector<std::string> tokens = android::base::Split(value, ",");
+ for (const auto &token : tokens) {
+ std::string trimmed_token = android::base::Trim(token);
+ if (trimmed_token.length() == 0) {
+ continue;
+ }
+ extra_hal_interfaces_to_dump.insert(trimmed_token);
+ }
+}
+
+// check if interface is included in either default hal list or extra hal list
+bool should_dump_hal_interface(const std::string& interface) {
for (const char** i = hal_interfaces_to_dump; *i; i++) {
- if (!strcmp(*i, interface)) {
+ if (interface == *i) {
return true;
}
}
- return false;
+ return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end();
}
bool should_dump_native_traces(const char* path) {
@@ -89,13 +117,15 @@
sp<IServiceManager> manager = IServiceManager::getService();
std::set<int> pids;
+ read_extra_hals_to_dump_from_property();
+
Return<void> ret = manager->debugDump([&](auto& hals) {
for (const auto &info : hals) {
if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
continue;
}
- if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+ if (!should_dump_hal_interface(info.interfaceName)) {
continue;
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index bb9e263..6e59cc5 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -139,12 +139,8 @@
return env;
}
-int GraphicsEnv::getCanLoadSystemLibraries() {
- if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
- // Return an integer value since this crosses library boundaries
- return 1;
- }
- return 0;
+bool GraphicsEnv::isDebuggable() {
+ return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
}
void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 937bcd9..227b458 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -84,7 +84,16 @@
public:
static GraphicsEnv& getInstance();
- int getCanLoadSystemLibraries();
+ // Check if the process is debuggable. It returns false except in any of the
+ // following circumstances:
+ // 1. ro.debuggable=1 (global debuggable enabled).
+ // 2. android:debuggable="true" in the manifest for an individual app.
+ // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
+ // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
+ // <meta-data android:name="com.android.graphics.injectLayers.enable"
+ // android:value="true"/>
+ // in the application manifest.
+ bool isDebuggable();
// Set a search path for loading graphics drivers. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 34575f5..e3e63ee 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -34,6 +34,7 @@
"BufferItemConsumer.cpp",
"ConsumerBase.cpp",
"CpuConsumer.cpp",
+ "DebugEGLImageTracker.cpp",
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
"GuiConfig.cpp",
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 528bfb1..3a7cb44 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -166,7 +166,9 @@
mCore->mFreeBuffers.push_back(front->mSlot);
}
- listener = mCore->mConnectedProducerListener;
+ if (mCore->mBufferReleasedCbEnabled) {
+ listener = mCore->mConnectedProducerListener;
+ }
++numDroppedBuffers;
}
@@ -457,7 +459,9 @@
mCore->mFreeBuffers.push_back(slot);
}
- listener = mCore->mConnectedProducerListener;
+ if (mCore->mBufferReleasedCbEnabled) {
+ listener = mCore->mConnectedProducerListener;
+ }
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
mCore->mDequeueCondition.notify_all();
@@ -668,7 +672,7 @@
BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
VALIDATE_CONSISTENCY();
- if (delta < 0) {
+ if (delta < 0 && mCore->mBufferReleasedCbEnabled) {
listener = mCore->mConsumerListener;
}
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index e0e3431..0264bd2 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -65,6 +65,7 @@
mConnectedApi(NO_CONNECTED_API),
mLinkedToDeath(),
mConnectedProducerListener(),
+ mBufferReleasedCbEnabled(false),
mSlots(),
mQueue(),
mFreeSlots(),
@@ -260,6 +261,12 @@
}
void BufferQueueCore::discardFreeBuffersLocked() {
+ // Notify producer about the discarded buffers.
+ if (mConnectedProducerListener != nullptr && mFreeBuffers.size() > 0) {
+ std::vector<int32_t> freeBuffers(mFreeBuffers.begin(), mFreeBuffers.end());
+ mConnectedProducerListener->onBuffersDiscarded(freeBuffers);
+ }
+
for (int s : mFreeBuffers) {
mFreeSlots.insert(s);
clearBufferSlotLocked(s);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9c311a3..a317aaf 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -936,6 +936,15 @@
}
}
+ // Make sure to merge the damage rect from the frame we're about
+ // to drop into the new frame's damage rect.
+ if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT ||
+ item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) {
+ item.mSurfaceDamage = Region::INVALID_REGION;
+ } else {
+ item.mSurfaceDamage |= last.mSurfaceDamage;
+ }
+
// Overwrite the droppable buffer with the incoming one
mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
frameReplacedListener = mCore->mConsumerListener;
@@ -1212,9 +1221,8 @@
}
mCore->mLinkedToDeath = listener;
}
- if (listener->needsReleaseNotify()) {
- mCore->mConnectedProducerListener = listener;
- }
+ mCore->mConnectedProducerListener = listener;
+ mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
}
break;
default:
diff --git a/libs/gui/DebugEGLImageTracker.cpp b/libs/gui/DebugEGLImageTracker.cpp
new file mode 100644
index 0000000..ab6f364
--- /dev/null
+++ b/libs/gui/DebugEGLImageTracker.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gui/DebugEGLImageTracker.h>
+
+#include <cinttypes>
+#include <unordered_map>
+
+using android::base::StringAppendF;
+
+std::mutex DebugEGLImageTracker::mInstanceLock;
+std::atomic<DebugEGLImageTracker *> DebugEGLImageTracker::mInstance;
+
+class DebugEGLImageTrackerNoOp : public DebugEGLImageTracker {
+public:
+ DebugEGLImageTrackerNoOp() = default;
+ ~DebugEGLImageTrackerNoOp() override = default;
+ void create(const char * /*from*/) override {}
+ void destroy(const char * /*from*/) override {}
+
+ void dump(std::string & /*result*/) override {}
+};
+
+class DebugEGLImageTrackerImpl : public DebugEGLImageTracker {
+public:
+ DebugEGLImageTrackerImpl() = default;
+ ~DebugEGLImageTrackerImpl() override = default;
+ void create(const char * /*from*/) override;
+ void destroy(const char * /*from*/) override;
+
+ void dump(std::string & /*result*/) override;
+
+private:
+ std::mutex mLock;
+ std::unordered_map<std::string, int64_t> mCreateTracker;
+ std::unordered_map<std::string, int64_t> mDestroyTracker;
+
+ int64_t mTotalCreated = 0;
+ int64_t mTotalDestroyed = 0;
+};
+
+DebugEGLImageTracker *DebugEGLImageTracker::getInstance() {
+ std::lock_guard lock(mInstanceLock);
+ if (mInstance == nullptr) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.enable_egl_image_tracker", value, "0");
+ const bool enabled = static_cast<bool>(atoi(value));
+
+ if (enabled) {
+ mInstance = new DebugEGLImageTrackerImpl();
+ } else {
+ mInstance = new DebugEGLImageTrackerNoOp();
+ }
+ }
+
+ return mInstance;
+}
+
+void DebugEGLImageTrackerImpl::create(const char *from) {
+ std::lock_guard lock(mLock);
+ mCreateTracker[from]++;
+ mTotalCreated++;
+}
+
+void DebugEGLImageTrackerImpl::destroy(const char *from) {
+ std::lock_guard lock(mLock);
+ mDestroyTracker[from]++;
+ mTotalDestroyed++;
+}
+
+void DebugEGLImageTrackerImpl::dump(std::string &result) {
+ std::lock_guard lock(mLock);
+ StringAppendF(&result, "Live EGL Image objects: %" PRIi64 "\n",
+ mTotalCreated - mTotalDestroyed);
+ StringAppendF(&result, "Total EGL Image created: %" PRIi64 "\n", mTotalCreated);
+ for (const auto &[from, count] : mCreateTracker) {
+ StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count);
+ }
+ StringAppendF(&result, "Total EGL Image destroyed: %" PRIi64 "\n", mTotalDestroyed);
+ for (const auto &[from, count] : mDestroyTracker) {
+ StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count);
+ }
+}
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index f5cf1c4..b8faa2d 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -32,10 +32,11 @@
// ---------------------------------------------------------------------------
-DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
+DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::ConfigChanged configChanged) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != nullptr) {
- mEventConnection = sf->createDisplayEventConnection(vsyncSource);
+ mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
if (mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 8d66154..8199c98 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -34,6 +34,7 @@
#include <math/mat4.h>
#include <gui/BufferItem.h>
+#include <gui/DebugEGLImageTracker.h>
#include <gui/GLConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@@ -944,6 +945,7 @@
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("~EglImage: eglDestroyImageKHR failed");
}
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
eglTerminate(mEglDisplay);
}
}
@@ -957,6 +959,7 @@
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("createIfNeeded: eglDestroyImageKHR failed");
}
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
eglTerminate(mEglDisplay);
mEglImage = EGL_NO_IMAGE_KHR;
mEglDisplay = EGL_NO_DISPLAY;
@@ -1006,7 +1009,10 @@
EGLint error = eglGetError();
ALOGE("error creating EGLImage: %#x", error);
eglTerminate(dpy);
+ } else {
+ DEBUG_EGL_IMAGE_TRACKER_CREATE();
}
+
return image;
}
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 936063a..808e336 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -24,6 +24,7 @@
enum {
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
NEEDS_RELEASE_NOTIFY,
+ ON_BUFFERS_DISCARDED,
};
class BpProducerListener : public BpInterface<IProducerListener>
@@ -56,6 +57,13 @@
}
return result;
}
+
+ virtual void onBuffersDiscarded(const std::vector<int>& discardedSlots) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+ data.writeInt32Vector(discardedSlots);
+ remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -76,6 +84,10 @@
virtual bool needsReleaseNotify() override {
return mBase->needsReleaseNotify();
}
+
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& discardedSlots) override {
+ return mBase->onBuffersDiscarded(discardedSlots);
+ }
};
IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener,
@@ -92,6 +104,17 @@
CHECK_INTERFACE(IProducerListener, data, reply);
reply->writeBool(needsReleaseNotify());
return NO_ERROR;
+ case ON_BUFFERS_DISCARDED: {
+ CHECK_INTERFACE(IProducerListener, data, reply);
+ std::vector<int32_t> discardedSlots;
+ status_t result = data.readInt32Vector(&discardedSlots);
+ if (result != NO_ERROR) {
+ ALOGE("ON_BUFFERS_DISCARDED failed to read discardedSlots: %d", result);
+ return result;
+ }
+ onBuffersDiscarded(discardedSlots);
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
@@ -104,4 +127,7 @@
return true;
}
+void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) {
+}
+
} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 60d246a..2333255 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -278,8 +278,8 @@
return NO_ERROR;
}
- virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource)
- {
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,
+ ConfigChanged configChanged) {
Parcel data, reply;
sp<IDisplayEventConnection> result;
int err = data.writeInterfaceToken(
@@ -288,6 +288,7 @@
return result;
}
data.writeInt32(static_cast<int32_t>(vsyncSource));
+ data.writeInt32(static_cast<int32_t>(configChanged));
err = remote()->transact(
BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
data, &reply);
@@ -1155,8 +1156,11 @@
}
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IDisplayEventConnection> connection(createDisplayEventConnection(
- static_cast<ISurfaceComposer::VsyncSource>(data.readInt32())));
+ auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32());
+ auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32());
+
+ sp<IDisplayEventConnection> connection(
+ createDisplayEventConnection(vsyncSource, configChanged));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index e6eb327..b822319 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -35,6 +35,7 @@
#include <ui/DisplayStatInfo.h>
#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
@@ -1287,6 +1288,14 @@
}
int Surface::connect(
+ int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) {
+ if (sListener != nullptr) {
+ mListenerProxy = new ProducerListenerProxy(this, sListener);
+ }
+ return connect(api, mListenerProxy, reportBufferRemoval);
+}
+
+int Surface::connect(
int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
ALOGV("Surface::connect");
@@ -1684,6 +1693,28 @@
}
}
+status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
+ std::vector<sp<GraphicBuffer>>* outBuffers) {
+ ALOGV("Surface::getAndFlushBuffersFromSlots");
+ for (int32_t i : slots) {
+ if (i < 0 || i >= NUM_BUFFER_SLOTS) {
+ ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i);
+ return BAD_VALUE;
+ }
+ }
+
+ Mutex::Autolock lock(mMutex);
+ for (int32_t i : slots) {
+ if (mSlots[i].buffer == nullptr) {
+ ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i);
+ continue;
+ }
+ outBuffers->push_back(mSlots[i].buffer);
+ mSlots[i].buffer = nullptr;
+ }
+ return OK;
+}
+
void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) {
ATRACE_CALL();
ALOGV("Surface::setSurfaceDamage");
@@ -1920,7 +1951,8 @@
return OK;
}
-status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer) {
+status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
+ Dataspace dataspace) {
if (buffer == nullptr) {
return BAD_VALUE;
}
@@ -1929,6 +1961,11 @@
if (err != OK) {
return err;
}
+ ui::Dataspace tmpDataspace = surface->getBuffersDataSpace();
+ err = surface->setBuffersDataSpace(dataspace);
+ if (err != OK) {
+ return err;
+ }
err = surface->attachBuffer(buffer->getNativeBuffer());
if (err != OK) {
return err;
@@ -1937,8 +1974,30 @@
if (err != OK) {
return err;
}
+ err = surface->setBuffersDataSpace(tmpDataspace);
+ if (err != OK) {
+ return err;
+ }
err = surface->disconnect(NATIVE_WINDOW_API_CPU);
return err;
}
+void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_t>& slots) {
+ ATRACE_CALL();
+ sp<Surface> parent = mParent.promote();
+ if (parent == nullptr) {
+ return;
+ }
+
+ std::vector<sp<GraphicBuffer>> discardedBufs;
+ status_t res = parent->getAndFlushBuffersFromSlots(slots, &discardedBufs);
+ if (res != OK) {
+ ALOGE("%s: Failed to get buffers from slots: %s(%d)", __FUNCTION__,
+ strerror(-res), res);
+ return;
+ }
+
+ mSurfaceListener->onBuffersDiscarded(discardedBufs);
+}
+
}; // namespace android
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 690a85f..17617bc 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -189,8 +189,12 @@
sp<IProducerListener> mLinkedToDeath;
// mConnectedProducerListener is used to handle the onBufferReleased
- // notification.
+ // and onBuffersDiscarded notification.
sp<IProducerListener> mConnectedProducerListener;
+ // mBufferReleasedCbEnabled is used to indicate whether onBufferReleased()
+ // callback is registered by the listener. When set to false,
+ // mConnectedProducerListener will not trigger onBufferReleased() callback.
+ bool mBufferReleasedCbEnabled;
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
diff --git a/libs/gui/include/gui/DebugEGLImageTracker.h b/libs/gui/include/gui/DebugEGLImageTracker.h
new file mode 100644
index 0000000..5d369c9
--- /dev/null
+++ b/libs/gui/include/gui/DebugEGLImageTracker.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 <atomic>
+#include <mutex>
+#include <string>
+
+class DebugEGLImageTracker {
+public:
+ static DebugEGLImageTracker *getInstance();
+
+ virtual void create(const char *from) = 0;
+ virtual void destroy(const char *from) = 0;
+
+ virtual void dump(std::string &result) = 0;
+
+protected:
+ DebugEGLImageTracker() = default;
+ virtual ~DebugEGLImageTracker() = default;
+ DebugEGLImageTracker(const DebugEGLImageTracker &) = delete;
+
+ static std::mutex mInstanceLock;
+ static std::atomic<DebugEGLImageTracker *> mInstance;
+};
+
+#define DEBUG_EGL_IMAGE_TRACKER_CREATE() \
+ (DebugEGLImageTracker::getInstance()->create(__PRETTY_FUNCTION__))
+#define DEBUG_EGL_IMAGE_TRACKER_DESTROY() \
+ (DebugEGLImageTracker::getInstance()->destroy(__PRETTY_FUNCTION__))
\ No newline at end of file
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 22de751..a558cf9 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -88,10 +88,13 @@
* DisplayEventReceiver creates and registers an event connection with
* SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
* or requestNextVsync to receive them.
+ * To receive Config Changed events specify this in the constructor.
* Other events start being delivered immediately.
*/
explicit DisplayEventReceiver(
- ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress);
/*
* ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index a13d8e4..32a3690 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_GUI_IPRODUCERLISTENER_H
#define ANDROID_GUI_IPRODUCERLISTENER_H
+#include <vector>
+
#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
#include <binder/IInterface.h>
@@ -44,6 +46,9 @@
// multiple threads.
virtual void onBufferReleased() = 0; // Asynchronous
virtual bool needsReleaseNotify() = 0;
+ // onBuffersFreed is called from IGraphicBufferConsumer::discardFreeBuffers
+ // to notify the producer that certain free buffers are discarded by the consumer.
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
};
class IProducerListener : public ProducerListener, public IInterface
@@ -65,6 +70,7 @@
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0);
virtual bool needsReleaseNotify();
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
};
class DummyProducerListener : public BnProducerListener
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index e89d3a4..7060bb1 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -90,6 +90,8 @@
eVsyncSourceSurfaceFlinger = 1
};
+ enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
+
/*
* Create a connection with SurfaceFlinger.
*/
@@ -97,7 +99,8 @@
/* return an IDisplayEventConnection */
virtual sp<IDisplayEventConnection> createDisplayEventConnection(
- VsyncSource vsyncSource = eVsyncSourceApp) = 0;
+ VsyncSource vsyncSource = eVsyncSourceApp,
+ ConfigChanged configChanged = eConfigChangedSuppress) = 0;
/* create a virtual display
* requires ACCESS_SURFACE_FLINGER permission.
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 0c471bb..a5641b0 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -20,6 +20,7 @@
#include <gui/BufferQueueDefs.h>
#include <gui/HdrMetadata.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
#include <ui/ANativeObjectBase.h>
#include <ui/GraphicTypes.h>
@@ -35,6 +36,21 @@
class ISurfaceComposer;
+/* This is the same as ProducerListener except that onBuffersDiscarded is
+ * called with a vector of graphic buffers instead of buffer slots.
+ */
+class SurfaceListener : public virtual RefBase
+{
+public:
+ SurfaceListener() = default;
+ virtual ~SurfaceListener() = default;
+
+ virtual void onBufferReleased() = 0;
+ virtual bool needsReleaseNotify() = 0;
+
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0;
+};
+
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
@@ -283,6 +299,10 @@
sp<Fence>* outFence);
virtual int attachBuffer(ANativeWindowBuffer*);
+ virtual int connect(
+ int api, bool reportBufferRemoval,
+ const sp<SurfaceListener>& sListener);
+
// When client connects to Surface with reportBufferRemoval set to true, any buffers removed
// from this Surface will be collected and returned here. Once this method returns, these
// buffers will no longer be referenced by this Surface unless they are attached to this
@@ -292,12 +312,33 @@
ui::Dataspace getBuffersDataSpace();
- static status_t attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer);
+ static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
+ ui::Dataspace dataspace);
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
+ class ProducerListenerProxy : public BnProducerListener {
+ public:
+ ProducerListenerProxy(wp<Surface> parent, sp<SurfaceListener> listener)
+ : mParent(parent), mSurfaceListener(listener) {}
+ virtual ~ProducerListenerProxy() {}
+
+ virtual void onBufferReleased() {
+ mSurfaceListener->onBufferReleased();
+ }
+
+ virtual bool needsReleaseNotify() {
+ return mSurfaceListener->needsReleaseNotify();
+ }
+
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
+ private:
+ wp<Surface> mParent;
+ sp<SurfaceListener> mSurfaceListener;
+ };
+
void querySupportedTimestampsLocked() const;
void freeAllBuffers();
@@ -465,6 +506,10 @@
bool mReportRemovedBuffers = false;
std::vector<sp<GraphicBuffer>> mRemovedBuffers;
+
+ sp<IProducerListener> mListenerProxy;
+ status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
+ std::vector<sp<GraphicBuffer>>* outBuffers);
};
} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 119e888..406f21f 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -998,12 +998,31 @@
ASSERT_EQ(true, thirdSegment.usedThirdBuffer);
}
+struct BufferDiscardedListener : public BnProducerListener {
+public:
+ BufferDiscardedListener() = default;
+ virtual ~BufferDiscardedListener() = default;
+
+ virtual void onBufferReleased() {}
+ virtual bool needsReleaseNotify() { return false; }
+ virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) {
+ mDiscardedSlots.insert(mDiscardedSlots.end(), slots.begin(), slots.end());
+ }
+
+ const std::vector<int32_t>& getDiscardedSlots() const { return mDiscardedSlots; }
+private:
+ // No need to use lock given the test triggers the listener in the same
+ // thread context.
+ std::vector<int32_t> mDiscardedSlots;
+};
+
TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
createBufferQueue();
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ sp<BufferDiscardedListener> pl(new BufferDiscardedListener);
+ ASSERT_EQ(OK, mProducer->connect(pl,
NATIVE_WINDOW_API_CPU, false, &output));
int slot = BufferQueue::INVALID_BUFFER_SLOT;
@@ -1044,12 +1063,19 @@
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ int releasedSlot = item.mSlot;
+
// Acquire 1 buffer, leaving 1 filled buffer in queue
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
// Now discard the free buffers
ASSERT_EQ(OK, mConsumer->discardFreeBuffers());
+ // Check onBuffersDiscarded is called with correct slots
+ auto buffersDiscarded = pl->getDiscardedSlots();
+ ASSERT_EQ(buffersDiscarded.size(), 1);
+ ASSERT_EQ(buffersDiscarded[0], releasedSlot);
+
// Check no free buffers in dump
String8 dumpString;
mConsumer->dumpState(String8{}, &dumpString);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index ff1ba0a..66a8a04 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -133,27 +133,6 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
}
- void expectMotionEvent(int motionEventType, int x, int y) {
- InputEvent *ev = consumeEvent();
- ASSERT_NE(ev, nullptr);
- ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION);
- MotionEvent *mev = static_cast<MotionEvent *>(ev);
- EXPECT_EQ(motionEventType, mev->getAction());
- EXPECT_EQ(x, mev->getX(0));
- EXPECT_EQ(y, mev->getY(0));
- }
-
- void expectNoMotionEvent(int motionEventType) {
- InputEvent *ev = consumeEvent();
- if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) {
- // Didn't find an event or a motion event so assume action didn't occur.
- return;
- }
-
- MotionEvent *mev = static_cast<MotionEvent *>(ev);
- EXPECT_NE(motionEventType, mev->getAction());
- }
-
~InputSurface() {
mInputFlinger->unregisterInputChannel(mServerChannel);
}
@@ -278,15 +257,6 @@
}
}
-void injectMotionEvent(std::string event, int x, int y) {
- char *buf1, *buf2;
- asprintf(&buf1, "%d", x);
- asprintf(&buf2, "%d", y);
- if (fork() == 0) {
- execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL);
- }
-}
-
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
@@ -422,9 +392,11 @@
surface->expectTap(1, 1);
}
-// Ensure we send the input to the right surface when the surface visibility changes due to the
-// first buffer being submitted. ref: b/120839715
-TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
+// TODO(b/139494112) update tests once we define expected behavior
+// Ensure we still send input to the surface regardless of surface visibility changes due to the
+// first buffer being submitted or alpha changes.
+// Original bug ref: b/120839715
+TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> bufferSurface =
InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
@@ -433,14 +405,14 @@
bufferSurface->showAt(10, 10);
injectTap(11, 11);
- bgSurface->expectTap(1, 1);
+ bufferSurface->expectTap(1, 1);
postBuffer(bufferSurface->mSurfaceControl);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
}
-TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
+TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> bufferSurface =
InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
@@ -455,10 +427,10 @@
bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
injectTap(11, 11);
- bgSurface->expectTap(1, 1);
+ bufferSurface->expectTap(1, 1);
}
-TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) {
+TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
@@ -471,7 +443,7 @@
fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
injectTap(11, 11);
- bgSurface->expectTap(1, 1);
+ fgSurface->expectTap(1, 1);
}
TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
@@ -491,26 +463,6 @@
bgSurface->expectTap(1, 1);
}
-TEST_F(InputSurfacesTest, transfer_touch_focus) {
- std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100);
-
- fromSurface->showAt(10, 10);
- injectMotionEvent("DOWN", 11, 11);
- fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1);
-
- std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100);
- toSurface->showAt(10, 10);
-
- sp<IBinder> fromToken = fromSurface->mServerChannel->getToken();
- sp<IBinder> toToken = toSurface->mServerChannel->getToken();
- SurfaceComposerClient::Transaction t;
- t.transferTouchFocus(fromToken, toToken).apply(true);
-
- injectMotionEvent("UP", 11, 11);
- toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1);
- fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP);
-}
-
TEST_F(InputSurfacesTest, input_respects_outscreen) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(-1, -1);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 62a1d76..293789b 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -57,6 +57,37 @@
static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+class DummySurfaceListener : public SurfaceListener {
+public:
+ DummySurfaceListener(bool enableReleasedCb = false) :
+ mEnableReleaseCb(enableReleasedCb),
+ mBuffersReleased(0) {}
+ virtual ~DummySurfaceListener() = default;
+
+ virtual void onBufferReleased() {
+ mBuffersReleased++;
+ }
+ virtual bool needsReleaseNotify() {
+ return mEnableReleaseCb;
+ }
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) {
+ mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end());
+ }
+
+ int getReleaseNotifyCount() const {
+ return mBuffersReleased;
+ }
+ const std::vector<sp<GraphicBuffer>>& getDiscardedBuffers() const {
+ return mDiscardedBuffers;
+ }
+private:
+ // No need to use lock given the test triggers the listener in the same
+ // thread context.
+ bool mEnableReleaseCb;
+ int32_t mBuffersReleased;
+ std::vector<sp<GraphicBuffer>> mDiscardedBuffers;
+};
+
class SurfaceTest : public ::testing::Test {
protected:
SurfaceTest() {
@@ -88,6 +119,86 @@
mComposerClient->dispose();
}
+ void testSurfaceListener(bool hasSurfaceListener, bool enableReleasedCb,
+ int32_t extraDiscardedBuffers) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+ consumer->setConsumerName(String8("TestConsumer"));
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<DummySurfaceListener> listener;
+ if (hasSurfaceListener) {
+ listener = new DummySurfaceListener(enableReleasedCb);
+ }
+ ASSERT_EQ(OK, surface->connect(
+ NATIVE_WINDOW_API_CPU,
+ /*reportBufferRemoval*/true,
+ /*listener*/listener));
+ const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ ANativeWindowBuffer* buffers[BUFFER_COUNT];
+ // Dequeue first to allocate a number of buffers
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffers[i]));
+ }
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], -1));
+ }
+
+ ANativeWindowBuffer* buffer;
+ // Fill BUFFER_COUNT-1 buffers
+ for (int i = 0; i < BUFFER_COUNT-1; i++) {
+ ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer));
+ ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, -1));
+ }
+
+ // Dequeue 1 buffer
+ ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer));
+
+ // Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called.
+ std::vector<BufferItem> releasedItems;
+ releasedItems.resize(1+extraDiscardedBuffers);
+ for (int i = 0; i < releasedItems.size(); i++) {
+ ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0));
+ ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot,
+ releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+ Fence::NO_FENCE));
+ }
+ int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0);
+ if (hasSurfaceListener) {
+ ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount());
+ }
+
+ // Acquire 1 buffer, leaving 1+extraDiscardedBuffers filled buffer in queue
+ BufferItem item;
+ ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&item, 0));
+
+ // Discard free buffers
+ ASSERT_EQ(NO_ERROR, consumer->discardFreeBuffers());
+
+ if (hasSurfaceListener) {
+ ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount());
+
+ // Check onBufferDiscarded is called with correct buffer
+ auto discardedBuffers = listener->getDiscardedBuffers();
+ ASSERT_EQ(discardedBuffers.size(), releasedItems.size());
+ for (int i = 0; i < releasedItems.size(); i++) {
+ ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer);
+ }
+
+ ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount());
+ }
+
+ // Disconnect the surface
+ ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+ }
+
sp<Surface> mSurface;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -480,6 +591,21 @@
ASSERT_LE(removedBuffers.size(), 1u);
}
+TEST_F(SurfaceTest, SurfaceListenerTest) {
+ // Test discarding 1 free buffers with no listener
+ testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/0);
+ // Test discarding 2 free buffers with no listener
+ testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/1);
+ // Test discarding 1 free buffers with a listener, disabling onBufferReleased
+ testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/0);
+ // Test discarding 2 free buffers with a listener, disabling onBufferReleased
+ testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/1);
+ // Test discarding 1 free buffers with a listener, enabling onBufferReleased
+ testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/0);
+ // Test discarding 3 free buffers with a listener, enabling onBufferReleased
+ testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/2);
+}
+
TEST_F(SurfaceTest, TestGetLastDequeueStartTime) {
sp<ANativeWindow> anw(mSurface);
ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
@@ -548,8 +674,8 @@
}
sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
- sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
- override {
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override {
return nullptr;
}
sp<IBinder> createDisplay(const String8& /*displayName*/,
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index d6a73bf..de3a23d 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -45,16 +45,6 @@
IBinder::FLAG_ONEWAY);
}
- virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-
- data.writeStrongBinder(fromToken);
- data.writeStrongBinder(toToken);
- remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-
virtual void registerInputChannel(const sp<InputChannel>& channel) {
Parcel data, reply;
data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
@@ -104,13 +94,6 @@
unregisterInputChannel(channel);
break;
}
- case TRANSFER_TOUCH_FOCUS: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<IBinder> fromToken = data.readStrongBinder();
- sp<IBinder> toToken = data.readStrongBinder();
- transferTouchFocus(fromToken, toToken);
- break;
- }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index 8a4298a..ce76e3f 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -42,13 +42,13 @@
void TouchVideoFrame::rotate(int32_t orientation) {
switch (orientation) {
case DISPLAY_ORIENTATION_90:
- rotateQuarterTurn(true /*clockwise*/);
+ rotateQuarterTurn(false /*clockwise*/);
break;
case DISPLAY_ORIENTATION_180:
rotate180();
break;
case DISPLAY_ORIENTATION_270:
- rotateQuarterTurn(false /*clockwise*/);
+ rotateQuarterTurn(true /*clockwise*/);
break;
}
}
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 815424e..fe06cad 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -85,14 +85,14 @@
TEST(TouchVideoFrame, Rotate90_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_90);
ASSERT_EQ(frame, frameRotated);
}
@@ -170,14 +170,14 @@
TEST(TouchVideoFrame, Rotate270_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_270);
ASSERT_EQ(frame, frameRotated);
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 36211ca..cc252d6 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -26,6 +26,7 @@
"libgui",
"liblog",
"libnativewindow",
+ "libprocessgroup",
"libsync",
"libui",
"libutils",
@@ -51,6 +52,7 @@
"gl/GLExtensions.cpp",
"gl/GLFramebuffer.cpp",
"gl/GLImage.cpp",
+ "gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
],
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 46a8e9e..d2a7525 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -19,9 +19,8 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "GLESRenderEngine.h"
-
-#include <math.h>
+#include <sched.h>
+#include <cmath>
#include <fstream>
#include <sstream>
#include <unordered_set>
@@ -31,6 +30,7 @@
#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
+#include <gui/DebugEGLImageTracker.h>
#include <renderengine/Mesh.h>
#include <renderengine/Texture.h>
#include <renderengine/private/Description.h>
@@ -42,6 +42,7 @@
#include <ui/Region.h>
#include <utils/KeyedVector.h>
#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
#include "GLExtensions.h"
#include "GLFramebuffer.h"
#include "GLImage.h"
@@ -422,10 +423,13 @@
mTraceGpuCompletion = true;
mFlushTracer = std::make_unique<FlushTracer>(this);
}
+ mImageManager = std::make_unique<ImageManager>(this);
mDrawingBuffer = createFramebuffer();
}
GLESRenderEngine::~GLESRenderEngine() {
+ // Destroy the image manager first.
+ mImageManager = nullptr;
std::lock_guard<std::mutex> lock(mRenderingMutex);
unbindFrameBuffer(mDrawingBuffer.get());
mDrawingBuffer = nullptr;
@@ -433,6 +437,7 @@
EGLImageKHR expired = mFramebufferImageCache.front().second;
mFramebufferImageCache.pop_front();
eglDestroyImageKHR(mEGLDisplay, expired);
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
}
mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -614,64 +619,51 @@
}
}
-status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- return cacheExternalTextureBufferLocked(buffer);
-}
-
status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
const sp<GraphicBuffer>& buffer,
const sp<Fence>& bufferFence) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
-}
-
-status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) {
if (buffer == nullptr) {
return BAD_VALUE;
}
ATRACE_CALL();
- if (mImageCache.count(buffer->getId()) > 0) {
- return NO_ERROR;
+ bool found = false;
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto cachedImage = mImageCache.find(buffer->getId());
+ found = (cachedImage != mImageCache.end());
}
- std::unique_ptr<Image> newImage = createImage();
-
- bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
- buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
- if (!created) {
- ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
- buffer->getPixelFormat());
- return NO_INIT;
- }
- mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
-
- return NO_ERROR;
-}
-
-status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
- const sp<GraphicBuffer>& buffer,
- const sp<Fence>& bufferFence) {
- ATRACE_CALL();
- status_t cacheResult = cacheExternalTextureBufferLocked(buffer);
-
- if (cacheResult != NO_ERROR) {
- return cacheResult;
+ // If we couldn't find the image in the cache at this time, then either
+ // SurfaceFlinger messed up registering the buffer ahead of time or we got
+ // backed up creating other EGLImages.
+ if (!found) {
+ status_t cacheResult = mImageManager->cache(buffer);
+ if (cacheResult != NO_ERROR) {
+ return cacheResult;
+ }
}
- auto cachedImage = mImageCache.find(buffer->getId());
+ // Whether or not we needed to cache, re-check mImageCache to make sure that
+ // there's an EGLImage. The current threading model guarantees that we don't
+ // destroy a cached image until it's really not needed anymore (i.e. this
+ // function should not be called), so the only possibility is that something
+ // terrible went wrong and we should just bind something and move on.
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto cachedImage = mImageCache.find(buffer->getId());
- if (cachedImage == mImageCache.end()) {
- // We failed creating the image if we got here, so bail out.
- bindExternalTextureImage(texName, *createImage());
- return NO_INIT;
+ if (cachedImage == mImageCache.end()) {
+ // We failed creating the image if we got here, so bail out.
+ ALOGE("Failed to create an EGLImage when rendering");
+ bindExternalTextureImage(texName, *createImage());
+ return NO_INIT;
+ }
+
+ bindExternalTextureImage(texName, *cachedImage->second);
}
- bindExternalTextureImage(texName, *cachedImage->second);
-
// Wait for the new buffer to be ready.
if (bufferFence != nullptr && bufferFence->isValid()) {
if (GLExtensions::getInstance().hasWaitSync()) {
@@ -696,13 +688,81 @@
return NO_ERROR;
}
+void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ mImageManager->cacheAsync(buffer, nullptr);
+}
+
+std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting(
+ const sp<GraphicBuffer>& buffer) {
+ auto barrier = std::make_shared<ImageManager::Barrier>();
+ mImageManager->cacheAsync(buffer, barrier);
+ return barrier;
+}
+
+status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mImageCache.count(buffer->getId()) > 0) {
+ // If there's already an image then fail fast here.
+ return NO_ERROR;
+ }
+ }
+ ATRACE_CALL();
+
+ // Create the image without holding a lock so that we don't block anything.
+ std::unique_ptr<Image> newImage = createImage();
+
+ bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
+ buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ if (!created) {
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ return NO_INIT;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mImageCache.count(buffer->getId()) > 0) {
+ // In theory it's possible for another thread to recache the image,
+ // so bail out if another thread won.
+ return NO_ERROR;
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+ }
+
+ return NO_ERROR;
+}
+
void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- const auto& cachedImage = mImageCache.find(bufferId);
- if (cachedImage != mImageCache.end()) {
- ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
- mImageCache.erase(bufferId);
- return;
+ mImageManager->releaseAsync(bufferId, nullptr);
+}
+
+std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
+ uint64_t bufferId) {
+ auto barrier = std::make_shared<ImageManager::Barrier>();
+ mImageManager->releaseAsync(bufferId, barrier);
+ return barrier;
+}
+
+void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) {
+ std::unique_ptr<Image> image;
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ const auto& cachedImage = mImageCache.find(bufferId);
+
+ if (cachedImage != mImageCache.end()) {
+ ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
+ // Move the buffer out of cache first, so that we can destroy
+ // without holding the cache's lock.
+ image = std::move(cachedImage->second);
+ mImageCache.erase(bufferId);
+ return;
+ }
}
ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
}
@@ -842,6 +902,7 @@
bool useFramebufferCache) {
sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
if (useFramebufferCache) {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
for (const auto& image : mFramebufferImageCache) {
if (image.first == graphicBuffer->getId()) {
return image.second;
@@ -857,14 +918,20 @@
nativeBuffer, attributes);
if (useFramebufferCache) {
if (image != EGL_NO_IMAGE_KHR) {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
EGLImageKHR expired = mFramebufferImageCache.front().second;
mFramebufferImageCache.pop_front();
eglDestroyImageKHR(mEGLDisplay, expired);
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
}
mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
}
}
+
+ if (image != EGL_NO_IMAGE_KHR) {
+ DEBUG_EGL_IMAGE_TRACKER_CREATE();
+ }
return image;
}
@@ -889,127 +956,123 @@
return BAD_VALUE;
}
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
+ BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
- BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
-
- if (fbo.getStatus() != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->handle);
- checkErrors();
- return fbo.getStatus();
- }
-
- // clear the entire buffer, sometimes when we reuse buffers we'd persist
- // ghost images otherwise.
- // we also require a full transparent framebuffer for overlays. This is
- // probably not quite efficient on all GPUs, since we could filter out
- // opaque layers.
- clearWithColor(0.0, 0.0, 0.0, 0.0);
-
- setViewportAndProjection(display.physicalDisplay, display.clip);
-
- setOutputDataSpace(display.outputDataspace);
- setDisplayMaxLuminance(display.maxLuminance);
-
- mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
- mState.projectionMatrix = projectionMatrix;
- if (!display.clearRegion.isEmpty()) {
- glDisable(GL_BLEND);
- fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
- }
-
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
- for (auto layer : layers) {
- mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
- const FloatRect bounds = layer.geometry.boundaries;
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = vec2(bounds.left, bounds.top);
- position[1] = vec2(bounds.left, bounds.bottom);
- position[2] = vec2(bounds.right, bounds.bottom);
- position[3] = vec2(bounds.right, bounds.top);
-
- setupLayerCropping(layer, mesh);
- setColorTransform(display.colorTransform * layer.colorTransform);
-
- bool usePremultipliedAlpha = true;
- bool disableTexture = true;
- bool isOpaque = false;
-
- if (layer.source.buffer.buffer != nullptr) {
- disableTexture = false;
- isOpaque = layer.source.buffer.isOpaque;
-
- sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
- bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
-
- usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- mat4 texMatrix = layer.source.buffer.textureTransform;
-
- texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
- texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer.source.buffer.isY410BT2020);
-
- renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(0.0, 0.0);
- texCoords[1] = vec2(0.0, 1.0);
- texCoords[2] = vec2(1.0, 1.0);
- texCoords[3] = vec2(1.0, 0.0);
- setupLayerTexturing(texture);
- }
-
- const half3 solidColor = layer.source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
- // Buffer sources will have a black solid color ignored in the shader,
- // so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
- if (layer.disableBlending) {
- glDisable(GL_BLEND);
- }
- setSourceDataSpace(layer.sourceDataspace);
-
- // We only want to do a special handling for rounded corners when having rounded corners
- // is the only reason it needs to turn on blending, otherwise, we handle it like the
- // usual way since it needs to turn on blending anyway.
- if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, layer, mesh);
- } else {
- drawMesh(mesh);
- }
-
- // Cleanup if there's a buffer source
- if (layer.source.buffer.buffer != nullptr) {
- disableBlending();
- setSourceY410BT2020(false);
- disableTexturing();
- }
- }
-
- if (drawFence != nullptr) {
- *drawFence = flush();
- }
- // If flush failed or we don't support native fences, we need to force the
- // gl command stream to be executed.
- if (drawFence == nullptr || drawFence->get() < 0) {
- bool success = finish();
- if (!success) {
- ALOGE("Failed to flush RenderEngine commands");
- checkErrors();
- // Chances are, something illegal happened (either the caller passed
- // us bad parameters, or we messed up our shader generation).
- return INVALID_OPERATION;
- }
- }
-
+ if (fbo.getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
checkErrors();
+ return fbo.getStatus();
}
+
+ // clear the entire buffer, sometimes when we reuse buffers we'd persist
+ // ghost images otherwise.
+ // we also require a full transparent framebuffer for overlays. This is
+ // probably not quite efficient on all GPUs, since we could filter out
+ // opaque layers.
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+
+ setOutputDataSpace(display.outputDataspace);
+ setDisplayMaxLuminance(display.maxLuminance);
+
+ mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+ mState.projectionMatrix = projectionMatrix;
+ if (!display.clearRegion.isEmpty()) {
+ glDisable(GL_BLEND);
+ fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+ }
+
+ Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+ for (auto layer : layers) {
+ mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+ const FloatRect bounds = layer.geometry.boundaries;
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ position[0] = vec2(bounds.left, bounds.top);
+ position[1] = vec2(bounds.left, bounds.bottom);
+ position[2] = vec2(bounds.right, bounds.bottom);
+ position[3] = vec2(bounds.right, bounds.top);
+
+ setupLayerCropping(layer, mesh);
+ setColorTransform(display.colorTransform * layer.colorTransform);
+
+ bool usePremultipliedAlpha = true;
+ bool disableTexture = true;
+ bool isOpaque = false;
+
+ if (layer.source.buffer.buffer != nullptr) {
+ disableTexture = false;
+ isOpaque = layer.source.buffer.isOpaque;
+
+ sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+ bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
+ layer.source.buffer.fence);
+
+ usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+ mat4 texMatrix = layer.source.buffer.textureTransform;
+
+ texture.setMatrix(texMatrix.asArray());
+ texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+ texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+ setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+ renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+ texCoords[0] = vec2(0.0, 0.0);
+ texCoords[1] = vec2(0.0, 1.0);
+ texCoords[2] = vec2(1.0, 1.0);
+ texCoords[3] = vec2(1.0, 0.0);
+ setupLayerTexturing(texture);
+ }
+
+ const half3 solidColor = layer.source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ // Buffer sources will have a black solid color ignored in the shader,
+ // so in that scenario the solid color passed here is arbitrary.
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
+ glDisable(GL_BLEND);
+ }
+ setSourceDataSpace(layer.sourceDataspace);
+
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
+ } else {
+ drawMesh(mesh);
+ }
+
+ // Cleanup if there's a buffer source
+ if (layer.source.buffer.buffer != nullptr) {
+ disableBlending();
+ setSourceY410BT2020(false);
+ disableTexturing();
+ }
+ }
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ if (drawFence == nullptr || drawFence->get() < 0) {
+ bool success = finish();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ checkErrors();
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+ }
+
+ checkErrors();
return NO_ERROR;
}
@@ -1306,6 +1369,23 @@
StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, unused] : mImageCache) {
+ StringAppendF(&result, "0x%" PRIx64 "\n", id);
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n",
+ mFramebufferImageCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, unused] : mFramebufferImageCache) {
+ StringAppendF(&result, "0x%" PRIx64 "\n", id);
+ }
+ }
}
GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
@@ -1432,7 +1512,7 @@
}
bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
[=](std::pair<uint64_t, EGLImageKHR> image) {
return image.first == bufferId;
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index de793c2..dd60e50 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -17,9 +17,7 @@
#ifndef SF_GLESRENDERENGINE_H_
#define SF_GLESRENDERENGINE_H_
-#include <android-base/thread_annotations.h>
#include <stdint.h>
-#include <sys/types.h>
#include <condition_variable>
#include <deque>
#include <mutex>
@@ -30,8 +28,11 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
+#include <android-base/thread_annotations.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/private/Description.h>
+#include <sys/types.h>
+#include "ImageManager.h"
#define EGL_NO_CONFIG ((EGLConfig)0)
@@ -74,7 +75,7 @@
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
- status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+ void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
status_t bindFrameBuffer(Framebuffer* framebuffer) override;
void unbindFrameBuffer(Framebuffer* framebuffer) override;
@@ -85,25 +86,32 @@
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
ANativeWindowBuffer* buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence)
- EXCLUDES(mRenderingMutex) override;
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
// internal to RenderEngine
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
EGLConfig getEGLConfig() const { return mEGLConfig; }
// Creates an output image for rendering to
EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- bool useFramebufferCache);
+ bool useFramebufferCache)
+ EXCLUDES(mFramebufferImageCacheMutex);
// Test-only methods
// Returns true iff mImageCache contains an image keyed by bufferId
bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
// Returns true iff mFramebufferImageCache contains an image keyed by bufferId
- bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ bool isFramebufferImageCachedForTesting(uint64_t bufferId)
+ EXCLUDES(mFramebufferImageCacheMutex);
+ // These are wrappers around public methods above, but exposing Barrier
+ // objects so that tests can block.
+ std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting(
+ const sp<GraphicBuffer>& buffer);
+ std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
protected:
Framebuffer* getFramebufferForDrawing() override;
- void dump(std::string& result) override;
+ void dump(std::string& result) override EXCLUDES(mRenderingMutex)
+ EXCLUDES(mFramebufferImageCacheMutex);
void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
ui::Transform::orientation_flags rotation) override;
void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
@@ -145,6 +153,9 @@
void setScissor(const Rect& region);
void disableScissor();
bool waitSync(EGLSyncKHR sync, EGLint flags);
+ status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
+ EXCLUDES(mRenderingMutex);
+ void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
// A data space is considered HDR data space if it has BT2020 color space
// with PQ or HLG transfer function.
@@ -200,7 +211,11 @@
uint32_t mFramebufferImageCacheSize = 0;
// Cache of output images, keyed by corresponding GraphicBuffer ID.
- std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache;
+ std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache
+ GUARDED_BY(mFramebufferImageCacheMutex);
+ // The only reason why we have this mutex is so that we don't segfault when
+ // dumping info.
+ std::mutex mFramebufferImageCacheMutex;
// Current dataspace of layer being rendered
ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
@@ -220,15 +235,6 @@
// multiple threads is guaranteed thread-safe.
std::mutex mRenderingMutex;
- // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
- // is held.
- status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) REQUIRES(mRenderingMutex);
- // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex
- // is held.
- status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer)
- REQUIRES(mRenderingMutex);
-
std::unique_ptr<Framebuffer> mDrawingBuffer;
class FlushTracer {
@@ -253,7 +259,9 @@
bool mRunning = true;
};
friend class FlushTracer;
+ friend class ImageManager;
std::unique_ptr<FlushTracer> mFlushTracer;
+ std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
};
} // namespace gl
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index dacf8d3..5fbb5ba 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -22,6 +22,7 @@
#include <GLES/glext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <gui/DebugEGLImageTracker.h>
#include <nativebase/nativebase.h>
#include <utils/Trace.h>
#include "GLESRenderEngine.h"
@@ -47,6 +48,7 @@
if (mEGLImage != EGL_NO_IMAGE_KHR) {
if (!usingFramebufferCache) {
eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
}
mEGLImage = EGL_NO_IMAGE_KHR;
mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
index 77e648e..8497721 100644
--- a/libs/renderengine/gl/GLImage.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -20,6 +20,7 @@
#include <vector>
+#include <gui/DebugEGLImageTracker.h>
#include <log/log.h>
#include <utils/Trace.h>
#include "GLESRenderEngine.h"
@@ -58,6 +59,7 @@
if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
ALOGE("failed to destroy image: %#x", eglGetError());
}
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
mEGLImage = EGL_NO_IMAGE_KHR;
}
@@ -69,6 +71,7 @@
ALOGE("failed to create EGLImage: %#x", eglGetError());
return false;
}
+ DEBUG_EGL_IMAGE_TRACKER_CREATE();
mProtected = isProtected;
}
diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp
new file mode 100644
index 0000000..5af0e4f
--- /dev/null
+++ b/libs/renderengine/gl/ImageManager.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <pthread.h>
+
+#include <processgroup/sched_policy.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+#include "ImageManager.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {
+ pthread_setname_np(mThread.native_handle(), "ImageManager");
+ // Use SCHED_FIFO to minimize jitter
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO for ImageManager");
+ }
+}
+
+ImageManager::~ImageManager() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRunning = false;
+ }
+ mCondition.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer,
+ const std::shared_ptr<Barrier>& barrier) {
+ if (buffer == nullptr) {
+ {
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ barrier->isOpen = true;
+ barrier->result = BAD_VALUE;
+ }
+ barrier->condition.notify_one();
+ return;
+ }
+ ATRACE_CALL();
+ QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier};
+ queueOperation(std::move(entry));
+}
+
+status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+ auto barrier = std::make_shared<Barrier>();
+ cacheAsync(buffer, barrier);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ barrier->condition.wait(barrier->mutex,
+ [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; });
+ return barrier->result;
+}
+
+void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) {
+ ATRACE_CALL();
+ QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier};
+ queueOperation(std::move(entry));
+}
+
+void ImageManager::queueOperation(const QueueEntry&& entry) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.emplace(entry);
+ ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
+ }
+ mCondition.notify_one();
+}
+
+void ImageManager::threadMain() {
+ set_sched_policy(0, SP_FOREGROUND);
+ bool run;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ run = mRunning;
+ }
+ while (run) {
+ QueueEntry entry;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCondition.wait(mMutex,
+ [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
+ run = mRunning;
+
+ if (!mRunning) {
+ // if mRunning is false, then ImageManager is being destroyed, so
+ // bail out now.
+ break;
+ }
+
+ entry = mQueue.front();
+ mQueue.pop();
+ ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
+ }
+
+ status_t result = NO_ERROR;
+ switch (entry.op) {
+ case QueueEntry::Operation::Delete:
+ mEngine->unbindExternalTextureBufferInternal(entry.bufferId);
+ break;
+ case QueueEntry::Operation::Insert:
+ result = mEngine->cacheExternalTextureBufferInternal(entry.buffer);
+ break;
+ }
+ if (entry.barrier != nullptr) {
+ {
+ std::lock_guard<std::mutex> entryLock(entry.barrier->mutex);
+ entry.barrier->result = result;
+ entry.barrier->isOpen = true;
+ }
+ entry.barrier->condition.notify_one();
+ }
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h
new file mode 100644
index 0000000..b5ba554
--- /dev/null
+++ b/libs/renderengine/gl/ImageManager.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class ImageManager {
+public:
+ struct Barrier {
+ std::mutex mutex;
+ std::condition_variable_any condition;
+ bool isOpen GUARDED_BY(mutex) = false;
+ status_t result GUARDED_BY(mutex) = NO_ERROR;
+ };
+ ImageManager(GLESRenderEngine* engine);
+ ~ImageManager();
+ void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier)
+ EXCLUDES(mMutex);
+ status_t cache(const sp<GraphicBuffer>& buffer);
+ void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex);
+
+private:
+ struct QueueEntry {
+ enum class Operation { Delete, Insert };
+
+ Operation op = Operation::Delete;
+ sp<GraphicBuffer> buffer = nullptr;
+ uint64_t bufferId = 0;
+ std::shared_ptr<Barrier> barrier = nullptr;
+ };
+
+ void queueOperation(const QueueEntry&& entry);
+ void threadMain();
+ GLESRenderEngine* const mEngine;
+ std::thread mThread = std::thread([this]() { threadMain(); });
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+ std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
+
+ bool mRunning GUARDED_BY(mMutex) = true;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e707004..c6a7bd8 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -116,10 +116,20 @@
const sp<Fence>& fence) = 0;
// Caches Image resources for this buffer, but does not bind the buffer to
// a particular texture.
- virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+ // Note that work is deferred to an additional thread, i.e. this call
+ // is made asynchronously, but the caller can expect that cache/unbind calls
+ // are performed in a manner that's conflict serializable, i.e. unbinding
+ // a buffer should never occur before binding the buffer if the caller
+ // called {bind, cache}ExternalTextureBuffer before calling unbind.
+ virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
// Removes internal resources referenced by the bufferId. This method should be
// invoked when the caller will no longer hold a reference to a GraphicBuffer
// and needs to clean up its resources.
+ // Note that work is deferred to an additional thread, i.e. this call
+ // is made asynchronously, but the caller can expect that cache/unbind calls
+ // are performed in a manner that's conflict serializable, i.e. unbinding
+ // a buffer should never occur before binding the buffer if the caller
+ // called {bind, cache}ExternalTextureBuffer before calling unbind.
virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
// When binding a native buffer, it must be done before setViewportAndProjection
// Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
@@ -176,6 +186,17 @@
// should be called for every display that needs to be rendered via the GPU.
// @param display The display-wide settings that should be applied prior to
// drawing any layers.
+ //
+ // Assumptions when calling this method:
+ // 1. There is exactly one caller - i.e. multi-threading is not supported.
+ // 2. Additional threads may be calling the {bind,cache}ExternalTexture
+ // methods above. But the main thread is responsible for holding resources
+ // such that Image destruction does not occur while this method is called.
+ //
+ // TODO(b/136806342): This should behavior should ideally be fixed since
+ // the above two assumptions are brittle, as conditional thread safetyness
+ // may be insufficient when maximizing rendering performance in the future.
+ //
// @param layers The layers to draw onto the display, in Z-order.
// @param buffer The buffer which will be drawn to. This buffer will be
// ready once drawFence fires.
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index e33bcfd..b4d3ef2 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -51,7 +51,7 @@
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
- MOCK_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&));
+ MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
MOCK_METHOD3(bindExternalTextureBuffer,
status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 9b483ef..e98babc 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -31,6 +31,7 @@
"libgui",
"liblog",
"libnativewindow",
+ "libprocessgroup",
"libsync",
"libui",
"libutils",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7acaecf..f47c7fd 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include <chrono>
+#include <condition_variable>
+#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
@@ -1001,8 +1003,15 @@
invokeDraw(settings, layers, mBuffer);
uint64_t bufferId = layer.source.buffer.buffer->getId();
EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- sRE->unbindExternalTextureBuffer(bufferId);
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->unbindExternalTextureBufferForTesting(bufferId);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(NO_ERROR, barrier->result);
}
TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
@@ -1019,21 +1028,52 @@
sRE->bindExternalTextureBuffer(texName, buf, nullptr);
uint64_t bufferId = buf->getId();
EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- sRE->unbindExternalTextureBuffer(bufferId);
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->unbindExternalTextureBufferForTesting(bufferId);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_EQ(NO_ERROR, barrier->result);
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) {
- status_t result = sRE->cacheExternalTextureBuffer(nullptr);
- ASSERT_EQ(BAD_VALUE, result);
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->cacheExternalTextureBufferForTesting(nullptr);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_TRUE(barrier->isOpen);
+ EXPECT_EQ(BAD_VALUE, barrier->result);
}
TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) {
sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
uint64_t bufferId = buf->getId();
- sRE->cacheExternalTextureBuffer(buf);
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->cacheExternalTextureBufferForTesting(buf);
+ {
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_EQ(NO_ERROR, barrier->result);
+ }
EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- sRE->unbindExternalTextureBuffer(bufferId);
+ barrier = sRE->unbindExternalTextureBufferForTesting(bufferId);
+ {
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_EQ(NO_ERROR, barrier->result);
+ }
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 0861a1f..d588346 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -20,6 +20,7 @@
#include <ui/GraphicBufferAllocator.h>
+#include <limits.h>
#include <stdio.h>
#include <grallocusage/GrallocUsageConversion.h>
@@ -114,6 +115,14 @@
if (!width || !height)
width = height = 1;
+ const uint32_t bpp = bytesPerPixel(format);
+ if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": Requesting too large a buffer size",
+ width, height, layerCount, format, usage);
+ return BAD_VALUE;
+ }
+
// Ensure that layerCount is valid.
if (layerCount < 1)
layerCount = 1;
@@ -123,10 +132,20 @@
status_t error =
mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+ size_t bufSize;
+
+ // if stride has no meaning or is too large,
+ // approximate size with the input width instead
+ if ((*stride) != 0 &&
+ std::numeric_limits<size_t>::max() / height / (*stride) < static_cast<size_t>(bpp)) {
+ bufSize = static_cast<size_t>(width) * height * bpp;
+ } else {
+ bufSize = static_cast<size_t>((*stride)) * height * bpp;
+ }
+
if (error == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
- uint32_t bpp = bytesPerPixel(format);
alloc_rec_t rec;
rec.width = width;
rec.height = height;
@@ -134,7 +153,7 @@
rec.format = format;
rec.layerCount = layerCount;
rec.usage = usage;
- rec.size = static_cast<size_t>(height * (*stride) * bpp);
+ rec.size = bufSize;
rec.requestorName = std::move(requestorName);
list.add(*handle, rec);
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 25d4512..324d9e1 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -54,7 +54,7 @@
void dump(std::string& res) const;
static void dumpToSystemLog();
-private:
+protected:
struct alloc_rec_t {
uint32_t width;
uint32_t height;
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 373fa4f..15f4b64 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,26 @@
}
cc_test {
+ name: "GraphicBufferAllocator_test",
+ header_libs: [
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+ shared_libs: [
+ "liblog",
+ "libui",
+ ],
+ srcs: [
+ "GraphicBufferAllocator_test.cpp",
+ "mock/MockGrallocAllocator.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "GraphicBuffer_test",
header_libs: [
"libdvr_headers",
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
new file mode 100644
index 0000000..efca083
--- /dev/null
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "GraphicBufferAllocatorTest"
+
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/PixelFormat.h>
+
+#include <gtest/gtest.h>
+
+#include "mock/MockGrallocAllocator.h"
+
+#include <algorithm>
+#include <limits>
+
+namespace android {
+
+namespace {
+
+constexpr uint32_t kTestWidth = 1024;
+constexpr uint32_t kTestHeight = 1;
+constexpr uint32_t kTestLayerCount = 1;
+constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+
+} // namespace
+
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+class TestableGraphicBufferAllocator : public GraphicBufferAllocator {
+public:
+ TestableGraphicBufferAllocator() {
+ mAllocator = std::make_unique<const mock::MockGrallocAllocator>();
+ }
+ void setUpAllocateExpectations(status_t err, uint32_t stride) {
+ std::cout << "Setting expected stride to " << stride << std::endl;
+ EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
+ allocate)
+ .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
+ }
+ std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
+};
+
+class GraphicBufferAllocatorTest : public testing::Test {
+public:
+ GraphicBufferAllocatorTest() : mAllocator() {}
+ const TestableGraphicBufferAllocator& getAllocator() { return mAllocator; }
+
+protected:
+ TestableGraphicBufferAllocator mAllocator;
+};
+
+TEST_F(GraphicBufferAllocatorTest, AllocateNoError) {
+ mAllocator.setUpAllocateExpectations(NO_ERROR, kTestWidth);
+ android::PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ uint32_t stride = 0;
+ buffer_handle_t handle;
+ status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage,
+ &handle, &stride, 0, "GraphicBufferAllocatorTest");
+ ASSERT_EQ(NO_ERROR, err);
+ ASSERT_EQ(kTestWidth, stride);
+}
+
+TEST_F(GraphicBufferAllocatorTest, AllocateZeroStride) {
+ android::PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ uint32_t expectedStride = 0;
+
+ mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride);
+ uint32_t stride = 0;
+ buffer_handle_t handle;
+ // a divide by zero would cause a crash
+ status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage,
+ &handle, &stride, 0, "GraphicBufferAllocatorTest");
+ ASSERT_EQ(NO_ERROR, err);
+ ASSERT_EQ(expectedStride, stride);
+}
+
+TEST_F(GraphicBufferAllocatorTest, AllocateLargeStride) {
+ uint32_t height = std::numeric_limits<uint32_t>::max();
+ uint32_t bpp = 4;
+ android::PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+
+ if (std::numeric_limits<size_t>::max() / height / bpp >= std::numeric_limits<uint32_t>::max()) {
+ std::cout << "stride cannot cause overflow" << std::endl;
+ GTEST_SUCCEED() << "stride cannot cause overflow";
+ return;
+ }
+ uint32_t width = std::numeric_limits<size_t>::max() / height / bpp;
+
+ uint32_t expectedStride = std::numeric_limits<uint32_t>::max();
+
+ mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride);
+ uint32_t stride = 0;
+ buffer_handle_t handle;
+ // an overflow would cause a crash
+ status_t err = mAllocator.allocate(width, height, format, kTestLayerCount, kTestUsage, &handle,
+ &stride, 0, "GraphicBufferAllocatorTest");
+ ASSERT_EQ(NO_ERROR, err);
+ ASSERT_EQ(expectedStride, stride);
+}
+} // namespace android
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index a7c248c..127f7ee 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -35,6 +35,22 @@
class GraphicBufferTest : public testing::Test {};
+TEST_F(GraphicBufferTest, AllocateNoError) {
+ PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ sp<GraphicBuffer> gb(new GraphicBuffer(kTestWidth, kTestHeight, format, kTestLayerCount,
+ kTestUsage, std::string("test")));
+ ASSERT_EQ(NO_ERROR, gb->initCheck());
+}
+
+TEST_F(GraphicBufferTest, AllocateBadDimensions) {
+ PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ uint32_t width, height;
+ width = height = std::numeric_limits<uint32_t>::max();
+ sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage,
+ std::string("test")));
+ ASSERT_EQ(BAD_VALUE, gb->initCheck());
+}
+
TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
std::unique_ptr<BufferHubBuffer> b1 =
BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.cpp b/libs/ui/tests/mock/MockGrallocAllocator.cpp
new file mode 100644
index 0000000..d71e25f
--- /dev/null
+++ b/libs/ui/tests/mock/MockGrallocAllocator.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 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 "MockGrallocAllocator.h"
+
+namespace android {
+
+namespace mock {
+
+MockGrallocAllocator::MockGrallocAllocator() = default;
+MockGrallocAllocator::~MockGrallocAllocator() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
new file mode 100644
index 0000000..22c80a4
--- /dev/null
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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 <ui/Gralloc.h>
+
+namespace android {
+
+class GraphicBuffer;
+
+namespace mock {
+
+class MockGrallocAllocator : public GrallocAllocator {
+public:
+ MockGrallocAllocator();
+ ~MockGrallocAllocator() override;
+
+ MOCK_METHOD(bool, isLoaded, (), (const, override));
+ MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override));
+ MOCK_METHOD(status_t, allocate,
+ (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles),
+ (const, override));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ac01dc8..ba7cacd 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -337,7 +337,7 @@
// Only enable the system search path for non-user builds
std::string system_path;
- if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ if (android::GraphicsEnv::getInstance().isDebuggable()) {
system_path = kSystemLayerLibraryDir;
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index e996be6..a3bb6de 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -61,7 +61,7 @@
using nsecs_t = int64_t;
-struct extention_map_t {
+struct extension_map_t {
const char* name;
__eglMustCastToProperFunctionPointerType address;
};
@@ -154,7 +154,7 @@
* (keep in sync with gExtensionString above)
*
*/
-static const extention_map_t sExtensionMap[] = {
+static const extension_map_t sExtensionMap[] = {
// EGL_KHR_lock_surface
{ "eglLockSurfaceKHR",
(__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
@@ -257,13 +257,14 @@
!strcmp((procname), "eglAwakenProcessIMG"))
// accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
+static std::unordered_map<std::string, int> sGLExtensionSlotMap;
-static int sGLExtentionSlot = 0;
+static int sGLExtensionSlot = 0;
static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
static void(*findProcAddress(const char* name,
- const extention_map_t* map, size_t n))() {
+ const extension_map_t* map, size_t n))() {
for (uint32_t i=0 ; i<n ; i++) {
if (!strcmp(name, map[i].name)) {
return map[i].address;
@@ -1223,7 +1224,7 @@
addr = findBuiltinWrapper(procname);
if (addr) return addr;
- // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+ // this protects accesses to sGLExtensionMap, sGLExtensionSlot, and sGLExtensionSlotMap
pthread_mutex_lock(&sExtensionMapMutex);
/*
@@ -1244,51 +1245,69 @@
*/
const std::string name(procname);
-
- auto& extentionMap = sGLExtentionMap;
- auto pos = extentionMap.find(name);
- addr = (pos != extentionMap.end()) ? pos->second : nullptr;
- const int slot = sGLExtentionSlot;
-
- ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
- "no more slots for eglGetProcAddress(\"%s\")",
- procname);
-
+ auto& extensionMap = sGLExtensionMap;
+ auto& extensionSlotMap = sGLExtensionSlotMap;
egl_connection_t* const cnx = &gEGLImpl;
LayerLoader& layer_loader(LayerLoader::getInstance());
- if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+ // See if we've already looked up this extension
+ auto pos = extensionMap.find(name);
+ addr = (pos != extensionMap.end()) ? pos->second : nullptr;
- if (cnx->dso && cnx->egl.eglGetProcAddress) {
+ if (!addr) {
+ // This is the first time we've looked this function up
+ // Ensure we have room to track it
+ const int slot = sGLExtensionSlot;
+ if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
- // Extensions are independent of the bound context
- addr = cnx->egl.eglGetProcAddress(procname);
- if (addr) {
+ if (cnx->dso && cnx->egl.eglGetProcAddress) {
- // purposefully track the bottom of the stack in extensionMap
- extentionMap[name] = addr;
+ // Extensions are independent of the bound context
+ addr = cnx->egl.eglGetProcAddress(procname);
+ if (addr) {
- // Apply layers
- addr = layer_loader.ApplyLayers(procname, addr);
+ // purposefully track the bottom of the stack in extensionMap
+ extensionMap[name] = addr;
- // Track the top most entry point
- cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
- addr = gExtensionForwarders[slot];
- sGLExtentionSlot++;
+ // Apply layers
+ addr = layer_loader.ApplyLayers(procname, addr);
+
+ // Track the top most entry point return the extension forwarder
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ addr = gExtensionForwarders[slot];
+
+ // Remember the slot for this extension
+ extensionSlotMap[name] = slot;
+
+ // Increment the global extension index
+ sGLExtensionSlot++;
+ }
}
+ } else {
+ // The extension forwarder has a fixed number of slots
+ ALOGE("no more slots for eglGetProcAddress(\"%s\")", procname);
}
- } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+ } else {
+ // We tracked an address, so we've seen this func before
+ // Look up the slot for this extension
+ auto slot_pos = extensionSlotMap.find(name);
+ int ext_slot = (slot_pos != extensionSlotMap.end()) ? slot_pos->second : -1;
+ if (ext_slot < 0) {
+ // Something has gone wrong, this should not happen
+ ALOGE("No extension slot found for %s", procname);
+ return nullptr;
+ }
- // We've seen this func before, but we tracked the bottom, so re-apply layers
- // More layers might have been enabled
+ // We tracked the bottom of the stack, so re-apply layers since
+ // more layers might have been enabled
addr = layer_loader.ApplyLayers(procname, addr);
- // Track the top most entry point
- cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
- addr = gExtensionForwarders[slot];
+ // Track the top most entry point and return the extension forwarder
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+ addr = gExtensionForwarders[ext_slot];
}
pthread_mutex_unlock(&sExtensionMapMutex);
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 8dd4d1d..576c8d8 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -47,7 +47,6 @@
"liblog",
"libutils",
"libui",
- "server_configurable_flags",
],
cflags: [
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ef1a224..cda0e0c 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -27,7 +27,6 @@
#if defined(__linux__)
#include <pthread.h>
#endif
-#include <server_configurable_flags/get_flags.h>
#include <unordered_set>
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
@@ -46,13 +45,6 @@
namespace android {
-static constexpr bool DEBUG = false;
-
-// Category (=namespace) name for the input settings that are applied at boot time
-static const char* INPUT_NATIVE_BOOT = "input_native_boot";
-// Feature flag name for the deep press feature
-static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
-
//Max number of elements to store in mEvents.
static constexpr size_t MAX_EVENTS = 5;
@@ -79,20 +71,6 @@
return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
}
-// Check if the "deep touch" feature is on.
-static bool deepPressEnabled() {
- std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
- std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
- if (flag_value == "1" || flag_value == "true") {
- ALOGI("Deep press feature enabled.");
- return true;
- }
- ALOGI("Deep press feature is not enabled.");
- return false;
-}
-
-
// --- ClassifierEvent ---
ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -141,53 +119,40 @@
// --- MotionClassifier ---
-MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
- mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
- mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
-#if defined(__linux__)
- // Set the thread name for debugging
- pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
-#endif
-}
-
-/**
- * This function may block for some time to initialize the HAL, so it should only be called
- * from the "InputClassifier HAL" thread.
- */
-bool MotionClassifier::init() {
- ensureHalThread(__func__);
- sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
- classifier::V1_0::IInputClassifier::getService();
- if (!service) {
- // Not really an error, maybe the device does not have this HAL,
- // but somehow the feature flag is flipped
- ALOGI("Could not obtain InputClassifier HAL");
- return false;
- }
-
- sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
- if (recipient != nullptr) {
- const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
- if (!linked) {
- ALOGE("Could not link MotionClassifier to the HAL death");
- return false;
- }
- }
-
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
+ : mEvents(MAX_EVENTS), mService(service) {
// Under normal operation, we do not need to reset the HAL here. But in the case where system
// crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
// have received events in the past. That means, that HAL could be in an inconsistent state
// once it receives events from the newly created MotionClassifier.
mEvents.push(ClassifierEvent::createHalResetEvent());
- {
- std::scoped_lock lock(mLock);
- if (mService) {
- ALOGE("MotionClassifier::%s should only be called once", __func__);
- }
- mService = service;
+ mHalThread = std::thread(&MotionClassifier::processEvents, this);
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient) {
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (!service) {
+ // Not really an error, maybe the device does not have this HAL,
+ // but somehow the feature flag is flipped
+ ALOGI("Could not obtain InputClassifier HAL");
+ return nullptr;
}
- return true;
+
+ const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link death recipient to the HAL death");
+ return nullptr;
+ }
+ // Using 'new' to access a non-public constructor
+ return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
}
MotionClassifier::~MotionClassifier() {
@@ -195,14 +160,6 @@
mHalThread.join();
}
-void MotionClassifier::ensureHalThread(const char* function) {
- if (DEBUG) {
- if (std::this_thread::get_id() != mHalThread.get_id()) {
- LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
- }
- }
-}
-
/**
* Obtain the classification from the HAL for a given MotionEvent.
* Should only be called from the InputClassifier thread (mHalThread).
@@ -213,23 +170,7 @@
* To remove any possibility of negatively affecting the touch latency, the HAL
* is called from a dedicated thread.
*/
-void MotionClassifier::callInputClassifierHal() {
- ensureHalThread(__func__);
- const bool initialized = init();
- if (!initialized) {
- // MotionClassifier no longer useful.
- // Deliver death notification from a separate thread
- // because ~MotionClassifier may be invoked, which calls mHalThread.join()
- std::thread([deathRecipient = mDeathRecipient](){
- sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
- if (recipient != nullptr) {
- recipient->serviceDied(0 /*cookie*/, nullptr);
- }
- }).detach();
- return;
- }
- // From this point on, mService is guaranteed to be non-null.
-
+void MotionClassifier::processEvents() {
while (true) {
ClassifierEvent event = mEvents.pop();
bool halResponseOk = true;
@@ -383,24 +324,41 @@
}
}
+// --- HalDeathRecipient
+
+InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
+
+void InputClassifier::HalDeathRecipient::serviceDied(
+ uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
+ sp<android::hidl::base::V1_0::IBase> service = who.promote();
+ if (service) {
+ service->unlinkToDeath(this);
+ }
+ mParent.setMotionClassifier(nullptr);
+}
// --- InputClassifier ---
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
- mListener(listener) {
- // The rest of the initialization is done in onFirstRef, because we need to obtain
- // an sp to 'this' in order to register for HAL death notifications
-}
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+ : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
-void InputClassifier::onFirstRef() {
- if (!deepPressEnabled()) {
- // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
- // When MotionClassifier is null, InputClassifier will forward all events
- // to the next InputListener, unmodified.
- return;
+void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+ if (enabled) {
+ ALOGI("Enabling motion classifier");
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+ mInitializeMotionClassifierThread = std::thread(
+ [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+ "Create MotionClassifier");
+#endif
+ } else {
+ ALOGI("Disabling motion classifier");
+ setMotionClassifier(nullptr);
}
- std::scoped_lock lock(mLock);
- mMotionClassifier = std::make_unique<MotionClassifier>(this);
}
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -441,15 +399,10 @@
mListener->notifyDeviceReset(args);
}
-void InputClassifier::serviceDied(uint64_t /*cookie*/,
- const wp<android::hidl::base::V1_0::IBase>& who) {
+void InputClassifier::setMotionClassifier(
+ std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
- ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
- mMotionClassifier = nullptr;
- sp<android::hidl::base::V1_0::IBase> service = who.promote();
- if (service) {
- service->unlinkToDeath(this);
- }
+ mMotionClassifier = std::move(motionClassifier);
}
void InputClassifier::dump(std::string& dump) {
@@ -465,4 +418,10 @@
dump += "\n";
}
+InputClassifier::~InputClassifier() {
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 47e20db..9ac8e2d 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -19,8 +19,8 @@
#include <android-base/thread_annotations.h>
#include <utils/RefBase.h>
-#include <unordered_map>
#include <thread>
+#include <unordered_map>
#include "BlockingQueue.h"
#include "InputListener.h"
@@ -90,6 +90,7 @@
*/
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
public:
+ virtual void setMotionClassifierEnabled(bool enabled) = 0;
/**
* Dump the state of the input classifier.
* This method may be called on any thread (usually by the input manager).
@@ -113,23 +114,23 @@
*/
class MotionClassifier final : public MotionClassifierInterface {
public:
- /**
- * The deathRecipient will be subscribed to the HAL death. If the death recipient
- * owns MotionClassifier and receives HAL death, it should delete its copy of it.
- * The callback serviceDied will also be sent if the MotionClassifier itself fails
- * to initialize. If the MotionClassifier fails to initialize, it is not useful, and
- * should be deleted.
- * If no death recipient is supplied, then the registration step will be skipped, so there will
- * be no listeners registered for the HAL death. This is useful for testing
- * MotionClassifier in isolation.
+ /*
+ * Create an instance of MotionClassifier.
+ * The death recipient, if provided, will be subscribed to the HAL death.
+ * The death recipient could be used to destroy MotionClassifier.
+ *
+ * This function should be called asynchronously, because getService takes a long time.
*/
- explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr);
+ static std::unique_ptr<MotionClassifierInterface> create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient);
+
~MotionClassifier();
/**
* Classifies events asynchronously; that is, it doesn't block events on a classification,
- * but instead sends them over to the classifier HAL and after a classification is
- * determined, it then marks the next event it sees in the stream with it.
+ * but instead sends them over to the classifier HAL. After a classification of a specific
+ * event is determined, MotionClassifier then marks the next event in the stream with this
+ * classification.
*
* Therefore, it is acceptable to have the classifications be delayed by 1-2 events
* in a particular gesture.
@@ -141,15 +142,9 @@
virtual void dump(std::string& dump) override;
private:
- /**
- * Initialize MotionClassifier.
- * Return true if initializaion is successful.
- */
- bool init();
- /**
- * Entity that will be notified of the HAL death (most likely InputClassifier).
- */
- wp<android::hardware::hidl_death_recipient> mDeathRecipient;
+ friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
+ explicit MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
// The events that need to be sent to the HAL.
BlockingQueue<ClassifierEvent> mEvents;
@@ -164,14 +159,9 @@
*/
std::thread mHalThread;
/**
- * Print an error message if the caller is not on the InputClassifier thread.
- * Caller must supply the name of the calling function as __func__
+ * Process events and call the InputClassifier HAL
*/
- void ensureHalThread(const char* function);
- /**
- * Call the InputClassifier HAL
- */
- void callInputClassifierHal();
+ void processEvents();
/**
* Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
* When init() successfully completes, mService is guaranteed to remain non-null and to not
@@ -223,19 +213,15 @@
const char* getServiceStatus() REQUIRES(mLock);
};
-
/**
* Implementation of the InputClassifierInterface.
* Represents a separate stage of input processing. All of the input events go through this stage.
* Acts as a passthrough for all input events except for motion events.
* The events of motion type are sent to MotionClassifier.
*/
-class InputClassifier : public InputClassifierInterface,
- public android::hardware::hidl_death_recipient {
+class InputClassifier : public InputClassifierInterface {
public:
explicit InputClassifier(const sp<InputListenerInterface>& listener);
- // Some of the constructor logic is finished in onFirstRef
- virtual void onFirstRef() override;
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -243,17 +229,47 @@
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override;
-
virtual void dump(std::string& dump) override;
+ ~InputClassifier();
+
+ // Called from InputManager
+ virtual void setMotionClassifierEnabled(bool enabled) override;
+
private:
// Protect access to mMotionClassifier, since it may become null via a hidl callback
std::mutex mLock;
- std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
// The next stage to pass input events to
sp<InputListenerInterface> mListener;
+
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
+ std::thread mInitializeMotionClassifierThread;
+ /**
+ * Set the value of mMotionClassifier.
+ * This is called from 2 different threads:
+ * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
+ * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
+ * mMotionClassifier to become nullptr.
+ */
+ void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
+
+ /**
+ * The deathRecipient will call setMotionClassifier(null) when the HAL dies.
+ */
+ class HalDeathRecipient : public android::hardware::hidl_death_recipient {
+ public:
+ explicit HalDeathRecipient(InputClassifier& parent);
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ InputClassifier& mParent;
+ };
+ // We retain a reference to death recipient, because the death recipient will be calling
+ // ~MotionClassifier if the HAL dies.
+ // If we don't retain a reference, and MotionClassifier is the only owner of the death
+ // recipient, the serviceDied call will cause death recipient to call its own destructor.
+ sp<HalDeathRecipient> mHalDeathRecipient;
};
} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 3996cca..52e9080 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -117,10 +117,6 @@
}
}
-void InputManager::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
- mDispatcher->transferTouchFocus(fromToken, toToken);
-}
-
// Used by tests only.
void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
IPCThreadState* ipc = IPCThreadState::self();
@@ -137,4 +133,8 @@
mDispatcher->unregisterInputChannel(channel);
}
+void InputManager::setMotionClassifierEnabled(bool enabled) {
+ mClassifier->setMotionClassifierEnabled(enabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index e568df5..c75611f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -96,11 +96,12 @@
virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
const sp<ISetInputWindowsListener>& setInputWindowsListener);
- virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
virtual void registerInputChannel(const sp<InputChannel>& channel);
virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+ void setMotionClassifierEnabled(bool enabled);
+
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index d8b352c..973b4f9 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -42,7 +42,6 @@
virtual status_t dump(int fd, const Vector<String16>& args);
void setInputWindows(const std::vector<InputWindowInfo>&,
const sp<ISetInputWindowsListener>&) {}
- void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {}
void registerInputChannel(const sp<InputChannel>&) {}
void unregisterInputChannel(const sp<InputChannel>&) {}
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 7cc17a2..4cddec5 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -22,6 +22,9 @@
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
using namespace android::hardware::input;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::input::common::V1_0::Classification;
namespace android {
@@ -129,6 +132,49 @@
ASSERT_EQ(args, outArgs);
}
+TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
+ mClassifier->setMotionClassifierEnabled(false);
+}
+
+/**
+ * Try to break it by calling setMotionClassifierEnabled multiple times.
+ */
+TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+/**
+ * A minimal implementation of IInputClassifier.
+ */
+struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
+ Return<Classification> classify(
+ const android::hardware::input::common::V1_0::MotionEvent& event) override {
+ return Classification::NONE;
+ };
+ Return<void> reset() override { return Void(); };
+ Return<void> resetDevice(int32_t deviceId) override { return Void(); };
+};
+
+/**
+ * An entity that will be subscribed to the HAL death.
+ */
+class TestDeathRecipient : public android::hardware::hidl_death_recipient {
+public:
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override{};
+};
+
// --- MotionClassifierTest ---
class MotionClassifierTest : public testing::Test {
@@ -136,7 +182,14 @@
std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
virtual void SetUp() override {
- mMotionClassifier = std::make_unique<MotionClassifier>();
+ mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
+ if (mMotionClassifier == nullptr) {
+ // If the device running this test does not have IInputClassifier service,
+ // use the test HAL instead.
+ // Using 'new' to access non-public constructor
+ mMotionClassifier =
+ std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
+ }
}
};
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index c11b88e..090a0ce 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -208,12 +208,6 @@
registerSensor(new RotationVectorSensor(), !needRotationVector, true);
registerSensor(new OrientationSensor(), !needRotationVector, true);
- bool needLinearAcceleration =
- (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
-
- registerSensor(new LinearAccelerationSensor(list, count),
- !needLinearAcceleration, true);
-
// virtual debugging sensors are not for user
registerSensor( new CorrectedGyroSensor(list, count), true, true);
registerSensor( new GyroDriftSensor(), true, true);
@@ -223,6 +217,11 @@
bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
+ bool needLinearAcceleration =
+ (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
+ registerSensor(new LinearAccelerationSensor(list, count),
+ !needLinearAcceleration, true);
+
bool needGameRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
@@ -299,14 +298,10 @@
}
void SensorService::setSensorAccess(uid_t uid, bool hasAccess) {
- SortedVector< sp<SensorEventConnection> > activeConnections;
- populateActiveConnections(&activeConnections);
- {
- Mutex::Autolock _l(mLock);
- for (size_t i = 0 ; i < activeConnections.size(); i++) {
- if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) {
- activeConnections[i]->setSensorAccess(hasAccess);
- }
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
+ if (conn->getUid() == uid) {
+ conn->setSensorAccess(hasAccess);
}
}
}
@@ -360,7 +355,7 @@
if (args.size() > 2) {
return INVALID_OPERATION;
}
- Mutex::Autolock _l(mLock);
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
SensorDevice& dev(SensorDevice::getInstance());
if (args.size() == 2 && args[0] == String16("restrict")) {
// If already in restricted mode. Ignore.
@@ -374,7 +369,7 @@
mCurrentOperatingMode = RESTRICTED;
// temporarily stop all sensor direct report and disable sensors
- disableAllSensorsLocked();
+ disableAllSensorsLocked(&connLock);
mWhiteListedPackage.setTo(String8(args[1]));
return status_t(NO_ERROR);
} else if (args.size() == 1 && args[0] == String16("enable")) {
@@ -382,7 +377,7 @@
if (mCurrentOperatingMode == RESTRICTED) {
mCurrentOperatingMode = NORMAL;
// enable sensors and recover all sensor direct report
- enableAllSensorsLocked();
+ enableAllSensorsLocked(&connLock);
}
if (mCurrentOperatingMode == DATA_INJECTION) {
resetToNormalModeLocked();
@@ -473,22 +468,18 @@
result.appendFormat("Sensor Privacy: %s\n",
mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
- result.appendFormat("%zd active connections\n", mActiveConnections.size());
- for (size_t i=0 ; i < mActiveConnections.size() ; i++) {
- sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != nullptr) {
- result.appendFormat("Connection Number: %zu \n", i);
- connection->dump(result);
- }
+ const auto& activeConnections = connLock.getActiveConnections();
+ result.appendFormat("%zd active connections\n", activeConnections.size());
+ for (size_t i=0 ; i < activeConnections.size() ; i++) {
+ result.appendFormat("Connection Number: %zu \n", i);
+ activeConnections[i]->dump(result);
}
- result.appendFormat("%zd direct connections\n", mDirectConnections.size());
- for (size_t i = 0 ; i < mDirectConnections.size() ; i++) {
- sp<SensorDirectConnection> connection(mDirectConnections[i].promote());
- if (connection != nullptr) {
- result.appendFormat("Direct connection %zu:\n", i);
- connection->dump(result);
- }
+ const auto& directConnections = connLock.getDirectConnections();
+ result.appendFormat("%zd direct connections\n", directConnections.size());
+ for (size_t i = 0 ; i < directConnections.size() ; i++) {
+ result.appendFormat("Direct connection %zu:\n", i);
+ directConnections[i]->dump(result);
}
result.appendFormat("Previous Registrations:\n");
@@ -515,17 +506,14 @@
}
void SensorService::disableAllSensors() {
- Mutex::Autolock _l(mLock);
- disableAllSensorsLocked();
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ disableAllSensorsLocked(&connLock);
}
-void SensorService::disableAllSensorsLocked() {
+void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) {
SensorDevice& dev(SensorDevice::getInstance());
- for (auto &i : mDirectConnections) {
- sp<SensorDirectConnection> connection(i.promote());
- if (connection != nullptr) {
- connection->stopAll(true /* backupRecord */);
- }
+ for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
+ connection->stopAll(true /* backupRecord */);
}
dev.disableAllSensors();
// Clear all pending flush connections for all active sensors. If one of the active
@@ -537,11 +525,11 @@
}
void SensorService::enableAllSensors() {
- Mutex::Autolock _l(mLock);
- enableAllSensorsLocked();
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ enableAllSensorsLocked(&connLock);
}
-void SensorService::enableAllSensorsLocked() {
+void SensorService::enableAllSensorsLocked(ConnectionSafeAutolock* connLock) {
// sensors should only be enabled if the operating state is not restricted and sensor
// privacy is not enabled.
if (mCurrentOperatingMode == RESTRICTED || mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -552,14 +540,12 @@
}
SensorDevice& dev(SensorDevice::getInstance());
dev.enableAllSensors();
- for (auto &i : mDirectConnections) {
- sp<SensorDirectConnection> connection(i.promote());
- if (connection != nullptr) {
- connection->recoverAll();
- }
+ for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
+ connection->recoverAll();
}
}
+
// NOTE: This is a remote API - make sure all args are validated
status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) {
if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
@@ -734,17 +720,8 @@
for (int i = 0; i < count; i++) {
mSensorEventBuffer[i].flags = 0;
}
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
- // Make a copy of the connection vector as some connections may be removed during the course
- // of this loop (especially when one-shot sensor events are present in the sensor_event
- // buffer). Promote all connections to StrongPointers before the lock is acquired. If the
- // destructor of the sp gets called when the lock is acquired, it may result in a deadlock
- // as ~SensorEventConnection() needs to acquire mLock again for cleanup. So copy all the
- // strongPointers to a vector before the lock is acquired.
- SortedVector< sp<SensorEventConnection> > activeConnections;
- populateActiveConnections(&activeConnections);
-
- Mutex::Autolock _l(mLock);
// Poll has returned. Hold a wakelock if one of the events is from a wake up sensor. The
// rest of this loop is under a critical section protected by mLock. Acquiring a wakeLock,
// sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should
@@ -818,6 +795,10 @@
}
}
+ // Cache the list of active connections, since we use it in multiple places below but won't
+ // modify it here
+ const std::vector<sp<SensorEventConnection>> activeConnections = connLock.getActiveConnections();
+
for (int i = 0; i < count; ++i) {
// Map flush_complete_events in the buffer to SensorEventConnections which called flush
// on the hardware sensor. mapFlushEventsToConnections[i] will be the
@@ -869,11 +850,8 @@
ALOGE("Dynamic sensor release error.");
}
- size_t numConnections = activeConnections.size();
- for (size_t i=0 ; i < numConnections; ++i) {
- if (activeConnections[i] != nullptr) {
- activeConnections[i]->removeSensor(handle);
- }
+ for (const sp<SensorEventConnection>& connection : activeConnections) {
+ connection->removeSensor(handle);
}
}
}
@@ -882,18 +860,14 @@
// Send our events to clients. Check the state of wake lock for each client and release the
// lock if none of the clients need it.
bool needsWakeLock = false;
- size_t numConnections = activeConnections.size();
- for (size_t i=0 ; i < numConnections; ++i) {
- if (activeConnections[i] != nullptr) {
- activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
- mMapFlushEventsToConnections);
- needsWakeLock |= activeConnections[i]->needsWakeLock();
- // If the connection has one-shot sensors, it may be cleaned up after first trigger.
- // Early check for one-shot sensors.
- if (activeConnections[i]->hasOneShotSensors()) {
- cleanupAutoDisabledSensorLocked(activeConnections[i], mSensorEventBuffer,
- count);
- }
+ for (const sp<SensorEventConnection>& connection : activeConnections) {
+ connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
+ mMapFlushEventsToConnections);
+ needsWakeLock |= connection->needsWakeLock();
+ // If the connection has one-shot sensors, it may be cleaned up after first trigger.
+ // Early check for one-shot sensors.
+ if (connection->hasOneShotSensors()) {
+ cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count);
}
}
@@ -912,17 +886,11 @@
}
void SensorService::resetAllWakeLockRefCounts() {
- SortedVector< sp<SensorEventConnection> > activeConnections;
- populateActiveConnections(&activeConnections);
- {
- Mutex::Autolock _l(mLock);
- for (size_t i=0 ; i < activeConnections.size(); ++i) {
- if (activeConnections[i] != nullptr) {
- activeConnections[i]->resetWakeLockRefCount();
- }
- }
- setWakeLockAcquiredLocked(false);
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
+ connection->resetWakeLockRefCount();
}
+ setWakeLockAcquiredLocked(false);
}
void SensorService::setWakeLockAcquiredLocked(bool acquire) {
@@ -1144,9 +1112,7 @@
sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
requestedMode == DATA_INJECTION, connOpPackageName, hasSensorAccess));
if (requestedMode == DATA_INJECTION) {
- if (mActiveConnections.indexOf(result) < 0) {
- mActiveConnections.add(result);
- }
+ mConnectionHolder.addEventConnectionIfNotPresent(result);
// Add the associated file descriptor to the Looper for polling whenever there is data to
// be injected.
result->updateLooperRegistration(mLooper);
@@ -1162,7 +1128,7 @@
sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
const native_handle *resource) {
- Mutex::Autolock _l(mLock);
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
// No new direct connections are allowed when sensor privacy is enabled
if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -1190,9 +1156,8 @@
}
// check for duplication
- for (auto &i : mDirectConnections) {
- sp<SensorDirectConnection> connection(i.promote());
- if (connection != nullptr && connection->isEquivalent(&mem)) {
+ for (const sp<SensorDirectConnection>& connection : connLock.getDirectConnections()) {
+ if (connection->isEquivalent(&mem)) {
ALOGE("Duplicate create channel request for the same share memory");
return nullptr;
}
@@ -1229,7 +1194,7 @@
return nullptr;
}
- SensorDirectConnection* conn = nullptr;
+ sp<SensorDirectConnection> conn;
SensorDevice& dev(SensorDevice::getInstance());
int channelHandle = dev.registerDirectChannel(&mem);
@@ -1246,7 +1211,7 @@
} else {
// add to list of direct connections
// sensor service should never hold pointer or sp of SensorDirectConnection object.
- mDirectConnections.add(wp<SensorDirectConnection>(conn));
+ mConnectionHolder.addDirectConnection(conn);
}
return conn;
}
@@ -1358,7 +1323,7 @@
}
void SensorService::cleanupConnection(SensorEventConnection* c) {
- Mutex::Autolock _l(mLock);
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
const wp<SensorEventConnection> connection(c);
size_t size = mActiveSensors.size();
ALOGD_IF(DEBUG_CONNECTIONS, "%zu active sensors", size);
@@ -1391,10 +1356,10 @@
}
}
c->updateLooperRegistration(mLooper);
- mActiveConnections.remove(connection);
+ mConnectionHolder.removeEventConnection(connection);
BatteryService::cleanup(c->getUid());
if (c->needsWakeLock()) {
- checkWakeLockStateLocked();
+ checkWakeLockStateLocked(&connLock);
}
{
@@ -1414,7 +1379,7 @@
SensorDevice& dev(SensorDevice::getInstance());
dev.unregisterDirectChannel(c->getHalChannelHandle());
- mDirectConnections.remove(c);
+ mConnectionHolder.removeDirectConnection(c);
}
sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
@@ -1433,7 +1398,7 @@
return BAD_VALUE;
}
- Mutex::Autolock _l(mLock);
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
if (mCurrentOperatingMode != NORMAL
&& !isWhiteListedPackage(connection->getPackageName())) {
return INVALID_OPERATION;
@@ -1484,7 +1449,7 @@
}
connection->sendEvents(&event, 1, nullptr);
if (!connection->needsWakeLock() && mWakeLockAcquired) {
- checkWakeLockStateLocked();
+ checkWakeLockStateLocked(&connLock);
}
}
}
@@ -1497,9 +1462,7 @@
BatteryService::enableSensor(connection->getUid(), handle);
// the sensor was added (which means it wasn't already there)
// so, see if this connection becomes active
- if (mActiveConnections.indexOf(connection) < 0) {
- mActiveConnections.add(connection);
- }
+ mConnectionHolder.addEventConnectionIfNotPresent(connection);
} else {
ALOGW("sensor %08x already enabled in connection %p (ignoring)",
handle, connection.get());
@@ -1603,7 +1566,7 @@
}
if (connection->hasAnySensor() == false) {
connection->updateLooperRegistration(mLooper);
- mActiveConnections.remove(connection);
+ mConnectionHolder.removeEventConnection(connection);
}
// see if this sensor becomes inactive
if (rec->removeConnection(connection)) {
@@ -1762,22 +1725,19 @@
}
void SensorService::checkWakeLockState() {
- Mutex::Autolock _l(mLock);
- checkWakeLockStateLocked();
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ checkWakeLockStateLocked(&connLock);
}
-void SensorService::checkWakeLockStateLocked() {
+void SensorService::checkWakeLockStateLocked(ConnectionSafeAutolock* connLock) {
if (!mWakeLockAcquired) {
return;
}
bool releaseLock = true;
- for (size_t i=0 ; i<mActiveConnections.size() ; i++) {
- sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != nullptr) {
- if (connection->needsWakeLock()) {
- releaseLock = false;
- break;
- }
+ for (const sp<SensorEventConnection>& connection : connLock->getActiveConnections()) {
+ if (connection->needsWakeLock()) {
+ releaseLock = false;
+ break;
}
}
if (releaseLock) {
@@ -1793,17 +1753,6 @@
}
}
-void SensorService::populateActiveConnections(
- SortedVector< sp<SensorEventConnection> >* activeConnections) {
- Mutex::Autolock _l(mLock);
- for (size_t i=0 ; i < mActiveConnections.size(); ++i) {
- sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != nullptr) {
- activeConnections->add(connection);
- }
- }
-}
-
bool SensorService::isWhiteListedPackage(const String8& packageName) {
return (packageName.contains(mWhiteListedPackage.string()));
}
@@ -1938,4 +1887,62 @@
}
return binder::Status::ok();
}
-}; // namespace android
+
+SensorService::ConnectionSafeAutolock::ConnectionSafeAutolock(
+ SensorService::SensorConnectionHolder& holder, Mutex& mutex)
+ : mConnectionHolder(holder), mAutolock(mutex) {}
+
+template<typename ConnectionType>
+const std::vector<sp<ConnectionType>>& SensorService::ConnectionSafeAutolock::getConnectionsHelper(
+ const SortedVector<wp<ConnectionType>>& connectionList,
+ std::vector<std::vector<sp<ConnectionType>>>* referenceHolder) {
+ referenceHolder->emplace_back();
+ std::vector<sp<ConnectionType>>& connections = referenceHolder->back();
+ for (const wp<ConnectionType>& weakConnection : connectionList){
+ sp<ConnectionType> connection = weakConnection.promote();
+ if (connection != nullptr) {
+ connections.push_back(std::move(connection));
+ }
+ }
+ return connections;
+}
+
+const std::vector<sp<SensorService::SensorEventConnection>>&
+ SensorService::ConnectionSafeAutolock::getActiveConnections() {
+ return getConnectionsHelper(mConnectionHolder.mActiveConnections,
+ &mReferencedActiveConnections);
+}
+
+const std::vector<sp<SensorService::SensorDirectConnection>>&
+ SensorService::ConnectionSafeAutolock::getDirectConnections() {
+ return getConnectionsHelper(mConnectionHolder.mDirectConnections,
+ &mReferencedDirectConnections);
+}
+
+void SensorService::SensorConnectionHolder::addEventConnectionIfNotPresent(
+ const sp<SensorService::SensorEventConnection>& connection) {
+ if (mActiveConnections.indexOf(connection) < 0) {
+ mActiveConnections.add(connection);
+ }
+}
+
+void SensorService::SensorConnectionHolder::removeEventConnection(
+ const wp<SensorService::SensorEventConnection>& connection) {
+ mActiveConnections.remove(connection);
+}
+
+void SensorService::SensorConnectionHolder::addDirectConnection(
+ const sp<SensorService::SensorDirectConnection>& connection) {
+ mDirectConnections.add(connection);
+}
+
+void SensorService::SensorConnectionHolder::removeDirectConnection(
+ const wp<SensorService::SensorDirectConnection>& connection) {
+ mDirectConnections.remove(connection);
+}
+
+SensorService::ConnectionSafeAutolock SensorService::SensorConnectionHolder::lock(Mutex& mutex) {
+ return ConnectionSafeAutolock(*this, mutex);
+}
+
+} // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index e6ec96d..060b5eb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -20,6 +20,7 @@
#include "SensorList.h"
#include "RecentEventLogger.h"
+#include <android-base/macros.h>
#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IUidObserver.h>
@@ -42,6 +43,7 @@
#include <sys/types.h>
#include <unordered_map>
#include <unordered_set>
+#include <vector>
#if __clang__
// Clang warns about SensorEventConnection::dump hiding BBinder::dump. The cause isn't fixable
@@ -95,10 +97,67 @@
friend class BinderService<SensorService>;
// nested class/struct for internal use
- class SensorRecord;
+ class ConnectionSafeAutolock;
+ class SensorConnectionHolder;
class SensorEventAckReceiver;
+ class SensorRecord;
class SensorRegistrationInfo;
+ // Promoting a SensorEventConnection or SensorDirectConnection from wp to sp must be done with
+ // mLock held, but destroying that sp must be done unlocked to avoid a race condition that
+ // causes a deadlock (remote dies while we hold a local sp, then our decStrong() call invokes
+ // the dtor -> cleanupConnection() tries to re-lock the mutex). This class ensures safe usage
+ // by wrapping a Mutex::Autolock on SensorService's mLock, plus vectors that hold promoted sp<>
+ // references until the lock is released, when they are safely destroyed.
+ // All read accesses to the connection lists in mConnectionHolder must be done via this class.
+ class ConnectionSafeAutolock final {
+ public:
+ // Returns a list of non-null promoted connection references
+ const std::vector<sp<SensorEventConnection>>& getActiveConnections();
+ const std::vector<sp<SensorDirectConnection>>& getDirectConnections();
+
+ private:
+ // Constructed via SensorConnectionHolder::lock()
+ friend class SensorConnectionHolder;
+ explicit ConnectionSafeAutolock(SensorConnectionHolder& holder, Mutex& mutex);
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ConnectionSafeAutolock);
+
+ // NOTE: Order of these members is important, as the destructor for non-static members
+ // get invoked in the reverse order of their declaration. Here we are relying on the
+ // Autolock to be destroyed *before* the vectors, so the sp<> objects are destroyed without
+ // the lock held, which avoids the deadlock.
+ SensorConnectionHolder& mConnectionHolder;
+ std::vector<std::vector<sp<SensorEventConnection>>> mReferencedActiveConnections;
+ std::vector<std::vector<sp<SensorDirectConnection>>> mReferencedDirectConnections;
+ Mutex::Autolock mAutolock;
+
+ template<typename ConnectionType>
+ const std::vector<sp<ConnectionType>>& getConnectionsHelper(
+ const SortedVector<wp<ConnectionType>>& connectionList,
+ std::vector<std::vector<sp<ConnectionType>>>* referenceHolder);
+ };
+
+ // Encapsulates the collection of active SensorEventConection and SensorDirectConnection
+ // references. Write access is done through this class with mLock held, but all read access
+ // must be routed through ConnectionSafeAutolock.
+ class SensorConnectionHolder {
+ public:
+ void addEventConnectionIfNotPresent(const sp<SensorEventConnection>& connection);
+ void removeEventConnection(const wp<SensorEventConnection>& connection);
+
+ void addDirectConnection(const sp<SensorDirectConnection>& connection);
+ void removeDirectConnection(const wp<SensorDirectConnection>& connection);
+
+ // Pass in the mutex that protects this connection holder; acquires the lock and returns an
+ // object that can be used to safely read the lists of connections
+ ConnectionSafeAutolock lock(Mutex& mutex);
+
+ private:
+ friend class ConnectionSafeAutolock;
+ SortedVector< wp<SensorEventConnection> > mActiveConnections;
+ SortedVector< wp<SensorDirectConnection> > mDirectConnections;
+ };
+
// If accessing a sensor we need to make sure the UID has access to it. If
// the app UID is idle then it cannot access sensors and gets no trigger
// events, no on-change events, flush event behavior does not change, and
@@ -250,7 +309,7 @@
// method checks whether all the events from these wake up sensors have been delivered to the
// corresponding applications, if yes the wakelock is released.
void checkWakeLockState();
- void checkWakeLockStateLocked();
+ void checkWakeLockStateLocked(ConnectionSafeAutolock* connLock);
bool isWakeLockAcquired();
bool isWakeUpSensorEvent(const sensors_event_t& event) const;
@@ -268,10 +327,6 @@
// Send events from the event cache for this particular connection.
void sendEventsFromCache(const sp<SensorEventConnection>& connection);
- // Promote all weak referecences in mActiveConnections vector to strong references and add them
- // to the output vector.
- void populateActiveConnections( SortedVector< sp<SensorEventConnection> >* activeConnections);
-
// If SensorService is operating in RESTRICTED mode, only select whitelisted packages are
// allowed to register for or call flush on sensors. Typically only cts test packages are
// allowed.
@@ -306,10 +361,10 @@
// temporarily stops all active direct connections and disables all sensors
void disableAllSensors();
- void disableAllSensorsLocked();
+ void disableAllSensorsLocked(ConnectionSafeAutolock* connLock);
// restarts the previously stopped direct connections and enables all sensors
void enableAllSensors();
- void enableAllSensorsLocked();
+ void enableAllSensorsLocked(ConnectionSafeAutolock* connLock);
static uint8_t sHmacGlobalKey[128];
static bool sHmacGlobalKeyIsValid;
@@ -327,12 +382,13 @@
mutable Mutex mLock;
DefaultKeyedVector<int, SensorRecord*> mActiveSensors;
std::unordered_set<int> mActiveVirtualSensors;
- SortedVector< wp<SensorEventConnection> > mActiveConnections;
+ SensorConnectionHolder mConnectionHolder;
bool mWakeLockAcquired;
sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
+ // WARNING: these SensorEventConnection instances must not be promoted to sp, except via
+ // modification to add support for them in ConnectionSafeAutolock
wp<const SensorEventConnection> * mMapFlushEventsToConnections;
std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
- SortedVector< wp<SensorDirectConnection> > mDirectConnections;
Mode mCurrentOperatingMode;
// This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 4cd0a13..501d176 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -149,9 +149,10 @@
"Scheduler/LayerHistory.cpp",
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
+ "Scheduler/PhaseOffsets.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
- "Scheduler/PhaseOffsets.cpp",
+ "Scheduler/VSyncModulator.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceInterceptor.cpp",
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b679380..46a62ed 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -48,7 +48,7 @@
class BufferLayer : public Layer {
public:
explicit BufferLayer(const LayerCreationArgs& args);
- ~BufferLayer() override;
+ virtual ~BufferLayer() override;
// -----------------------------------------------------------------------
// Overriden from Layer
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 6709fb4..414814a 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -369,6 +369,15 @@
return mCurrentSurfaceDamage;
}
+void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) {
+ if (damage.bounds() == Rect::INVALID_RECT ||
+ mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) {
+ mCurrentSurfaceDamage = Region::INVALID_REGION;
+ } else {
+ mCurrentSurfaceDamage |= damage;
+ }
+}
+
int BufferLayerConsumer::getCurrentApi() const {
Mutex::Autolock lock(mMutex);
return mCurrentApi;
@@ -485,7 +494,6 @@
if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
- mRE.cacheExternalTextureBuffer(item.mGraphicBuffer);
}
}
}
@@ -522,6 +530,12 @@
ConsumerBase::dumpLocked(result, prefix);
}
+BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
+ renderengine::RenderEngine& engine)
+ : mGraphicBuffer(graphicBuffer), mRE(engine) {
+ mRE.cacheExternalTextureBuffer(mGraphicBuffer);
+}
+
BufferLayerConsumer::Image::~Image() {
if (mGraphicBuffer != nullptr) {
ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index e3f6100..617b1c2 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -140,6 +140,9 @@
// must be called from SF main thread
const Region& getSurfaceDamage() const;
+ // Merge the given damage region into the current damage region value.
+ void mergeSurfaceDamage(const Region& damage);
+
// getCurrentApi retrieves the API which queues the current buffer.
int getCurrentApi() const;
@@ -220,8 +223,7 @@
// Utility class for managing GraphicBuffer references into renderengine
class Image {
public:
- Image(sp<GraphicBuffer> graphicBuffer, renderengine::RenderEngine& engine)
- : mGraphicBuffer(graphicBuffer), mRE(engine) {}
+ Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine);
virtual ~Image();
const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index bd0b55f..eb1bf66 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -143,7 +143,7 @@
}
Mutex::Autolock lock(mQueueItemLock);
- return mQueueItems[0].mTimestamp <= mFlinger->mScheduler->expectedPresentTime();
+ return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime();
}
nsecs_t BufferQueueLayer::getDesiredPresentTime() {
@@ -201,7 +201,7 @@
uint64_t frameNumber = mQueueItems[0].mFrameNumber;
// The head of the queue will be dropped if there are signaled and timely frames behind it
- nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime();
+ nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
if (isRemovedFromCurrentState()) {
expectedPresentTime = 0;
@@ -279,7 +279,7 @@
getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
getTransformToDisplayInverse(), mFreezeGeometryUpdates);
- nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime();
+ nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
if (isRemovedFromCurrentState()) {
expectedPresentTime = 0;
@@ -316,6 +316,7 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
@@ -351,6 +352,7 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
@@ -459,6 +461,7 @@
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", mName.string());
+ break;
}
}
@@ -493,6 +496,7 @@
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", mName.string());
+ break;
}
}
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 05c721f..63a07c3 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -50,8 +50,12 @@
mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
}
+
BufferStateLayer::~BufferStateLayer() {
if (mActiveBuffer != nullptr) {
+ // Ensure that mActiveBuffer is uncached from RenderEngine here, as
+ // RenderEngine may have been using the buffer as an external texture
+ // after the client uncached the buffer.
auto& engine(mFlinger->getRenderEngine());
engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
}
@@ -380,7 +384,7 @@
return true;
}
- return mDesiredPresentTime <= mFlinger->mScheduler->expectedPresentTime();
+ return mDesiredPresentTime <= mFlinger->getExpectedPresentTime();
}
nsecs_t BufferStateLayer::getDesiredPresentTime() {
@@ -571,11 +575,6 @@
return BAD_VALUE;
}
- if (mActiveBuffer != nullptr) {
- // todo: get this to work with BufferStateLayerCache
- auto& engine(mFlinger->getRenderEngine());
- engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
- }
mActiveBuffer = s.buffer;
mActiveBufferFence = s.acquireFence;
auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 4e2bc45..97e24cb 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -34,6 +34,7 @@
class BufferStateLayer : public BufferLayer {
public:
explicit BufferStateLayer(const LayerCreationArgs&);
+
~BufferStateLayer() override;
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 77f2f57..16fe27c 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -55,16 +55,16 @@
return true;
}
-void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
+bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
auto& [processToken, id] = cacheId;
if (processToken == nullptr) {
ALOGE("failed to cache buffer: invalid process token");
- return;
+ return false;
}
if (!buffer) {
ALOGE("failed to cache buffer: invalid buffer");
- return;
+ return false;
}
std::lock_guard lock(mMutex);
@@ -77,13 +77,13 @@
token = processToken.promote();
if (!token) {
ALOGE("failed to cache buffer: invalid token");
- return;
+ return false;
}
status_t err = token->linkToDeath(mDeathRecipient);
if (err != NO_ERROR) {
ALOGE("failed to cache buffer: could not link to death");
- return;
+ return false;
}
auto [itr, success] =
mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>());
@@ -95,10 +95,11 @@
if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
ALOGE("failed to cache buffer: cache is full");
- return;
+ return false;
}
processBuffers[id].buffer = buffer;
+ return true;
}
void ClientCache::erase(const client_cache_t& cacheId) {
@@ -139,16 +140,17 @@
return buf->buffer;
}
-void ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
+bool ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
const wp<ErasedRecipient>& recipient) {
std::lock_guard lock(mMutex);
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
ALOGE("failed to register erased recipient, could not retrieve buffer");
- return;
+ return false;
}
buf->recipients.insert(recipient);
+ return true;
}
void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId,
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index 9f057c4..aa6c80d 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -36,7 +36,7 @@
public:
ClientCache();
- void add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+ bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
void erase(const client_cache_t& cacheId);
sp<GraphicBuffer> get(const client_cache_t& cacheId);
@@ -48,7 +48,7 @@
virtual void bufferErased(const client_cache_t& clientCacheId) = 0;
};
- void registerErasedRecipient(const client_cache_t& cacheId,
+ bool registerErasedRecipient(const client_cache_t& cacheId,
const wp<ErasedRecipient>& recipient);
void unregisterErasedRecipient(const client_cache_t& cacheId,
const wp<ErasedRecipient>& recipient);
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 7927fa9..e33bedb 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -35,11 +35,7 @@
return false;
}
-bool ContainerLayer::canReceiveInput() const {
- return !isHiddenByPolicy();
-}
void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
const Rect&, int32_t, const ui::Dataspace) {}
-
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 7222a3e..e6dbfcc 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -31,8 +31,6 @@
const char* getTypeId() const override { return "ContainerLayer"; }
bool isVisible() const override;
- bool canReceiveInput() const override;
-
void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
const Rect& viewport, int32_t supportedPerFrameMetadata,
const ui::Dataspace targetDataspace) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index cc5a5b5..7f47a2e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -24,6 +24,7 @@
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <gui/BufferQueue.h>
+#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
namespace android {
@@ -229,6 +230,7 @@
void Composer::registerCallback(const sp<IComposerCallback>& callback)
{
+ android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
auto ret = mClient->registerCallback(callback);
if (!ret.isOk()) {
ALOGE("failed to register IComposerCallback");
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index fccaf81..3ee4ef1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2037,11 +2037,18 @@
return mRemovedFromCurrentState;
}
+// Debug helper for b/137560795
+#define INT32_MIGHT_OVERFLOW(n) (((n) >= INT32_MAX / 2) || ((n) <= INT32_MIN / 2))
+
+#define RECT_BOUNDS_INVALID(rect) \
+ (INT32_MIGHT_OVERFLOW((rect).left) || INT32_MIGHT_OVERFLOW((rect).right) || \
+ INT32_MIGHT_OVERFLOW((rect).bottom) || INT32_MIGHT_OVERFLOW((rect).top))
+
InputWindowInfo Layer::fillInputInfo() {
InputWindowInfo info = mDrawingState.inputInfo;
if (info.displayId == ADISPLAY_ID_NONE) {
- info.displayId = mDrawingState.layerStack;
+ info.displayId = getLayerStack();
}
ui::Transform t = getTransform();
@@ -2066,6 +2073,26 @@
layerBounds = getCroppedBufferSize(getDrawingState());
}
layerBounds = t.transform(layerBounds);
+
+ // debug check for b/137560795
+ {
+ if (RECT_BOUNDS_INVALID(layerBounds)) {
+ ALOGE("layer %s bounds are invalid (%" PRIi32 ", %" PRIi32 ", %" PRIi32 ", %" PRIi32
+ ")",
+ mName.c_str(), layerBounds.left, layerBounds.top, layerBounds.right,
+ layerBounds.bottom);
+ std::string out;
+ getTransform().dump(out, "Transform");
+ ALOGE("%s", out.c_str());
+ layerBounds.left = layerBounds.top = layerBounds.right = layerBounds.bottom = 0;
+ }
+
+ if (INT32_MIGHT_OVERFLOW(xSurfaceInset) || INT32_MIGHT_OVERFLOW(ySurfaceInset)) {
+ ALOGE("layer %s surface inset are invalid (%" PRIi32 ", %" PRIi32 ")", mName.c_str(),
+ int32_t(xSurfaceInset), int32_t(ySurfaceInset));
+ xSurfaceInset = ySurfaceInset = 0;
+ }
+ }
layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset);
// Input coordinate should match the layer bounds.
@@ -2102,7 +2129,7 @@
}
bool Layer::canReceiveInput() const {
- return isVisible();
+ return !isHiddenByPolicy();
}
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8a80e15..b46eb11 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_LAYER_H
#define ANDROID_LAYER_H
-#include <sys/types.h>
-
#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
@@ -28,6 +26,7 @@
#include <math/vec4.h>
#include <renderengine/Mesh.h>
#include <renderengine/Texture.h>
+#include <sys/types.h>
#include <ui/FloatRect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
@@ -44,16 +43,16 @@
#include <vector>
#include "Client.h"
+#include "ClientCache.h"
+#include "DisplayHardware/ComposerHal.h"
+#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
+#include "RenderArea.h"
#include "SurfaceFlinger.h"
#include "TransactionCompletedThread.h"
-#include "DisplayHardware/ComposerHal.h"
-#include "DisplayHardware/HWComposer.h"
-#include "RenderArea.h"
-
using namespace android::surfaceflinger;
namespace android {
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 8e7386d..80da154 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -44,11 +44,14 @@
enum class samplingStep {
noWorkNeeded,
idleTimerWaiting,
+ waitForQuietFrame,
waitForZeroPhase,
waitForSamplePhase,
sample
};
+constexpr auto timeForRegionSampling = 5000000ns;
+constexpr auto maxRegionSamplingSkips = 10;
constexpr auto defaultRegionSamplingOffset = -3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
@@ -216,9 +219,9 @@
void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mThreadControlMutex);
- if (mDiscardedFrames) {
+ if (mDiscardedFrames > 0) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
- mDiscardedFrames = false;
+ mDiscardedFrames = 0;
mPhaseCallback->startVsyncListener();
}
}
@@ -236,13 +239,25 @@
auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
if (lastSampleTime + mTunables.mSamplingPeriod > now) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
- mDiscardedFrames = true;
+ if (mDiscardedFrames == 0) mDiscardedFrames++;
return;
}
+ if (mDiscardedFrames < maxRegionSamplingSkips) {
+ // If there is relatively little time left for surfaceflinger
+ // until the next vsync deadline, defer this sampling work
+ // to a later frame, when hopefully there will be more time.
+ DisplayStatInfo stats;
+ mScheduler.getDisplayStatInfo(&stats);
+ if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
+ mDiscardedFrames++;
+ return;
+ }
+ }
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
- mDiscardedFrames = false;
+ mDiscardedFrames = 0;
lastSampleTime = now;
mIdleTimer.reset();
@@ -259,7 +274,7 @@
namespace {
// Using Rec. 709 primaries
-float getLuma(float r, float g, float b) {
+inline float getLuma(float r, float g, float b) {
constexpr auto rec709_red_primary = 0.2126f;
constexpr auto rec709_green_primary = 0.7152f;
constexpr auto rec709_blue_primary = 0.0722f;
@@ -294,10 +309,10 @@
const uint32_t* rowBase = data + row * stride;
for (int32_t column = area.left; column < area.right; ++column) {
uint32_t pixel = rowBase[column];
- const float r = (pixel & 0xFF) / 255.0f;
- const float g = ((pixel >> 8) & 0xFF) / 255.0f;
- const float b = ((pixel >> 16) & 0xFF) / 255.0f;
- const uint8_t luma = std::round(getLuma(r, g, b) * 255.0f);
+ const float r = pixel & 0xFF;
+ const float g = (pixel >> 8) & 0xFF;
+ const float b = (pixel >> 16) & 0xFF;
+ const uint8_t luma = std::round(getLuma(r, g, b));
++brightnessBuckets[luma];
if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f;
}
@@ -343,9 +358,19 @@
}
const auto device = mFlinger.getDefaultDisplayDevice();
- const auto display = device->getCompositionDisplay();
- const auto state = display->getState();
- const auto orientation = static_cast<ui::Transform::orientation_flags>(state.orientation);
+ const auto orientation = [](uint32_t orientation) {
+ switch (orientation) {
+ default:
+ case DisplayState::eOrientationDefault:
+ return ui::Transform::ROT_0;
+ case DisplayState::eOrientation90:
+ return ui::Transform::ROT_90;
+ case DisplayState::eOrientation180:
+ return ui::Transform::ROT_180;
+ case DisplayState::eOrientation270:
+ return ui::Transform::ROT_270;
+ }
+ }(device->getOrientation());
std::vector<RegionSamplingThread::Descriptor> descriptors;
Region sampleRegion;
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 3c6fcf3..96ffe20 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -117,7 +117,7 @@
std::condition_variable_any mCondition;
bool mRunning GUARDED_BY(mThreadControlMutex) = true;
bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
- bool mDiscardedFrames GUARDED_BY(mThreadControlMutex) = false;
+ uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
std::mutex mSamplingMutex;
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index cd6fa41..0c94052 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -79,11 +79,7 @@
Mutex::Autolock lock(mMutex);
mPhase = phase;
- if (mReferenceTime != referenceTime) {
- for (auto& eventListener : mEventListeners) {
- eventListener.mHasFired = false;
- }
- }
+ const bool referenceTimeChanged = mReferenceTime != referenceTime;
mReferenceTime = referenceTime;
if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
// Inflate the reference time to be the most recent predicted
@@ -94,6 +90,16 @@
mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
}
mPeriod = period;
+ if (!mModelLocked && referenceTimeChanged) {
+ for (auto& eventListener : mEventListeners) {
+ eventListener.mLastEventTime = mReferenceTime + mPhase + eventListener.mPhase;
+ // If mLastEventTime is after mReferenceTime (can happen when positive phase offsets
+ // are used) we treat it as like it happened in previous period.
+ if (eventListener.mLastEventTime > mReferenceTime) {
+ eventListener.mLastEventTime -= mPeriod;
+ }
+ }
+ }
if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
@@ -115,11 +121,13 @@
void lockModel() {
Mutex::Autolock lock(mMutex);
mModelLocked = true;
+ ATRACE_INT("DispSync:ModelLocked", mModelLocked);
}
void unlockModel() {
Mutex::Autolock lock(mMutex);
mModelLocked = false;
+ ATRACE_INT("DispSync:ModelLocked", mModelLocked);
}
virtual bool threadLoop() {
@@ -284,12 +292,8 @@
// new offset to allow for a seamless offset change without double-firing or
// skipping.
nsecs_t diff = oldPhase - phase;
- if (diff > mPeriod / 2) {
- diff -= mPeriod;
- } else if (diff < -mPeriod / 2) {
- diff += mPeriod;
- }
eventListener.mLastEventTime -= diff;
+ eventListener.mLastCallbackTime -= diff;
mCond.signal();
return NO_ERROR;
}
@@ -304,7 +308,6 @@
nsecs_t mLastEventTime;
nsecs_t mLastCallbackTime;
DispSync::Callback* mCallback;
- bool mHasFired = false;
};
struct CallbackInvocation {
@@ -352,12 +355,7 @@
eventListener.mName);
continue;
}
- if (eventListener.mHasFired && !mModelLocked) {
- eventListener.mLastEventTime = t;
- ALOGV("[%s] [%s] Skipping event due to already firing", mName,
- eventListener.mName);
- continue;
- }
+
CallbackInvocation ci;
ci.mCallback = eventListener.mCallback;
ci.mEventTime = t;
@@ -366,7 +364,6 @@
callbackInvocations.push_back(ci);
eventListener.mLastEventTime = t;
eventListener.mLastCallbackTime = now;
- eventListener.mHasFired = true;
}
}
@@ -493,7 +490,6 @@
ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
}
- reset();
beginResync();
if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
@@ -545,17 +541,15 @@
void DispSync::beginResync() {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] beginResync", mName);
- mThread->unlockModel();
- mModelUpdated = false;
- mNumResyncSamples = 0;
+ resetLocked();
}
-bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) {
+bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
- *periodChanged = false;
+ *periodFlushed = false;
const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
mResyncSamples[idx] = timestamp;
if (mNumResyncSamples == 0) {
@@ -569,16 +563,20 @@
const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
- if (std::abs(observedVsync - mPendingPeriod) < std::abs(observedVsync - mPeriod)) {
- // Observed vsync is closer to the pending period, so reset the
- // model and flush the pending period.
+ if (std::abs(observedVsync - mPendingPeriod) <= std::abs(observedVsync - mIntendedPeriod)) {
+ // Either the observed vsync is closer to the pending period, (and
+ // thus we detected a period change), or the period change will
+ // no-op. In either case, reset the model and flush the pending
+ // period.
resetLocked();
+ mIntendedPeriod = mPendingPeriod;
mPeriod = mPendingPeriod;
mPendingPeriod = 0;
if (mTraceDetailedInfo) {
ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
+ ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
}
- *periodChanged = true;
+ *periodFlushed = true;
}
}
// Always update the reference time with the most recent timestamp.
@@ -609,6 +607,7 @@
bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
if (modelLocked) {
+ *periodFlushed = true;
mThread->lockModel();
}
return !modelLocked;
@@ -643,10 +642,17 @@
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
- if (mTraceDetailedInfo) {
- ATRACE_INT("DispSync:PendingPeriod", period);
+
+ const bool pendingPeriodShouldChange =
+ period != mIntendedPeriod || (period == mIntendedPeriod && mPendingPeriod != 0);
+
+ if (pendingPeriodShouldChange) {
+ mPendingPeriod = period;
}
- mPendingPeriod = period;
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
+ ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
+ }
}
nsecs_t DispSync::getPeriod() {
@@ -662,7 +668,13 @@
nsecs_t durationSum = 0;
nsecs_t minDuration = INT64_MAX;
nsecs_t maxDuration = 0;
- for (size_t i = 1; i < mNumResyncSamples; i++) {
+ // We skip the first 2 samples because the first vsync duration on some
+ // devices may be much more inaccurate than on other devices, e.g. due
+ // to delays in ramping up from a power collapse. By doing so this
+ // actually increases the accuracy of the DispSync model even though
+ // we're effectively relying on fewer sample points.
+ static constexpr size_t numSamplesSkipped = 2;
+ for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
@@ -673,15 +685,14 @@
// Exclude the min and max from the average
durationSum -= minDuration + maxDuration;
- mPeriod = durationSum / (mNumResyncSamples - 3);
+ mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2);
ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
double sampleAvgX = 0;
double sampleAvgY = 0;
double scale = 2.0 * M_PI / double(mPeriod);
- // Intentionally skip the first sample
- for (size_t i = 1; i < mNumResyncSamples; i++) {
+ for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
double samplePhase = double(sample % mPeriod) * scale;
@@ -689,8 +700,8 @@
sampleAvgY += sin(samplePhase);
}
- sampleAvgX /= double(mNumResyncSamples - 1);
- sampleAvgY /= double(mNumResyncSamples - 1);
+ sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped);
+ sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped);
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
@@ -764,6 +775,9 @@
mPresentSampleOffset = 0;
mError = 0;
mZeroErrSamplesCount = 0;
+ if (mTraceDetailedInfo) {
+ ATRACE_INT64("DispSync:Error", mError);
+ }
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
mPresentFences[i] = FenceTime::NO_FENCE;
}
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 8f8b8e7..3e33c7e 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -49,7 +49,7 @@
virtual void reset() = 0;
virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
virtual void beginResync() = 0;
- virtual bool addResyncSample(nsecs_t timestamp, bool* periodChanged) = 0;
+ virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0;
virtual void endResync() = 0;
virtual void setPeriod(nsecs_t period) = 0;
virtual nsecs_t getPeriod() = 0;
@@ -120,17 +120,19 @@
// from the hardware vsync events.
void beginResync() override;
// Adds a vsync sample to the dispsync model. The timestamp is the time
- // of the vsync event that fired. periodChanged will return true if the
+ // of the vsync event that fired. periodFlushed will return true if the
// vsync period was detected to have changed to mPendingPeriod.
//
// This method will return true if more vsync samples are needed to lock
// down the DispSync model, and false otherwise.
- bool addResyncSample(nsecs_t timestamp, bool* periodChanged) override;
+ // periodFlushed will be set to true if mPendingPeriod is flushed to
+ // mIntendedPeriod, and false otherwise.
+ bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override;
void endResync() override;
// The setPeriod method sets the vsync event model's period to a specific
- // value. This should be used to prime the model when a display is first
- // turned on. It should NOT be used after that.
+ // value. This should be used to prime the model when a display is first
+ // turned on, or when a refresh rate change is requested.
void setPeriod(nsecs_t period) override;
// The getPeriod method returns the current vsync period.
@@ -205,6 +207,11 @@
// nanoseconds.
nsecs_t mPeriod;
+ // mIntendedPeriod is the intended period of the modeled vsync events in
+ // nanoseconds. Under ideal conditions this should be similar if not the
+ // same as mPeriod, plus or minus an observed error.
+ nsecs_t mIntendedPeriod = 0;
+
// mPendingPeriod is the proposed period change in nanoseconds.
// If mPendingPeriod differs from mPeriod and is nonzero, it will
// be flushed to mPeriod when we detect that the hardware switched
@@ -236,8 +243,8 @@
// process to store information about the hardware vsync event times used
// to compute the model.
nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
- size_t mFirstResyncSample;
- size_t mNumResyncSamples;
+ size_t mFirstResyncSample = 0;
+ size_t mNumResyncSamples = 0;
int mNumResyncSamplesSincePresent;
// These member variables store information about the present fences used
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 00948ae..5faf46e 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -27,18 +27,23 @@
namespace android {
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset,
+ nsecs_t offsetThresholdForNextVsync, bool traceVsync,
const char* name)
: mName(name),
mTraceVsync(traceVsync),
mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
+ mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)),
+ mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)),
mDispSync(dispSync),
- mPhaseOffset(phaseOffset) {}
+ mPhaseOffset(phaseOffset),
+ mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
if (enable) {
+ tracePhaseOffset();
status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
static_cast<DispSync::Callback*>(this),
mLastCallbackTime);
@@ -64,16 +69,21 @@
void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
std::lock_guard lock(mVsyncMutex);
-
- // Normalize phaseOffset to [0, period)
- auto period = mDispSync->getPeriod();
- phaseOffset %= period;
- if (phaseOffset < 0) {
- // If we're here, then phaseOffset is in (-period, 0). After this
- // operation, it will be in (0, period)
- phaseOffset += period;
+ const nsecs_t period = mDispSync->getPeriod();
+ // Check if offset should be handled as negative
+ if (phaseOffset >= mOffsetThresholdForNextVsync) {
+ phaseOffset -= period;
}
+
+ // Normalize phaseOffset to [-period, period)
+ const int numPeriods = phaseOffset / period;
+ phaseOffset -= numPeriods * period;
+ if (mPhaseOffset == phaseOffset) {
+ return;
+ }
+
mPhaseOffset = phaseOffset;
+ tracePhaseOffset();
// If we're not enabled, we don't need to mess with the listeners
if (!mEnabled) {
@@ -92,11 +102,11 @@
{
std::lock_guard lock(mCallbackMutex);
callback = mCallback;
+ }
- if (mTraceVsync) {
- mValue = (mValue + 1) % 2;
- ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
- }
+ if (mTraceVsync) {
+ mValue = (mValue + 1) % 2;
+ ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
}
if (callback != nullptr) {
@@ -104,4 +114,14 @@
}
}
-} // namespace android
\ No newline at end of file
+void DispSyncSource::tracePhaseOffset() {
+ if (mPhaseOffset > 0) {
+ ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset);
+ ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0);
+ } else {
+ ATRACE_INT(mVsyncOffsetLabel.c_str(), 0);
+ ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 4759699..50560a5 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -25,7 +25,8 @@
class DispSyncSource final : public VSyncSource, private DispSync::Callback {
public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+ DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync,
+ bool traceVsync, const char* name);
~DispSyncSource() override = default;
@@ -38,12 +39,16 @@
// The following method is the implementation of the DispSync::Callback.
virtual void onDispSyncEvent(nsecs_t when);
+ void tracePhaseOffset() REQUIRES(mVsyncMutex);
+
const char* const mName;
int mValue = 0;
const bool mTraceVsync;
const std::string mVsyncOnLabel;
const std::string mVsyncEventLabel;
+ const std::string mVsyncOffsetLabel;
+ const std::string mVsyncNegativeOffsetLabel;
nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
DispSync* mDispSync;
@@ -53,7 +58,8 @@
std::mutex mVsyncMutex;
nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
+ const nsecs_t mOffsetThresholdForNextVsync;
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 05bad4d..9d1f777 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -76,6 +76,10 @@
return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
", count=%u}",
event.header.displayId, event.vsync.count);
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", configId=%u}",
+ event.header.displayId, event.config.configId);
default:
return "Event{}";
}
@@ -107,8 +111,10 @@
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
- ResyncCallback resyncCallback)
+ ResyncCallback resyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged)
: resyncCallback(std::move(resyncCallback)),
+ configChanged(configChanged),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -203,8 +209,10 @@
mVSyncSource->setPhaseOffset(phaseOffset);
}
-sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
+sp<EventThreadConnection> EventThread::createEventConnection(
+ ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
+ return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
+ configChanged);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -398,9 +406,11 @@
const sp<EventThreadConnection>& connection) const {
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
return true;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ return connection->configChanged == ISurfaceComposer::eConfigChangedDispatch;
+
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
switch (connection->vsyncRequest) {
case VSyncRequest::None:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 61530c6..dd23b88 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -69,7 +69,8 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, ResyncCallback);
+ EventThreadConnection(EventThread*, ResyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged);
virtual ~EventThreadConnection();
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
@@ -82,6 +83,7 @@
const ResyncCallback resyncCallback;
VSyncRequest vsyncRequest = VSyncRequest::None;
+ const ISurfaceComposer::ConfigChanged configChanged;
private:
virtual void onFirstRef();
@@ -93,7 +95,8 @@
public:
virtual ~EventThread();
- virtual sp<EventThreadConnection> createEventConnection(ResyncCallback) const = 0;
+ virtual sp<EventThreadConnection> createEventConnection(
+ ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const = 0;
// called before the screen is turned off from main thread
virtual void onScreenReleased() = 0;
@@ -128,7 +131,8 @@
EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
~EventThread();
- sp<EventThreadConnection> createEventConnection(ResyncCallback) const override;
+ sp<EventThreadConnection> createEventConnection(
+ ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 1db43a3..f80c233 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -46,11 +46,13 @@
LayerHistory::~LayerHistory() = default;
std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
+ float minRefreshRate,
float maxRefreshRate) {
const int64_t id = sNextId++;
std::lock_guard lock(mLock);
- mInactiveLayerInfos.emplace(id, std::make_shared<LayerInfo>(name, maxRefreshRate));
+ mInactiveLayerInfos.emplace(id,
+ std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate));
return std::make_unique<LayerHistory::LayerHandle>(*this, id);
}
@@ -173,5 +175,18 @@
}
}
+void LayerHistory::clearHistory() {
+ std::lock_guard lock(mLock);
+
+ auto it = mActiveLayerInfos.begin();
+ while (it != mActiveLayerInfos.end()) {
+ auto id = it->first;
+ auto layerInfo = it->second;
+ layerInfo->clearHistory();
+ mInactiveLayerInfos.insert({id, layerInfo});
+ it = mActiveLayerInfos.erase(it);
+ }
+}
+
} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index adc5ce5..5598cc1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -53,7 +53,8 @@
~LayerHistory();
// When the layer is first created, register it.
- std::unique_ptr<LayerHandle> createLayer(const std::string name, float maxRefreshRate);
+ std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate,
+ float maxRefreshRate);
// Method for inserting layers and their requested present time into the unordered map.
void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
@@ -64,6 +65,9 @@
// layers. See go/content-fps-detection-in-scheduler for more information.
std::pair<float, bool> getDesiredRefreshRateAndHDR();
+ // Clears all layer history.
+ void clearHistory();
+
// Removes the handle and the object from the map.
void destroyLayer(const int64_t id);
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 95d7d31..723d71f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -24,9 +24,10 @@
namespace android {
namespace scheduler {
-LayerInfo::LayerInfo(const std::string name, float maxRefreshRate)
+LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate)
: mName(name),
mMinRefreshDuration(1e9f / maxRefreshRate),
+ mLowActivityRefreshDuration(1e9f / minRefreshRate),
mRefreshRateHistory(mMinRefreshDuration) {}
LayerInfo::~LayerInfo() = default;
@@ -38,12 +39,19 @@
mLastUpdatedTime = std::max(lastPresentTime, systemTime());
mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+ if (mLastPresentTime == 0) {
+ // First frame
+ mLastPresentTime = lastPresentTime;
+ return;
+ }
+
const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
mLastPresentTime = lastPresentTime;
// Ignore time diff that are too high - those are stale values
- if (timeDiff > TIME_EPSILON_NS.count()) return;
- const nsecs_t refreshDuration = (timeDiff > 0) ? timeDiff : mMinRefreshDuration;
- mRefreshRateHistory.insertRefreshRate(refreshDuration);
+ if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
+ const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration);
+ const int fps = 1e9f / refreshDuration;
+ mRefreshRateHistory.insertRefreshRate(fps);
}
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 02b6aef..17afdda 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -46,7 +46,7 @@
public:
explicit RefreshRateHistory(nsecs_t minRefreshDuration)
: mMinRefreshDuration(minRefreshDuration) {}
- void insertRefreshRate(nsecs_t refreshRate) {
+ void insertRefreshRate(int refreshRate) {
mElements.push_back(refreshRate);
if (mElements.size() > HISTORY_SIZE) {
mElements.pop_front();
@@ -54,13 +54,13 @@
}
float getRefreshRateAvg() const {
- nsecs_t refreshDuration = mMinRefreshDuration;
- if (mElements.size() == HISTORY_SIZE) {
- refreshDuration = scheduler::calculate_mean(mElements);
+ if (mElements.empty()) {
+ return 1e9f / mMinRefreshDuration;
}
- return 1e9f / refreshDuration;
+ return scheduler::calculate_mean(mElements);
}
+
void clearHistory() { mElements.clear(); }
private:
@@ -86,13 +86,43 @@
// Checks whether the present time that was inserted HISTORY_SIZE ago is within a
// certain threshold: TIME_EPSILON_NS.
bool isRelevant() const {
- const int64_t obsoleteEpsilon = systemTime() - scheduler::TIME_EPSILON_NS.count();
- // The layer had to publish at least HISTORY_SIZE of updates, and the first
- // update should not be older than TIME_EPSILON_NS nanoseconds.
- if (mElements.size() == HISTORY_SIZE &&
- mElements.at(HISTORY_SIZE - 1) > obsoleteEpsilon) {
+ if (mElements.size() < 2) {
+ return false;
+ }
+
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ if (mElements.size() != HISTORY_SIZE &&
+ mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) {
+ return false;
+ }
+
+ // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
+ const int64_t obsoleteEpsilon =
+ systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
+ if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isLowActivityLayer() const {
+ // We want to make sure that we received more than two frames from the layer
+ // in order to check low activity.
+ if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
+ return false;
+ }
+
+ const int64_t obsoleteEpsilon =
+ systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
+ // Check the frame before last to determine whether there is low activity.
+ // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
+ // infrequent updates.
+ if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
+ obsoleteEpsilon) {
return true;
}
+
return false;
}
@@ -100,11 +130,12 @@
private:
std::deque<nsecs_t> mElements;
- static constexpr size_t HISTORY_SIZE = 10;
+ static constexpr size_t HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
};
public:
- LayerInfo(const std::string name, float maxRefreshRate);
+ LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
~LayerInfo();
LayerInfo(const LayerInfo&) = delete;
@@ -134,6 +165,10 @@
// Calculate the average refresh rate.
float getDesiredRefreshRate() const {
std::lock_guard lock(mLock);
+
+ if (mPresentTimeHistory.isLowActivityLayer()) {
+ return 1e9f / mLowActivityRefreshDuration;
+ }
return mRefreshRateHistory.getRefreshRateAvg();
}
@@ -165,6 +200,7 @@
private:
const std::string mName;
const nsecs_t mMinRefreshDuration;
+ const nsecs_t mLowActivityRefreshDuration;
mutable std::mutex mLock;
nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index baf900d..fcb307f 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -96,7 +96,8 @@
}
mEventThread = eventThread;
- mEvents = eventThread->createEventConnection(std::move(resyncCallback));
+ mEvents = eventThread->createEventConnection(std::move(resyncCallback),
+ ISurfaceComposer::eConfigChangedSuppress);
mEvents->stealReceiveChannel(&mEventTube);
mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
this);
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 276bce1..9fa2bbc 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -25,6 +25,7 @@
namespace scheduler {
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
PhaseOffsets::~PhaseOffsets() = default;
namespace impl {
@@ -72,25 +73,31 @@
property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1");
const int phaseOffsetThresholdForNextVsyncNs = atoi(value);
- mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
- : sfVsyncPhaseOffsetNs,
- earlyAppOffsetNs != -1 ? earlyAppOffsetNs
- : vsyncPhaseOffsetNs};
- mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs
- : sfVsyncPhaseOffsetNs,
- earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs
- : vsyncPhaseOffsetNs};
- mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
+ Offsets defaultOffsets;
+ Offsets highFpsOffsets;
+ defaultOffsets.early = {RefreshRateType::DEFAULT,
+ earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
+ earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
+ defaultOffsets.earlyGl = {RefreshRateType::DEFAULT,
+ earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
+ earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
+ defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
- mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
- : highFpsLateSfOffsetNs,
- highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
- : highFpsLateAppOffsetNs};
- mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
- : highFpsLateSfOffsetNs,
- highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
- : highFpsLateAppOffsetNs};
- mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
+ highFpsOffsets.early = {RefreshRateType::PERFORMANCE,
+ highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
+ : highFpsLateSfOffsetNs,
+ highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
+ : highFpsLateAppOffsetNs};
+ highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE,
+ highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
+ : highFpsLateSfOffsetNs,
+ highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
+ : highFpsLateAppOffsetNs};
+ highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
+ highFpsLateAppOffsetNs};
+
+ mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
+ mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1
? phaseOffsetThresholdForNextVsyncNs
@@ -99,12 +106,7 @@
PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
- switch (refreshRateType) {
- case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
- return mHighRefreshRateOffsets;
- default:
- return mDefaultRefreshRateOffsets;
- }
+ return mOffsets.at(refreshRateType);
}
void PhaseOffsets::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index dc71e6e..2b5c2f1 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -17,6 +17,7 @@
#pragma once
#include <cinttypes>
+#include <unordered_map>
#include "RefreshRateConfigs.h"
#include "VSyncModulator.h"
@@ -79,14 +80,10 @@
void dump(std::string& result) const override;
private:
- Offsets getDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
- Offsets getHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
-
std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
RefreshRateConfigs::RefreshRateType::DEFAULT;
- Offsets mDefaultRefreshRateOffsets;
- Offsets mHighRefreshRateOffsets;
+ std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets;
nsecs_t mOffsetThresholdForNextVsync;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index d730058..d813708 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -34,10 +34,9 @@
*/
class RefreshRateConfigs {
public:
- // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
- // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+ // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
// is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
- enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+ enum class RefreshRateType { DEFAULT, PERFORMANCE };
struct RefreshRate {
// This config ID corresponds to the position of the config in the vector that is stored
@@ -47,26 +46,57 @@
std::string name;
// Refresh rate in frames per second, rounded to the nearest integer.
uint32_t fps = 0;
- // config Id (returned from HWC2::Display::Config::getId())
- hwc2_config_t id;
+ // Vsync period in nanoseconds.
+ nsecs_t vsyncPeriod;
+ // Hwc config Id (returned from HWC2::Display::Config::getId())
+ hwc2_config_t hwcId;
};
+ // Returns true if this device is doing refresh rate switching. This won't change at runtime.
+ bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+
+ // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
+ // from multiple threads. This can only be called if refreshRateSwitching() returns true.
// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
// baking them in.
- const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
- return mRefreshRates;
- }
- std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
- const auto& refreshRate = mRefreshRates.find(type);
- if (refreshRate != mRefreshRates.end()) {
- return refreshRate->second;
- }
- return nullptr;
+ const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
+ LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
+ return mRefreshRateMap;
}
- RefreshRateType getRefreshRateType(hwc2_config_t id) const {
- for (const auto& [type, refreshRate] : mRefreshRates) {
- if (refreshRate->id == id) {
+ const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
+ if (!mRefreshRateSwitchingSupported) {
+ return getCurrentRefreshRate().second;
+ } else {
+ auto refreshRate = mRefreshRateMap.find(type);
+ LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
+ return refreshRate->second;
+ }
+ }
+
+ std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
+ int currentConfig = mCurrentConfig;
+ if (mRefreshRateSwitchingSupported) {
+ for (const auto& [type, refresh] : mRefreshRateMap) {
+ if (refresh.configId == currentConfig) {
+ return {type, refresh};
+ }
+ }
+ LOG_ALWAYS_FATAL();
+ }
+ return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
+ }
+
+ const RefreshRate& getRefreshRateFromConfigId(int configId) const {
+ LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
+ return mRefreshRates[configId];
+ }
+
+ RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
+ if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+
+ for (const auto& [type, refreshRate] : mRefreshRateMap) {
+ if (refreshRate.hwcId == hwcId) {
return type;
}
}
@@ -74,64 +104,102 @@
return RefreshRateType::DEFAULT;
}
- void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
- mRefreshRates.clear();
+ void setCurrentConfig(int config) {
+ LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
+ mCurrentConfig = config;
+ }
- // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
- mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
- std::make_shared<RefreshRate>(
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
- HWC2_SCREEN_OFF_CONFIG_ID}));
+ struct InputConfig {
+ hwc2_config_t hwcId = 0;
+ nsecs_t vsyncPeriod = 0;
+ };
- if (configs.size() < 1) {
- ALOGE("Device does not have valid configs. Config size is 0.");
- return;
+ RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+ int currentConfig) {
+ init(refreshRateSwitching, configs, currentConfig);
+ }
+
+ RefreshRateConfigs(bool refreshRateSwitching,
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ int currentConfig) {
+ std::vector<InputConfig> inputConfigs;
+ for (const auto& config : configs) {
+ inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
}
-
- // Create a map between config index and vsync period. This is all the info we need
- // from the configs.
- std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
- for (int i = 0; i < configs.size(); ++i) {
- configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
- }
-
- std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
- [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
- return a.second > b.second;
- });
-
- // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
- nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
- if (vsyncPeriod != 0) {
- const float fps = 1e9 / vsyncPeriod;
- const int configId = configIdToVsyncPeriod[0].first;
- mRefreshRates.emplace(RefreshRateType::DEFAULT,
- std::make_shared<RefreshRate>(
- RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps),
- configs.at(configId)->getId()}));
- }
-
- if (configs.size() < 2) {
- return;
- }
-
- // When the configs are ordered by the resync rate. We assume that the second one is
- // PERFORMANCE, eg. the higher rate.
- vsyncPeriod = configIdToVsyncPeriod[1].second;
- if (vsyncPeriod != 0) {
- const float fps = 1e9 / vsyncPeriod;
- const int configId = configIdToVsyncPeriod[1].first;
- mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
- std::make_shared<RefreshRate>(
- RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps),
- configs.at(configId)->getId()}));
- }
+ init(refreshRateSwitching, inputConfigs, currentConfig);
}
private:
- std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
+ void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+ int currentConfig) {
+ mRefreshRateSwitchingSupported = refreshRateSwitching;
+ LOG_ALWAYS_FATAL_IF(configs.empty());
+ LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
+ mCurrentConfig = currentConfig;
+
+ auto buildRefreshRate = [&](int configId) -> RefreshRate {
+ const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
+ const float fps = 1e9 / vsyncPeriod;
+ return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
+ vsyncPeriod, configs[configId].hwcId};
+ };
+
+ for (int i = 0; i < configs.size(); ++i) {
+ mRefreshRates.push_back(buildRefreshRate(i));
+ }
+
+ if (!mRefreshRateSwitchingSupported) return;
+
+ auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
+ if (configs.size() < 2) {
+ return {};
+ }
+
+ std::vector<const RefreshRate*> sortedRefreshRates;
+ for (const auto& refreshRate : mRefreshRates) {
+ sortedRefreshRates.push_back(&refreshRate);
+ }
+ std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
+ [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+
+ // When the configs are ordered by the resync rate, we assume that
+ // the first one is DEFAULT and the second one is PERFORMANCE,
+ // i.e. the higher rate.
+ if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
+ sortedRefreshRates[1]->vsyncPeriod == 0) {
+ return {};
+ }
+
+ return std::pair<int, int>(sortedRefreshRates[0]->configId,
+ sortedRefreshRates[1]->configId);
+ };
+
+ auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
+ if (!defaultAndPerfConfigs) {
+ mRefreshRateSwitchingSupported = false;
+ return;
+ }
+
+ mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
+ mRefreshRateMap[RefreshRateType::PERFORMANCE] =
+ mRefreshRates[defaultAndPerfConfigs->second];
+ }
+
+ // Whether this device is doing refresh rate switching or not. This must not change after this
+ // object is initialized.
+ bool mRefreshRateSwitchingSupported;
+ // The list of refresh rates, indexed by display config ID. This must not change after this
+ // object is initialized.
+ std::vector<RefreshRate> mRefreshRates;
+ // The mapping of refresh rate type to RefreshRate. This must not change after this object is
+ // initialized.
+ std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
+ // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on
+ // the main thread, and read by the Scheduler (and other objects) on other threads, so it's
+ // atomic.
+ std::atomic<int> mCurrentConfig;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 7e7c630..947eb08 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -41,21 +41,18 @@
static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
public:
- RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
- : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}
+ RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
+ int currentConfigMode, int currentPowerMode)
+ : mRefreshRateConfigs(refreshRateConfigs),
+ mTimeStats(timeStats),
+ mCurrentConfigMode(currentConfigMode),
+ mCurrentPowerMode(currentPowerMode) {}
- // Sets power mode. We only collect the information when the power mode is not
- // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
- // on config mode.
+ // Sets power mode.
void setPowerMode(int mode) {
if (mCurrentPowerMode == mode) {
return;
}
- // If power mode is normal, the time is going to be recorded under config modes.
- if (mode == HWC_POWER_MODE_NORMAL) {
- mCurrentPowerMode = mode;
- return;
- }
flushTime();
mCurrentPowerMode = mode;
}
@@ -79,16 +76,15 @@
flushTime();
std::unordered_map<std::string, int64_t> totalTime;
- for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
- int64_t totalTimeForConfig = 0;
- if (!config) {
- continue;
- }
- if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
- totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
- }
- totalTime[config->name] = totalTimeForConfig;
+ // Multiple configs may map to the same name, e.g. "60fps". Add the
+ // times for such configs together.
+ for (const auto& [config, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
}
+ for (const auto& [config, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+ }
+ totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
}
@@ -104,32 +100,26 @@
}
private:
- void flushTime() {
- // Normal power mode is counted under different config modes.
- if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
- flushTimeForMode(mCurrentConfigMode);
- } else {
- flushTimeForMode(SCREEN_OFF_CONFIG_ID);
- }
- }
-
// Calculates the time that passed in ms between the last time we recorded time and the time
// this method was called.
- void flushTimeForMode(int mode) {
+ void flushTime() {
nsecs_t currentTime = systemTime();
nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
int64_t timeElapsedMs = ns2ms(timeElapsed);
mPreviousRecordedTime = currentTime;
- mConfigModesTotalTime[mode] += timeElapsedMs;
- for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
- if (!config) {
- continue;
+ uint32_t fps = 0;
+ if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+ // Normal power mode is counted under different config modes.
+ if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
+ mConfigModesTotalTime[mCurrentConfigMode] = 0;
}
- if (config->configId == mode) {
- mTimeStats.recordRefreshRate(config->fps, timeElapsed);
- }
+ mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
+ fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+ } else {
+ mScreenOffTime += timeElapsedMs;
}
+ mTimeStats.recordRefreshRate(fps, timeElapsed);
}
// Formats the time in milliseconds into easy to read format.
@@ -149,10 +139,11 @@
// Aggregate refresh rate statistics for telemetry.
TimeStats& mTimeStats;
- int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
- int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+ int mCurrentConfigMode;
+ int32_t mCurrentPowerMode;
- std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ int64_t mScreenOffTime = 0;
nsecs_t mPreviousRecordedTime = systemTime();
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 513436a..0b43378 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -76,6 +76,7 @@
mSupportKernelTimer = support_kernel_idle_timer(false);
mSetTouchTimerMs = set_touch_timer_ms(0);
+ mSetDisplayPowerTimerMs = set_display_power_timer_ms(0);
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.set_idle_timer_ms", value, "0");
@@ -110,26 +111,38 @@
[this] { expiredTouchTimerCallback(); });
mTouchTimer->start();
}
+
+ if (mSetDisplayPowerTimerMs > 0) {
+ mDisplayPowerTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetDisplayPowerTimerMs),
+ [this] { resetDisplayPowerTimerCallback(); },
+ [this] {
+ expiredDisplayPowerTimerCallback();
+ });
+ mDisplayPowerTimer->start();
+ }
}
Scheduler::~Scheduler() {
// Ensure the IdleTimer thread is joined before we start destroying state.
+ mDisplayPowerTimer.reset();
mTouchTimer.reset();
mIdleTimer.reset();
}
sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
- const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+ const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
const int64_t id = sNextId++;
ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
std::unique_ptr<EventThread> eventThread =
makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
- std::move(interceptCallback));
+ offsetThresholdForNextVsync, std::move(interceptCallback));
auto eventThreadConnection =
- createConnectionInternal(eventThread.get(), std::move(resyncCallback));
+ createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
mConnections.emplace(id,
std::make_unique<Connection>(new ConnectionHandle(id),
eventThreadConnection,
@@ -138,24 +151,26 @@
}
std::unique_ptr<EventThread> Scheduler::makeEventThread(
- const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
std::unique_ptr<VSyncSource> eventThreadSource =
- std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+ std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync,
+ true, connectionName);
return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
std::move(interceptCallback), connectionName);
}
-sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
- ResyncCallback&& resyncCallback) {
- return eventThread->createEventConnection(std::move(resyncCallback));
+sp<EventThreadConnection> Scheduler::createConnectionInternal(
+ EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
+ return eventThread->createEventConnection([&] { resync(); }, configChanged);
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
+ const sp<Scheduler::ConnectionHandle>& handle,
+ ISurfaceComposer::ConfigChanged configChanged) {
RETURN_VALUE_IF_INVALID(nullptr);
- return createConnectionInternal(mConnections[handle->id]->thread.get(),
- std::move(resyncCallback));
+ return createConnectionInternal(mConnections[handle->id]->thread.get(), configChanged);
}
EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -245,23 +260,15 @@
setVsyncPeriod(period);
}
-ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
- std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
- return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
- if (const auto vsync = ptr.lock()) {
- vsync->resync(getVsyncPeriod);
- }
- };
-}
-
-void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
- static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
+void Scheduler::resync() {
+ static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
const nsecs_t now = systemTime();
- const nsecs_t last = lastResyncTime.exchange(now);
+ const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
+ resyncToHardwareVsync(false,
+ mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
}
}
@@ -280,13 +287,13 @@
}
}
-void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
+void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) {
bool needsHwVsync = false;
- *periodChanged = false;
+ *periodFlushed = false;
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
+ needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
}
}
@@ -309,7 +316,7 @@
mPrimaryDispSync->setIgnorePresentFences(ignore);
}
-nsecs_t Scheduler::expectedPresentTime() {
+nsecs_t Scheduler::getDispSyncExpectedPresentTime() {
return mPrimaryDispSync->expectedPresentTime();
}
@@ -319,13 +326,20 @@
std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
std::string const& name, int windowType) {
- RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE;
-
- const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
- const uint32_t fps = (refreshRate) ? refreshRate->fps : 0;
- return mLayerHistory.createLayer(name, fps);
+ uint32_t defaultFps, performanceFps;
+ if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+ defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+ performanceFps =
+ mRefreshRateConfigs
+ .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE)
+ .fps;
+ } else {
+ defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
+ performanceFps = defaultFps;
+ }
+ return mLayerHistory.createLayer(name, defaultFps, performanceFps);
}
void Scheduler::addLayerPresentTimeAndHDR(
@@ -371,16 +385,11 @@
}
void Scheduler::setChangeRefreshRateCallback(
- const ChangeRefreshRateCallback& changeRefreshRateCallback) {
+ const ChangeRefreshRateCallback&& changeRefreshRateCallback) {
std::lock_guard<std::mutex> lock(mCallbackLock);
mChangeRefreshRateCallback = changeRefreshRateCallback;
}
-void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- mGetVsyncPeriod = getVsyncPeriod;
-}
-
void Scheduler::updateFrameSkipping(const int64_t skipCount) {
ATRACE_INT("FrameSkipCount", skipCount);
if (mSkipCount != skipCount) {
@@ -404,43 +413,77 @@
if (mSupportKernelTimer) {
resetIdleTimer();
}
+
+ // Touch event will boost the refresh rate to performance.
+ // Clear Layer History to get fresh FPS detection
+ mLayerHistory.clearHistory();
+}
+
+void Scheduler::setDisplayPowerState(bool normal) {
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mIsDisplayPowerStateNormal = normal;
+ }
+
+ if (mDisplayPowerTimer) {
+ mDisplayPowerTimer->reset();
+ }
+
+ // Display Power event will boost the refresh rate to performance.
+ // Clear Layer History to get fresh FPS detection
+ mLayerHistory.clearHistory();
}
void Scheduler::resetTimerCallback() {
- timerChangeRefreshRate(IdleTimerState::RESET);
+ handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
ATRACE_INT("ExpiredIdleTimer", 0);
}
void Scheduler::resetKernelTimerCallback() {
ATRACE_INT("ExpiredKernelIdleTimer", 0);
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mGetVsyncPeriod) {
- resyncToHardwareVsync(false, mGetVsyncPeriod());
+ const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+ if (refreshRate.first == RefreshRateType::PERFORMANCE) {
+ // If we're not in performance mode then the kernel timer shouldn't do
+ // anything, as the refresh rate during DPU power collapse will be the
+ // same.
+ resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod);
}
}
void Scheduler::expiredTimerCallback() {
- timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
ATRACE_INT("ExpiredIdleTimer", 1);
}
void Scheduler::resetTouchTimerCallback() {
- // We do not notify the applications about config changes when idle timer is reset.
- touchChangeRefreshRate(TouchState::ACTIVE);
+ handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
ATRACE_INT("TouchState", 1);
}
void Scheduler::expiredTouchTimerCallback() {
- // We do not notify the applications about config changes when idle timer expires.
- touchChangeRefreshRate(TouchState::INACTIVE);
+ handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
ATRACE_INT("TouchState", 0);
}
+void Scheduler::resetDisplayPowerTimerCallback() {
+ handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
+ ATRACE_INT("ExpiredDisplayPowerTimer", 0);
+}
+
+void Scheduler::expiredDisplayPowerTimerCallback() {
+ handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
+ ATRACE_INT("ExpiredDisplayPowerTimer", 1);
+}
+
void Scheduler::expiredKernelTimerCallback() {
ATRACE_INT("ExpiredKernelIdleTimer", 1);
- // Disable HW Vsync if the timer expired, as we don't need it
- // enabled if we're not pushing frames.
- disableHardwareVsync(false);
+ const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+ if (refreshRate.first != RefreshRateType::PERFORMANCE) {
+ // Disable HW Vsync if the timer expired, as we don't need it
+ // enabled if we're not pushing frames, and if we're in PERFORMANCE
+ // mode then we'll need to re-update the DispSync model anyways.
+ disableHardwareVsync(false);
+ }
}
std::string Scheduler::doDump() {
@@ -450,39 +493,23 @@
return stream.str();
}
-void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
- RefreshRateType newRefreshRateType;
- {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mCurrentIdleTimerState == idleTimerState) {
- return;
- }
- mCurrentIdleTimerState = idleTimerState;
- newRefreshRateType = calculateRefreshRateType();
- if (mRefreshRateType == newRefreshRateType) {
- return;
- }
- mRefreshRateType = newRefreshRateType;
- }
- changeRefreshRate(newRefreshRateType, ConfigEvent::None);
-}
-
-void Scheduler::touchChangeRefreshRate(TouchState touchState) {
+template <class T>
+void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None;
RefreshRateType newRefreshRateType;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mCurrentTouchState == touchState) {
+ if (*currentState == newState) {
return;
}
- mCurrentTouchState = touchState;
+ *currentState = newState;
newRefreshRateType = calculateRefreshRateType();
if (mRefreshRateType == newRefreshRateType) {
return;
}
mRefreshRateType = newRefreshRateType;
- // Send an event in case that content detection is on as touch has a higher priority
- if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+ if (eventOnContentDetection &&
+ mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
event = ConfigEvent::Changed;
}
}
@@ -490,11 +517,21 @@
}
Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+ if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+ return RefreshRateType::DEFAULT;
+ }
+
// HDR content is not supported on PERFORMANCE mode
if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
return RefreshRateType::DEFAULT;
}
+ // If Display Power is not in normal operation we want to be in performance mode.
+ // When coming back to normal mode, a grace period is given with DisplayPowerTimer
+ if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
+ return RefreshRateType::PERFORMANCE;
+ }
+
// As long as touch is active we want to be in performance mode
if (mCurrentTouchState == TouchState::ACTIVE) {
return RefreshRateType::PERFORMANCE;
@@ -510,27 +547,25 @@
return RefreshRateType::PERFORMANCE;
}
- // Content detection is on, find the appropriate refresh rate
- // Start with the smallest refresh rate which is within a margin of the content
- RefreshRateType currRefreshRateType = RefreshRateType::PERFORMANCE;
- constexpr float MARGIN = 0.05f;
- auto iter = mRefreshRateConfigs.getRefreshRates().cbegin();
- while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
- if (iter->second->fps >= mContentRefreshRate * (1 - MARGIN)) {
- currRefreshRateType = iter->first;
- break;
- }
- ++iter;
- }
+ // Content detection is on, find the appropriate refresh rate with minimal error
+ auto begin = mRefreshRateConfigs.getRefreshRateMap().cbegin();
+
+ auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRateMap().cend(),
+ [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
+ return std::abs(l.second.fps - static_cast<float>(rate)) <
+ std::abs(r.second.fps - static_cast<float>(rate));
+ });
+ RefreshRateType currRefreshRateType = iter->first;
// Some content aligns better on higher refresh rate. For example for 45fps we should choose
// 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
// align well with both
- float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
+ constexpr float MARGIN = 0.05f;
+ float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps /
float(mContentRefreshRate);
if (std::abs(std::round(ratio) - ratio) > MARGIN) {
- while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
- ratio = iter->second->fps / float(mContentRefreshRate);
+ while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
+ ratio = iter->second.fps / float(mContentRefreshRate);
if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
currRefreshRateType = iter->first;
@@ -543,6 +578,11 @@
return currRefreshRateType;
}
+Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return mRefreshRateType;
+}
+
void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
std::lock_guard<std::mutex> lock(mCallbackLock);
if (mChangeRefreshRateCallback) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 96d4bd5..4b21dad 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -50,7 +50,6 @@
using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
- using GetVsyncPeriod = std::function<nsecs_t()>;
// Enum to indicate whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
@@ -80,28 +79,18 @@
const std::unique_ptr<EventThread> thread;
};
- // Stores per-display state about VSYNC.
- struct VsyncState {
- explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {}
-
- void resync(const GetVsyncPeriod&);
-
- Scheduler& scheduler;
- std::atomic<nsecs_t> lastResyncTime = 0;
- };
-
explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig);
virtual ~Scheduler();
/** Creates an EventThread connection. */
- sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs,
- ResyncCallback,
+ sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback);
- sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
- ResyncCallback);
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ const sp<ConnectionHandle>& handle, ISurfaceComposer::ConfigChanged configChanged);
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -135,16 +124,20 @@
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
+ // Resyncs the scheduler to hardware vsync.
+ // If makeAvailable is true, then hardware vsync will be turned on.
+ // Otherwise, if hardware vsync is not already enabled then this method will
+ // no-op.
+ // The period is the vsync period from the current display configuration.
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
- // Creates a callback for resyncing.
- ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod);
+ void resync();
void setRefreshSkipCount(int count);
- // Passes a vsync sample to DispSync. periodChange will be true if DipSync
- // detected that the vsync period changed, and false otherwise.
- void addResyncSample(const nsecs_t timestamp, bool* periodChanged);
+ // Passes a vsync sample to DispSync. periodFlushed will be true if
+ // DispSync detected that the vsync period changed, and false otherwise.
+ void addResyncSample(const nsecs_t timestamp, bool* periodFlushed);
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
- nsecs_t expectedPresentTime();
+ nsecs_t getDispSyncExpectedPresentTime();
// Registers the layer in the scheduler, and returns the handle for future references.
std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
int windowType);
@@ -159,8 +152,7 @@
// Updates FPS based on the most content presented.
void updateFpsBasedOnContent();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
- void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
- void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
+ void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback);
// Returns whether idle timer is enabled or not
bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
@@ -171,15 +163,22 @@
// Function that resets the touch timer.
void notifyTouchEvent();
+ // Function that sets whether display power mode is normal or not.
+ void setDisplayPowerState(bool normal);
+
// Returns relevant information about Scheduler for dumpsys purposes.
std::string doDump();
// calls DispSync::dump() on primary disp sync
void dumpPrimaryDispSync(std::string& result) const;
+ // Get the appropriate refresh type for current conditions.
+ RefreshRateType getPreferredRefreshRateType();
+
protected:
virtual std::unique_ptr<EventThread> makeEventThread(
- const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback interceptCallback);
private:
@@ -190,9 +189,11 @@
enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
enum class IdleTimerState { EXPIRED, RESET };
enum class TouchState { INACTIVE, ACTIVE };
+ enum class DisplayPowerTimerState { EXPIRED, RESET };
// Creates a connection on the given EventThread and forwards the given callbacks.
- sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
+ sp<EventThreadConnection> createConnectionInternal(EventThread*,
+ ISurfaceComposer::ConfigChanged);
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
@@ -213,12 +214,15 @@
void resetTouchTimerCallback();
// Function that is called when the touch timer expires.
void expiredTouchTimerCallback();
+ // Function that is called when the display power timer resets.
+ void resetDisplayPowerTimerCallback();
+ // Function that is called when the display power timer expires.
+ void expiredDisplayPowerTimerCallback();
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
- // Idle timer feature's function to change the refresh rate.
- void timerChangeRefreshRate(IdleTimerState idleTimerState);
- // Touch timer feature's function to change the refresh rate.
- void touchChangeRefreshRate(TouchState touchState);
+ // handles various timer features to change the refresh rate.
+ template <class T>
+ void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
// Calculate the new refresh rate type
RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
@@ -243,7 +247,8 @@
std::mutex mHWVsyncLock;
bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock);
bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock);
- const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+
+ std::atomic<nsecs_t> mLastResyncTime = 0;
std::unique_ptr<DispSync> mPrimaryDispSync;
std::unique_ptr<EventControlThread> mEventControlThread;
@@ -274,9 +279,12 @@
int64_t mSetTouchTimerMs = 0;
std::unique_ptr<scheduler::IdleTimer> mTouchTimer;
+ // Timer used to monitor display power mode.
+ int64_t mSetDisplayPowerTimerMs = 0;
+ std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer;
+
std::mutex mCallbackLock;
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
- GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
@@ -285,14 +293,17 @@
ContentFeatureState::CONTENT_DETECTION_OFF;
IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
+ DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
+ DisplayPowerTimerState::EXPIRED;
uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
+ bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
// Global config to force HDR content to work on DEFAULT refreshRate
- static constexpr bool mForceHDRContentToDefaultRefreshRate = true;
+ static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 3bf3922..f193553 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -30,19 +30,18 @@
// about layers.
static constexpr size_t ARRAY_SIZE = 30;
-// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
-// the config is not visible to SF, and is completely maintained by HWC. However, we would
-// still like to keep track of time when the device is in this config.
-static constexpr int SCREEN_OFF_CONFIG_ID = -1;
-static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
-
-// This number is used when we try to determine how long does a given layer stay relevant.
-// Currently it is set to 100ms, because that would indicate 10Hz rendering.
-static constexpr std::chrono::nanoseconds TIME_EPSILON_NS = 100ms;
-
// This number is used when we try to determine how long do we keep layer information around
-// before we remove it. Currently it is set to 100ms.
-static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 100ms;
+// before we remove it. It is also used to determine how long the layer stays relevant.
+// This time period captures infrequent updates when playing YouTube video with static image,
+// or waiting idle in messaging app, when cursor is blinking.
+static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
+
+// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
+// LOW_ACTIVITY_EPSILON_NS apart.
+// This is helping SF to vote for lower refresh rates when there is not activity
+// in screen.
+static constexpr int LOW_ACTIVITY_BUFFERS = 2;
+static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
// Calculates the statistical mean (average) in the data structure (array, vector). The
// function does not modify the contents of the array.
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
new file mode 100644
index 0000000..7a3bf8e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "VSyncModulator.h"
+
+#include <cutils/properties.h>
+#include <utils/Trace.h>
+
+#include <cinttypes>
+#include <mutex>
+
+namespace android {
+
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+VSyncModulator::VSyncModulator() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.vsync_trace_detailed_info", value, "0");
+ mTraceDetailedInfo = atoi(value);
+ // Populate the offset map with some default offsets.
+ const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0};
+ setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0);
+}
+
+void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
+ nsecs_t thresholdForNextVsync) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mOffsetMap.insert_or_assign(OffsetType::Early, early);
+ mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl);
+ mOffsetMap.insert_or_assign(OffsetType::Late, late);
+ mThresholdForNextVsync = thresholdForNextVsync;
+ updateOffsetsLocked();
+}
+
+void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
+ if (transactionStart == Scheduler::TransactionStart::EARLY) {
+ mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ }
+
+ // An early transaction stays an early transaction.
+ if (transactionStart == mTransactionStart ||
+ mTransactionStart == Scheduler::TransactionStart::EARLY) {
+ return;
+ }
+ mTransactionStart = transactionStart;
+ updateOffsets();
+}
+
+void VSyncModulator::onTransactionHandled() {
+ if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
+ mTransactionStart = Scheduler::TransactionStart::NORMAL;
+ updateOffsets();
+}
+
+void VSyncModulator::onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) {
+ return;
+ }
+ mRefreshRateChangePending = true;
+ updateOffsets();
+}
+
+void VSyncModulator::onRefreshRateChangeCompleted() {
+ if (!mRefreshRateChangePending) {
+ return;
+ }
+ mRefreshRateChangePending = false;
+ updateOffsets();
+}
+
+void VSyncModulator::onRefreshed(bool usedRenderEngine) {
+ bool updateOffsetsNeeded = false;
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updateOffsetsNeeded = true;
+ }
+ if (usedRenderEngine) {
+ mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
+ updateOffsetsNeeded = true;
+ } else if (mRemainingRenderEngineUsageCount > 0) {
+ mRemainingRenderEngineUsageCount--;
+ updateOffsetsNeeded = true;
+ }
+ if (updateOffsetsNeeded) {
+ updateOffsets();
+ }
+}
+
+VSyncModulator::Offsets VSyncModulator::getOffsets() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOffsets;
+}
+
+VSyncModulator::Offsets VSyncModulator::getNextOffsets() {
+ return mOffsetMap.at(getNextOffsetType());
+}
+
+VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() {
+ // Early offsets are used if we're in the middle of a refresh rate
+ // change, or if we recently begin a transaction.
+ if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
+ mRefreshRateChangePending) {
+ return OffsetType::Early;
+ } else if (mRemainingRenderEngineUsageCount > 0) {
+ return OffsetType::EarlyGl;
+ } else {
+ return OffsetType::Late;
+ }
+}
+
+void VSyncModulator::updateOffsets() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ updateOffsetsLocked();
+}
+
+void VSyncModulator::updateOffsetsLocked() {
+ const Offsets desired = getNextOffsets();
+
+ if (mSfConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
+ }
+
+ if (mAppConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
+ }
+
+ flushOffsets();
+}
+
+void VSyncModulator::flushOffsets() {
+ OffsetType type = getNextOffsetType();
+ mOffsets = mOffsetMap.at(type);
+ if (!mTraceDetailedInfo) {
+ return;
+ }
+ ATRACE_INT("Vsync-EarlyOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early);
+ ATRACE_INT("Vsync-EarlyGLOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl);
+ ATRACE_INT("Vsync-LateOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late);
+ ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early);
+ ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl);
+ ATRACE_INT("Vsync-HighFpsLateOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 81a7864..ddbd221 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,8 +16,6 @@
#pragma once
-#include <utils/Errors.h>
-
#include <cinttypes>
#include <mutex>
@@ -35,12 +33,28 @@
// sending new transactions.
const int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
+ // Number of frames we'll keep the early gl phase offsets once they are activated.
+ // This acts as a low-pass filter to avoid scenarios where we rapidly
+ // switch in and out of gl composition.
+ const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+
public:
+ VSyncModulator();
+
+ // Wrapper for a collection of surfaceflinger/app offsets for a particular
+ // configuration .
struct Offsets {
+ scheduler::RefreshRateConfigs::RefreshRateType fpsMode;
nsecs_t sf;
nsecs_t app;
};
+ enum class OffsetType {
+ Early,
+ EarlyGl,
+ Late,
+ };
+
// Sets the phase offsets
//
// sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
@@ -51,31 +65,10 @@
// appEarly: Like sfEarly, but for the app-vsync
// appEarlyGl: Like sfEarlyGl, but for the app-vsync.
// appLate: The regular app vsync phase offset.
- void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) {
- mEarlyOffsets = early;
- mEarlyGlOffsets = earlyGl;
- mLateOffsets = late;
+ void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
+ nsecs_t thresholdForNextVsync) EXCLUDES(mMutex);
- if (mSfConnectionHandle && late.sf != mOffsets.load().sf) {
- mScheduler->setPhaseOffset(mSfConnectionHandle, late.sf);
- }
-
- if (mAppConnectionHandle && late.app != mOffsets.load().app) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, late.app);
- }
-
- mOffsets = late;
- }
-
- Offsets getEarlyOffsets() const { return mEarlyOffsets; }
-
- Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; }
-
- void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
- mSfEventThread = sfEventThread;
- mAppEventThread = appEventThread;
- }
-
+ // Sets the scheduler and vsync connection handlers.
void setSchedulerAndHandles(Scheduler* scheduler,
Scheduler::ConnectionHandle* appConnectionHandle,
Scheduler::ConnectionHandle* sfConnectionHandle) {
@@ -84,120 +77,57 @@
mSfConnectionHandle = sfConnectionHandle;
}
- void setTransactionStart(Scheduler::TransactionStart transactionStart) {
- if (transactionStart == Scheduler::TransactionStart::EARLY) {
- mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
- }
+ // Signals that a transaction has started, and changes offsets accordingly.
+ void setTransactionStart(Scheduler::TransactionStart transactionStart);
- // An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EARLY) {
- return;
- }
- mTransactionStart = transactionStart;
- updateOffsets();
- }
-
- void onTransactionHandled() {
- if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
- mTransactionStart = Scheduler::TransactionStart::NORMAL;
- updateOffsets();
- }
+ // Signals that a transaction has been completed, so that we can finish
+ // special handling for a transaction.
+ void onTransactionHandled();
// Called when we send a refresh rate change to hardware composer, so that
// we can move into early offsets.
- void onRefreshRateChangeInitiated() {
- if (mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = true;
- updateOffsets();
- }
+ void 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.
- void onRefreshRateChangeDetected() {
- if (!mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = false;
- updateOffsets();
- }
+ void onRefreshRateChangeCompleted();
- void onRefreshed(bool usedRenderEngine) {
- bool updateOffsetsNeeded = false;
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
- }
- if (usedRenderEngine != mLastFrameUsedRenderEngine) {
- mLastFrameUsedRenderEngine = usedRenderEngine;
- updateOffsetsNeeded = true;
- }
- if (updateOffsetsNeeded) {
- updateOffsets();
- }
- }
+ // Called when the display is presenting a new frame. usedRenderEngine
+ // should be set to true if RenderEngine was involved with composing the new
+ // frame.
+ void onRefreshed(bool usedRenderEngine);
- Offsets getOffsets() {
- // Early offsets are used if we're in the middle of a refresh rate
- // change, or if we recently begin a transaction.
- if (mTransactionStart == Scheduler::TransactionStart::EARLY ||
- mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
- return mEarlyOffsets;
- } else if (mLastFrameUsedRenderEngine) {
- return mEarlyGlOffsets;
- } else {
- return mLateOffsets;
- }
- }
+ // Returns the offsets that we are currently using
+ Offsets getOffsets() EXCLUDES(mMutex);
private:
- void updateOffsets() {
- const Offsets desired = getOffsets();
- const Offsets current = mOffsets;
+ // Returns the next offsets that we should be using
+ Offsets getNextOffsets() REQUIRES(mMutex);
+ // Returns the next offset type that we should use.
+ OffsetType getNextOffsetType();
+ // Updates offsets and persists them into the scheduler framework.
+ void updateOffsets() EXCLUDES(mMutex);
+ void updateOffsetsLocked() REQUIRES(mMutex);
+ // Updates the internal offsets and offset type.
+ void flushOffsets() REQUIRES(mMutex);
- bool changed = false;
- if (desired.sf != current.sf) {
- if (mSfConnectionHandle != nullptr) {
- mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
- } else {
- mSfEventThread->setPhaseOffset(desired.sf);
- }
- changed = true;
- }
- if (desired.app != current.app) {
- if (mAppConnectionHandle != nullptr) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
- } else {
- mAppEventThread->setPhaseOffset(desired.app);
- }
- changed = true;
- }
-
- if (changed) {
- mOffsets = desired;
- }
- }
-
- Offsets mLateOffsets;
- Offsets mEarlyOffsets;
- Offsets mEarlyGlOffsets;
-
- EventThread* mSfEventThread = nullptr;
- EventThread* mAppEventThread = nullptr;
+ mutable std::mutex mMutex;
+ std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex);
+ nsecs_t mThresholdForNextVsync;
Scheduler* mScheduler = nullptr;
Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
- std::atomic<Offsets> mOffsets;
+ Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0};
std::atomic<Scheduler::TransactionStart> mTransactionStart =
Scheduler::TransactionStart::NORMAL;
- std::atomic<bool> mLastFrameUsedRenderEngine = false;
std::atomic<bool> mRefreshRateChangePending = false;
std::atomic<int> mRemainingEarlyFrameCount = 0;
+ std::atomic<int> mRemainingRenderEngineUsageCount = 0;
+
+ bool mTraceDetailedInfo = false;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bc134cb..e8ddb0c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -48,6 +48,8 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <dvr/vr_flinger.h>
#include <gui/BufferQueue.h>
+#include <gui/DebugEGLImageTracker.h>
+
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
@@ -311,6 +313,9 @@
wideColorGamutCompositionPixelFormat =
static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+ mColorSpaceAgnosticDataspace =
+ static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
+
useContextPriority = use_context_priority(true);
auto tmpPrimaryDisplayOrientation = primary_display_orientation(
@@ -382,7 +387,8 @@
mLumaSampling = atoi(value);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
@@ -564,14 +570,16 @@
readPersistentProperties();
mBootStage = BootStage::FINISHED;
- // set the refresh rate according to the policy
- const auto& performanceRefreshRate =
- mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE);
+ if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ // set the refresh rate according to the policy
+ const auto& performanceRefreshRate =
+ mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
- if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) {
- setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
- } else {
- setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+ if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
+ } else {
+ setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+ }
}
}));
}
@@ -614,29 +622,6 @@
ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
Mutex::Autolock _l(mStateLock);
- // start the EventThread
- mScheduler =
- getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
- mRefreshRateConfigs);
- auto resyncCallback =
- mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
- mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
- resyncCallback,
- impl::EventThread::InterceptVSyncsCallback());
- mSfConnectionHandle = mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
- resyncCallback, [this](nsecs_t timestamp) {
- mInterceptor->saveVSyncEvent(timestamp);
- });
-
- mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
- mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
- mSfConnectionHandle.get());
-
- mRegionSamplingThread =
- new RegionSamplingThread(*this, *mScheduler,
- RegionSamplingThread::EnvironmentTimingTunables());
// Get a RenderEngine for the given display / config (can't fail)
int32_t renderEngineFeature = 0;
@@ -707,19 +692,6 @@
ALOGE("Run StartPropertySetThread failed!");
}
- mScheduler->setChangeRefreshRateCallback(
- [this](RefreshRateType type, Scheduler::ConfigEvent event) {
- Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(type, event);
- });
- mScheduler->setGetVsyncPeriodCallback([this] {
- Mutex::Autolock lock(mStateLock);
- return getVsyncPeriod();
- });
-
- mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
- mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
-
ALOGV("Done initializing");
}
@@ -873,7 +845,8 @@
info.xdpi = xdpi;
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
+ const auto refreshRateType =
+ mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
info.appVsyncOffset = offset.late.app;
@@ -941,10 +914,13 @@
// Start receiving vsync samples now, so that we can detect a period
// switch.
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ // As we called to set period, we will call to onRefreshRateChangeCompleted once
+ // DispSync model is locked.
+ mVsyncModulator.onRefreshRateChangeInitiated();
mPhaseOffsets->setRefreshRateType(info.type);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.onRefreshRateChangeInitiated();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
}
mDesiredActiveConfigChanged = true;
ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
@@ -972,14 +948,15 @@
}
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId);
+ mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
+ mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
display->setActiveConfig(mUpcomingActiveConfig.configId);
- mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -988,6 +965,19 @@
}
}
+void SurfaceFlinger::desiredActiveConfigChangeDone() {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
+ mDesiredActiveConfigChanged = false;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
+}
+
bool SurfaceFlinger::performSetActiveConfig() {
ATRACE_CALL();
if (mCheckPendingFence) {
@@ -1017,10 +1007,7 @@
if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
// display is not valid or we are already in the requested mode
// on both cases there is nothing left to do
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
- mDesiredActiveConfigChanged = false;
- ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ desiredActiveConfigChangeDone();
return false;
}
@@ -1028,13 +1015,10 @@
// allowed configs might have change by the time we process the refresh.
// Make sure the desired config is still allowed
if (!isDisplayConfigAllowed(desiredActiveConfig.configId)) {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
- mDesiredActiveConfig.configId = display->getActiveConfig();
- mDesiredActiveConfigChanged = false;
- ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ desiredActiveConfigChangeDone();
return false;
}
+
mUpcomingActiveConfig = desiredActiveConfig;
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
@@ -1244,9 +1228,6 @@
return;
}
- auto resyncCallback =
- mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
// TODO(b/128863962): Part of the Injector should be refactored, so that it
// can be passed to Scheduler.
if (enable) {
@@ -1258,11 +1239,11 @@
impl::EventThread::InterceptVSyncsCallback(),
"injEventThread");
}
- mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
+ mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); });
} else {
ALOGV("VSync Injections disabled");
mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
- std::move(resyncCallback));
+ [&] { mScheduler->resync(); });
}
mInjectVSyncs = enable;
@@ -1365,16 +1346,11 @@
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource) {
- auto resyncCallback = mScheduler->makeResyncCallback([this] {
- Mutex::Autolock lock(mStateLock);
- return getVsyncPeriod();
- });
-
+ ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
const auto& handle =
vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback));
+ return mScheduler->createDisplayEventConnection(handle, configChanged);
}
// ----------------------------------------------------------------------------
@@ -1447,10 +1423,10 @@
return;
}
- bool periodChanged = false;
- mScheduler->addResyncSample(timestamp, &periodChanged);
- if (periodChanged) {
- mVsyncModulator.onRefreshRateChangeDetected();
+ bool periodFlushed = false;
+ mScheduler->addResyncSample(timestamp, &periodFlushed);
+ if (periodFlushed) {
+ mVsyncModulator.onRefreshRateChangeCompleted();
}
}
@@ -1471,13 +1447,8 @@
ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
- const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate);
- if (!refreshRateConfig) {
- ALOGV("Skipping refresh rate change request for unsupported rate.");
- return;
- }
-
- const int desiredConfigId = refreshRateConfig->configId;
+ const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate);
+ const int desiredConfigId = refreshRateConfig.configId;
if (!isDisplayConfigAllowed(desiredConfigId)) {
ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
@@ -1523,10 +1494,23 @@
void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
ATRACE_CALL();
- Mutex::Autolock lock(mStateLock);
+
+ // Enable / Disable HWVsync from the main thread to avoid race conditions with
+ // display power state.
+ postMessageAsync(new LambdaMessage(
+ [=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
+}
+
+void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
+ ATRACE_CALL();
+
+ mHWCVsyncPendingState = enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable;
+
if (const auto displayId = getInternalDisplayIdLocked()) {
- getHwComposer().setVsyncEnabled(*displayId,
- enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+ sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
+ if (display && display->isPoweredOn()) {
+ setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ }
}
}
@@ -1624,7 +1608,8 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-bool SurfaceFlinger::previousFrameMissed() NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
// 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
@@ -1633,14 +1618,46 @@
? mPreviousPresentFences[0]
: mPreviousPresentFences[1];
- return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
+ if (fence == Fence::NO_FENCE) {
+ return false;
+ }
+
+ if (graceTimeMs > 0 && fence->getStatus() == Fence::Status::Unsignaled) {
+ fence->wait(graceTimeMs);
+ }
+
+ return (fence->getStatus() == Fence::Status::Unsignaled);
+}
+
+void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
+ DisplayStatInfo stats;
+ mScheduler->getDisplayStatInfo(&stats);
+ const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
+ // Inflate the expected present time if we're targetting the next vsync.
+ mExpectedPresentTime =
+ mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
+ ? presentTime
+ : presentTime + stats.vsyncPeriod;
}
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
- bool frameMissed = previousFrameMissed();
+ // calculate the expected present time once and use the cached
+ // value throughout this frame to make sure all layers are
+ // seeing this same value.
+ populateExpectedPresentTime();
+
+ // When Backpressure propagation is enabled we want to give a small grace period
+ // for the present fence to fire instead of just giving up on this frame to handle cases
+ // where present fence is just about to get signaled.
+ const int graceTimeForPresentFenceMs =
+ (mPropagateBackpressure &&
+ (mPropagateBackpressureClientComposition || !mHadClientComposition))
+ ? 1
+ : 0;
+ bool frameMissed = previousFrameMissed(graceTimeForPresentFenceMs);
bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
bool gpuFrameMissed = mHadClientComposition && frameMissed;
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
@@ -1839,7 +1856,14 @@
RenderIntent renderIntent;
pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
display->setColorMode(colorMode, targetDataspace, renderIntent);
+
+ if (isHdrColorMode(colorMode)) {
+ targetDataspace = Dataspace::UNKNOWN;
+ } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) {
+ targetDataspace = mColorSpaceAgnosticDataspace;
+ }
}
+
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
if (layer->isHdrY410()) {
layer->forceClientComposition(displayDevice);
@@ -1866,9 +1890,7 @@
const auto& displayState = display->getState();
layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
- displayDevice->getSupportedPerFrameMetadata(),
- isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN
- : targetDataspace);
+ displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
}
}
@@ -2531,6 +2553,9 @@
if (event.connection == HWC2::Connection::Connected) {
if (!mPhysicalDisplayTokens.count(info->id)) {
ALOGV("Creating display %s", to_string(info->id).c_str());
+ if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+ initScheduler(info->id);
+ }
mPhysicalDisplayTokens[info->id] = new BBinder();
DisplayDeviceState state;
state.displayId = info->id;
@@ -2931,7 +2956,7 @@
setInputWindowsFinished();
}
- executeInputWindowCommands();
+ mInputWindowCommands.clear();
}
void SurfaceFlinger::updateInputWindowInfo() {
@@ -2955,19 +2980,6 @@
mPendingInputWindowCommands.clear();
}
-void SurfaceFlinger::executeInputWindowCommands() {
- for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) {
- if (transferTouchFocusCommand.fromToken != nullptr &&
- transferTouchFocusCommand.toToken != nullptr &&
- transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) {
- mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken,
- transferTouchFocusCommand.toToken);
- }
- }
-
- mInputWindowCommands.clear();
-}
-
void SurfaceFlinger::updateCursorAsync()
{
for (const auto& [token, display] : mDisplays) {
@@ -2989,6 +3001,55 @@
layer->releasePendingBuffer(systemTime());
}
+void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+ if (mScheduler) {
+ // In practice it's not allowed to hotplug in/out the primary display once it's been
+ // connected during startup, but some tests do it, so just warn and return.
+ ALOGW("Can't re-init scheduler");
+ return;
+ }
+
+ int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+ mRefreshRateConfigs =
+ std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
+ getHwComposer().getConfigs(
+ primaryDisplayId),
+ currentConfig);
+ mRefreshRateStats =
+ std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
+ currentConfig, HWC_POWER_MODE_OFF);
+ mRefreshRateStats->setConfigMode(currentConfig);
+
+ // start the EventThread
+ mScheduler =
+ getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
+ *mRefreshRateConfigs);
+ mAppConnectionHandle =
+ mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
+ mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ impl::EventThread::InterceptVSyncsCallback());
+ mSfConnectionHandle =
+ mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
+ mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ [this](nsecs_t timestamp) {
+ mInterceptor->saveVSyncEvent(timestamp);
+ });
+
+ mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+ mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
+ mSfConnectionHandle.get());
+
+ mRegionSamplingThread =
+ new RegionSamplingThread(*this, *mScheduler,
+ RegionSamplingThread::EnvironmentTimingTunables());
+
+ mScheduler->setChangeRefreshRateCallback(
+ [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(type, event);
+ });
+}
+
void SurfaceFlinger::commitTransaction()
{
if (!mLayersPendingRemoval.isEmpty()) {
@@ -3244,8 +3305,7 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
- nsecs_t expectedPresentTime;
- expectedPresentTime = mScheduler->expectedPresentTime();
+ const nsecs_t expectedPresentTime = getExpectedPresentTime();
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.push_back(layer);
} else {
@@ -3642,7 +3702,7 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
const Vector<ComposerState>& states) {
- nsecs_t expectedPresentTime = mScheduler->expectedPresentTime();
+ nsecs_t expectedPresentTime = getExpectedPresentTime();
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
@@ -3766,6 +3826,7 @@
if (uncacheBuffer.isValid()) {
ClientCache::getInstance().erase(uncacheBuffer);
+ getRenderEngine().unbindExternalTextureBuffer(uncacheBuffer.id);
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4120,9 +4181,18 @@
bool bufferChanged = what & layer_state_t::eBufferChanged;
bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
sp<GraphicBuffer> buffer;
- if (bufferChanged && cacheIdChanged) {
- ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+ if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
buffer = s.buffer;
+ bool success = ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+ if (success) {
+ getRenderEngine().cacheExternalTextureBuffer(s.buffer);
+ success = ClientCache::getInstance()
+ .registerErasedRecipient(s.cachedBuffer,
+ wp<ClientCache::ErasedRecipient>(this));
+ if (!success) {
+ getRenderEngine().unbindExternalTextureBuffer(s.buffer->getId());
+ }
+ }
} else if (cacheIdChanged) {
buffer = ClientCache::getInstance().get(s.cachedBuffer);
} else if (bufferChanged) {
@@ -4405,6 +4475,13 @@
new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
}
+void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled) {
+ if (mHWCVsyncState != enabled) {
+ getHwComposer().setVsyncEnabled(displayId, enabled);
+ mHWCVsyncState = enabled;
+ }
+}
+
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
if (display->isVirtual()) {
ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
@@ -4431,6 +4508,7 @@
// Turn on the display
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+ setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
@@ -4456,6 +4534,9 @@
mScheduler->onScreenReleased(mAppConnectionHandle);
}
+ // Make sure HWVsync is disabled before turning off the display
+ setVsyncEnabledInHWC(*displayId, HWC2::Vsync::Disable);
+
getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
// from this point on, SF will stop drawing on this display
@@ -4481,7 +4562,8 @@
if (display->isPrimary()) {
mTimeStats->setPowerMode(mode);
- mRefreshRateStats.setPowerMode(mode);
+ mRefreshRateStats->setPowerMode(mode);
+ mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
@@ -4646,7 +4728,16 @@
StringAppendF(&result, "Scheduler enabled.");
StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n",
mUseSmart90ForVideo ? "on" : "off");
+ StringAppendF(&result, "Allowed Display Configs: ");
+ for (int32_t configId : mAllowedDisplayConfigs) {
+ StringAppendF(&result, "%" PRIu32 " Hz, ",
+ mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
+ }
+ StringAppendF(&result, "(config override by backdoor: %s)\n\n",
+ mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
mScheduler->dump(mAppConnectionHandle, result);
+ StringAppendF(&result, "+ Refresh rate switching: %s\n",
+ mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
}
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4931,6 +5022,8 @@
getRenderEngine().dump(result);
+ DebugEGLImageTracker::getInstance()->dump(result);
+
if (const auto display = getDefaultDisplayDeviceLocked()) {
display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
"undefinedRegion");
@@ -5009,7 +5102,7 @@
result.append("\nScheduler state:\n");
result.append(mScheduler->doDump() + "\n");
StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
- result.append(mRefreshRateStats.doDump() + "\n");
+ result.append(mRefreshRateStats->doDump() + "\n");
result.append(mTimeStats->miniDump());
result.append("\n");
@@ -5344,7 +5437,12 @@
return NO_ERROR;
}
case 1023: { // Set native mode
+ int32_t colorMode;
+
mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
+ if (data.readInt32(&colorMode) == NO_ERROR) {
+ mForceColorMode = static_cast<ColorMode>(colorMode);
+ }
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -5475,7 +5573,8 @@
case 1034: {
// TODO(b/129297325): expose this via developer menu option
n = data.readInt32();
- if (n && !mRefreshRateOverlay) {
+ if (n && !mRefreshRateOverlay &&
+ mRefreshRateConfigs->refreshRateSwitchingSupported()) {
RefreshRateType type;
{
std::lock_guard<std::mutex> lock(mActiveConfigLock);
@@ -6059,18 +6158,43 @@
return;
}
- ALOGV("Updating allowed configs");
- mAllowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(), allowedConfigs.end());
+ const auto allowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(),
+ allowedConfigs.end());
+ if (allowedDisplayConfigs == mAllowedDisplayConfigs) {
+ return;
+ }
- // Set the highest allowed config by iterating backwards on available refresh rates
- const auto& refreshRates = mRefreshRateConfigs.getRefreshRates();
- for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
- if (iter->second && isDisplayConfigAllowed(iter->second->configId)) {
- ALOGV("switching to config %d", iter->second->configId);
- setDesiredActiveConfig(
- {iter->first, iter->second->configId, Scheduler::ConfigEvent::Changed});
- break;
+ ALOGV("Updating allowed configs");
+ mAllowedDisplayConfigs = std::move(allowedDisplayConfigs);
+
+ // TODO(b/140204874): This hack triggers a notification that something has changed, so
+ // that listeners that care about a change in allowed configs can get the notification.
+ // Giving current ActiveConfig so that most other listeners would just drop the event
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
+ display->getActiveConfig());
+
+ if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ const auto& type = mScheduler->getPreferredRefreshRateType();
+ const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type);
+ if (isDisplayConfigAllowed(config.configId)) {
+ ALOGV("switching to Scheduler preferred config %d", config.configId);
+ setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed});
+ } else {
+ // Set the highest allowed config by iterating backwards on available refresh rates
+ const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap();
+ for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
+ if (isDisplayConfigAllowed(iter->second.configId)) {
+ ALOGV("switching to allowed config %d", iter->second.configId);
+ setDesiredActiveConfig(
+ {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed});
+ break;
+ }
+ }
}
+ } else if (!isDisplayConfigAllowed(display->getActiveConfig())) {
+ ALOGV("switching to config %d", allowedConfigs[0]);
+ setDesiredActiveConfig(
+ {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
}
}
@@ -6087,7 +6211,7 @@
return NO_ERROR;
}
- postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+ postMessageSync(new LambdaMessage([&]() {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set allowed display configs for invalid display token %p",
@@ -6095,6 +6219,7 @@
} else if (display->isVirtual()) {
ALOGW("Attempt to set allowed display configs for virtual display");
} else {
+ Mutex::Autolock lock(mStateLock);
setAllowedDisplayConfigsInternal(display, allowedConfigs);
}
}));
@@ -6140,6 +6265,10 @@
return nullptr;
}
+void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
+ getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 03c3d95..7767789 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -170,9 +170,9 @@
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
+ public ClientCache::ErasedRecipient,
private IBinder::DeathRecipient,
- private HWC2::ComposerCallback
-{
+ private HWC2::ComposerCallback {
public:
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -293,11 +293,20 @@
// TODO: this should be made accessible only to EventThread
void setPrimaryVsyncEnabled(bool enabled);
+ // main thread function to enable/disable h/w composer event
+ void setPrimaryVsyncEnabledInternal(bool enabled);
+
// called on the main thread by MessageQueue when an internal message
// is received
// TODO: this should be made accessible only to MessageQueue
void onMessageReceived(int32_t what);
+ // populates the expected present time for this frame.
+ // When we are in negative offsets, we perform a correction so that the
+ // predicted vsync for the *next* frame is used instead.
+ void populateExpectedPresentTime();
+ nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; }
+
// for debugging only
// TODO: this should be made accessible only to HWComposer
const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
@@ -319,6 +328,9 @@
sp<Layer> fromHandle(const sp<IBinder>& handle) REQUIRES(mStateLock);
+ // Inherit from ClientCache::ErasedRecipient
+ void bufferErased(const client_cache_t& clientCacheId) override;
+
private:
friend class BufferLayer;
friend class BufferQueueLayer;
@@ -399,7 +411,9 @@
const sp<IGraphicBufferProducer>& bufferProducer) const override;
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
sp<IDisplayEventConnection> createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp) override;
+ ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress) override;
status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
@@ -508,13 +522,15 @@
// Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
// Once HWC has returned the present fence, this sets the active config and a new refresh
- // rate in SF. It also triggers HWC vsync.
+ // rate in SF.
void setActiveConfigInternal() REQUIRES(mStateLock);
// Active config is updated on INVALIDATE call in a state machine-like manner. When the
// desired config was set, HWC needs to update the panel on the next refresh, and when
// we receive the fence back, we know that the process was complete. It returns whether
// we need to wait for the next invalidate
bool performSetActiveConfig() REQUIRES(mStateLock);
+ // Called when active config is no longer is progress
+ void desiredActiveConfigChangeDone() REQUIRES(mStateLock);
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
@@ -537,9 +553,9 @@
void updateInputFlinger();
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
- void executeInputWindowCommands();
void setInputWindowsFinished();
void updateCursorAsync();
+ void initScheduler(DisplayId primaryDisplayId);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -832,7 +848,8 @@
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
- bool previousFrameMissed();
+ bool previousFrameMissed(int graceTimeMs = 0);
+ void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
/*
* Debugging & dumpsys
@@ -1103,6 +1120,7 @@
ui::Dataspace mDefaultCompositionDataspace;
ui::Dataspace mWideColorGamutCompositionDataspace;
+ ui::Dataspace mColorSpaceAgnosticDataspace;
SurfaceFlingerBE mBE;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
@@ -1115,8 +1133,8 @@
sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
- scheduler::RefreshRateConfigs mRefreshRateConfigs;
- scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
+ std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+ std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
// All configs are allowed if the set is empty.
using DisplayConfigs = std::set<int32_t>;
@@ -1167,6 +1185,12 @@
// The Layer pointer is removed from the set when the destructor is called so there shouldn't
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
+
+ // Flags to capture the state of Vsync in HWC
+ HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
+ HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
+
+ nsecs_t mExpectedPresentTime;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 2b33ba1..b4716eb 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -218,6 +218,22 @@
return static_cast<int32_t>(defaultValue);
}
+int64_t color_space_agnostic_dataspace(Dataspace defaultValue) {
+ auto temp = SurfaceFlingerProperties::color_space_agnostic_dataspace();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return static_cast<int64_t>(defaultValue);
+}
+
+bool refresh_rate_switching(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
int32_t set_idle_timer_ms(int32_t defaultValue) {
auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
if (temp.has_value()) {
@@ -234,6 +250,14 @@
return defaultValue;
}
+int32_t set_display_power_timer_ms(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::set_display_power_timer_ms();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
bool use_smart_90_for_video(bool defaultValue) {
auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 1964ccd..e394cca 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -70,10 +70,17 @@
int32_t wcg_composition_pixel_format(
android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+int64_t color_space_agnostic_dataspace(
+ android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+bool refresh_rate_switching(bool defaultValue);
+
int32_t set_idle_timer_ms(int32_t defaultValue);
int32_t set_touch_timer_ms(int32_t defaultValue);
+int32_t set_display_power_timer_ms(int32_t defaultValue);
+
bool use_smart_90_for_video(bool defaultValue);
bool enable_protected_contents(bool defaultValue);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index decabd5..000f21c 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -15,7 +15,7 @@
module: "android.sysprop.SurfaceFlingerProperties"
owner: Platform
-# The following two propertiess define (respectively):
+# The following two properties define (respectively):
#
# - The phase offset between hardware vsync and when apps are woken up by the
# Choreographer callback
@@ -251,6 +251,20 @@
prop_name: "ro.surface_flinger.wcg_composition_pixel_format"
}
+# colorSpaceAgnosticDataspace specifies the data space that
+# SurfaceFlinger expects for surfaces which are color space agnostic.
+# The variable works only when useColorManagement is specified. If
+# unspecified, the data space follows what SurfaceFlinger expects for
+# surfaces when useColorManagement is specified.
+
+prop {
+ api_name: "color_space_agnostic_dataspace"
+ type: Long
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.color_space_agnostic_dataspace"
+}
+
# Return the native panel primary data. The data includes red, green,
# blue and white. The primary format is CIE 1931 XYZ color space.
# If unspecified, the primaries is sRGB gamut by default.
@@ -287,6 +301,18 @@
prop_name: "ro.surface_flinger.display_primary_white"
}
+# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate
+# switching on the device, e.g. to switch between 60 and 90 Hz. The settings
+# below that are related to refresh rate switching will only have an effect if
+# refresh_rate_switching is enabled.
+prop {
+ api_name: "refresh_rate_switching"
+ type: Boolean
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.refresh_rate_switching"
+}
+
# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
# refresh rate. Setting this property to 0 means there is no timer.
@@ -309,6 +335,18 @@
prop_name: "ro.surface_flinger.set_touch_timer_ms"
}
+# setDisplayPowerTimerMs indicates what is considered a timeout in milliseconds for Scheduler.
+# This value is used by the Scheduler to trigger display power inactivity callbacks that will
+# keep the display in peak refresh rate as long as display power is not in normal mode.
+# Setting this property to 0 means there is no timer.
+prop {
+ api_name: "set_display_power_timer_ms"
+ type: Integer
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_display_power_timer_ms"
+}
+
# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
# screen refresh rate based on that.
prop {
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
index 6ae3ac1..2e804c8 100644
--- a/services/surfaceflinger/sysprop/api/system-current.txt
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -2,6 +2,7 @@
package android.sysprop {
public final class SurfaceFlingerProperties {
+ method public static java.util.Optional<java.lang.Long> color_space_agnostic_dataspace();
method public static java.util.Optional<java.lang.Long> default_composition_dataspace();
method public static java.util.Optional<java.lang.Integer> default_composition_pixel_format();
method public static java.util.List<java.lang.Double> display_primary_blue();
@@ -16,7 +17,9 @@
method public static java.util.Optional<java.lang.Long> max_virtual_display_dimension();
method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns();
method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation();
+ method public static java.util.Optional<java.lang.Boolean> refresh_rate_switching();
method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
+ method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_touch_timer_ms();
method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service();
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index d5f6534..c93e15e 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -28,6 +28,7 @@
#include <binder/ProcessState.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
@@ -6059,4 +6060,97 @@
}
}
+// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
+// the dropped buffer's damage region into the next buffer's damage region. If
+// we don't do this, we'll report an incorrect damage region to hardware
+// composer, resulting in broken rendering. This test checks the BufferQueue
+// case.
+//
+// Unfortunately, we don't currently have a way to inspect the damage region
+// SurfaceFlinger sends to hardware composer from a test, so this test requires
+// the dev to manually watch the device's screen during the test to spot broken
+// rendering. Because the results can't be automatically verified, this test is
+// marked disabled.
+TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
+ const int width = mDisplayWidth;
+ const int height = mDisplayHeight;
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+ const auto producer = layer->getIGraphicBufferProducer();
+ const sp<IProducerListener> dummyListener(new DummyProducerListener);
+ IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
+ ASSERT_EQ(OK,
+ producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
+
+ std::map<int, sp<GraphicBuffer>> slotMap;
+ auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
+ ASSERT_NE(nullptr, buf);
+ const auto iter = slotMap.find(slot);
+ ASSERT_NE(slotMap.end(), iter);
+ *buf = iter->second;
+ };
+
+ auto dequeue = [&](int* outSlot) {
+ ASSERT_NE(nullptr, outSlot);
+ *outSlot = -1;
+ int slot;
+ sp<Fence> fence;
+ uint64_t age;
+ FrameEventHistoryDelta timestamps;
+ const status_t dequeueResult =
+ producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ &age, ×tamps);
+ if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ sp<GraphicBuffer> newBuf;
+ ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
+ ASSERT_NE(nullptr, newBuf.get());
+ slotMap[slot] = newBuf;
+ } else {
+ ASSERT_EQ(OK, dequeueResult);
+ }
+ *outSlot = slot;
+ };
+
+ auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
+ IGraphicBufferProducer::QueueBufferInput input(
+ /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
+ /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+ /*transform=*/0, Fence::NO_FENCE);
+ input.setSurfaceDamage(damage);
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
+ };
+
+ auto fillAndPostBuffers = [&](const Color& color) {
+ int slot1;
+ ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
+ int slot2;
+ ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
+
+ sp<GraphicBuffer> buf1;
+ ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
+ sp<GraphicBuffer> buf2;
+ ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
+ fillGraphicBufferColor(buf1, Rect(width, height), color);
+ fillGraphicBufferColor(buf2, Rect(width, height), color);
+
+ const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
+ ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
+ ASSERT_NO_FATAL_FAILURE(
+ queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
+ displayTime));
+ };
+
+ const auto startTime = systemTime();
+ const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
+ int colorIndex = 0;
+ while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
+ ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
+ std::this_thread::sleep_for(1s);
+ }
+
+ ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4f8ed1a..349dd3f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -125,7 +125,17 @@
}
void setupScheduler() {
- mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+ mFlinger.mutableRefreshRateConfigs() =
+ std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
+ configs,
+ /*currentConfig=*/0);
+ mFlinger.mutableRefreshRateStats() =
+ std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(),
+ *mFlinger.mutableTimeStats(),
+ /*currentConfig=*/0,
+ /*powerMode=*/HWC_POWER_MODE_OFF);
+ mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 2e705da..0aa8cf5 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,6 +51,7 @@
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
+ static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
static constexpr int mIterations = 100;
};
@@ -78,7 +79,8 @@
void DispSyncSourceTest::createDispSyncSource() {
createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
+ mOffsetThresholdForNextVsync.count(), true,
"DispSyncSourceTest");
mDispSyncSource->setCallback(this);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 5f58e7d..f40996e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -29,6 +29,7 @@
#include <ui/DebugUtils.h>
#include "DisplayIdentificationTest.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -179,7 +180,16 @@
}
void DisplayTransactionTest::setupScheduler() {
- mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+ mFlinger.mutableRefreshRateConfigs() =
+ std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/0);
+ mFlinger.mutableRefreshRateStats() =
+ std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(),
+ *mFlinger.mutableTimeStats(),
+ /*currentConfig=*/0,
+ /*powerMode=*/HWC_POWER_MODE_OFF);
+ mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index ea908a9..dbd9b84 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -55,8 +55,9 @@
class MockEventThreadConnection : public EventThreadConnection {
public:
MockEventThreadConnection(android::impl::EventThread* eventThread,
- ResyncCallback&& resyncCallback)
- : EventThreadConnection(eventThread, std::move(resyncCallback)) {}
+ ResyncCallback&& resyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged)
+ : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -67,7 +68,8 @@
~EventThreadTest() override;
void createThread();
- sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder);
+ sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
+ ISurfaceComposer::ConfigChanged configChanged);
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
@@ -110,7 +112,8 @@
.WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
createThread();
- mConnection = createConnection(mConnectionEventCallRecorder);
+ mConnection = createConnection(mConnectionEventCallRecorder,
+ ISurfaceComposer::eConfigChangedDispatch);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -138,9 +141,10 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder) {
+ ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable());
+ new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
+ configChanged);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -267,7 +271,9 @@
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
- sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
+ sp<MockEventThreadConnection> firstConnection =
+ createConnection(firstConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(0, firstConnection);
// By itself, this should not enable vsync events
@@ -277,7 +283,8 @@
// However if there is another connection which wants events at a nonzero rate.....
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder);
+ createConnection(secondConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, secondConnection);
// EventThread should enable vsync callbacks.
@@ -363,7 +370,9 @@
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ sp<MockEventThreadConnection> errorConnection =
+ createConnection(errorConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -387,7 +396,9 @@
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
- sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ sp<MockEventThreadConnection> errorConnection =
+ createConnection(errorConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -449,5 +460,18 @@
expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
}
+TEST_F(EventThreadTest, suppressConfigChanged) {
+ ConnectionEventRecorder suppressConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> suppressConnection =
+ createConnection(suppressConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
+
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9);
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9);
+
+ auto args = suppressConnectionEventRecorder.waitForCall();
+ ASSERT_FALSE(args.has_value());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 96121bb..1d75011 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -23,6 +23,8 @@
namespace android {
namespace scheduler {
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+
class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
nsecs_t FAKE_PHASE_OFFSET_NS = 0;
@@ -34,20 +36,20 @@
nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
PhaseOffsets::Offsets getOffsetsForRefreshRate(
- RefreshRateConfigs::RefreshRateType /*refreshRateType*/) const override {
+ RefreshRateType /*refreshRateType*/) const override {
return getCurrentOffsets();
}
// Returns early, early GL, and late offsets for Apps and SF.
PhaseOffsets::Offsets getCurrentOffsets() const override {
- return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
+ return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
}
// This function should be called when the device is switching between different
// refresh rates, to properly update the offsets.
- void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {}
+ void setRefreshRateType(RefreshRateType /*refreshRateType*/) override {}
nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; }
@@ -56,4 +58,4 @@
};
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 2b1dfa8..8e7440c 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -25,7 +25,17 @@
protected:
std::unique_ptr<LayerHistory> mLayerHistory;
+ static constexpr float MIN_REFRESH_RATE = 30.f;
static constexpr float MAX_REFRESH_RATE = 90.f;
+ static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
+ static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+
+ void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
+ mLayerHistory->setVisibility(testLayer, true);
+ for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
+ };
};
LayerHistoryTest::LayerHistoryTest() {
@@ -36,31 +46,25 @@
namespace {
TEST_F(LayerHistoryTest, oneLayer) {
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
+ for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- // This is still 0, because the layer is not considered recently active if it
- // has been present in less than 10 frames.
- EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- // This should be MAX_REFRESH_RATE as we have more than 10 samples
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ // Add a few more. This time we should get MAX refresh rate as the layer
+ // becomes relevant
+ static constexpr auto A_FEW = 10;
+ for (auto i = 0u; i < A_FEW; i++) {
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
}
TEST_F(LayerHistoryTest, oneHDRLayer) {
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestHDRLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
@@ -74,12 +78,13 @@
TEST_F(LayerHistoryTest, explicitTimestamp) {
std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(test30FpsLayer, true);
nsecs_t startTime = systemTime();
- for (int i = 0; i < 31; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
}
EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
@@ -87,29 +92,31 @@
TEST_F(LayerHistoryTest, multipleLayers) {
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(test30FpsLayer, true);
std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
- mLayerHistory->createLayer("TestLayer2", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer2, true);
nsecs_t startTime = systemTime();
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
startTime = systemTime();
- for (int i = 0; i < 10; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- for (int i = 10; i < 30; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
@@ -119,10 +126,12 @@
mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- // After 100 ms frames become obsolete.
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
- // Insert the 31st frame.
- mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333), false /*isHDR*/);
+ // After 1200 ms frames become obsolete.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1500));
+
+ mLayerHistory->insert(test30FpsLayer,
+ startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 5067fe8..f315a8a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -23,7 +23,6 @@
#include "DisplayHardware/HWC2.h"
#include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
using namespace std::chrono_literals;
using testing::_;
@@ -50,9 +49,8 @@
ASSERT_EQ(left.configId, right.configId);
ASSERT_EQ(left.name, right.name);
ASSERT_EQ(left.fps, right.fps);
+ ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
}
-
- RefreshRateConfigs mConfigs;
};
RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -71,101 +69,39 @@
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
- std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
- mConfigs.populate(displayConfigs);
-
- // We always store a configuration for screen off.
- const auto& rates = mConfigs.getRefreshRates();
- ASSERT_EQ(1, rates.size());
- const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
- ASSERT_NE(rates.end(), powerSavingRate);
- ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
- ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
-
- RefreshRate expectedConfig =
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
- assertRatesEqual(expectedConfig, *powerSavingRate->second);
-
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
- ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-
- // Sanity check that getRefreshRate() does not modify the underlying configs.
- ASSERT_EQ(1, mConfigs.getRefreshRates().size());
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfig=*/0);
+ ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
}
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
- auto display = new Hwc2::mock::Display();
- std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
- auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
- config60.setVsyncPeriod(VSYNC_60);
- displayConfigs.push_back(config60.build());
- mConfigs.populate(displayConfigs);
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
+ {HWC2_CONFIG_ID_90, VSYNC_90}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfig=*/0);
- const auto& rates = mConfigs.getRefreshRates();
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ const auto& rates = refreshRateConfigs->getRefreshRateMap();
ASSERT_EQ(2, rates.size());
- const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
- const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
- ASSERT_NE(rates.end(), powerSavingRate);
- ASSERT_NE(rates.end(), defaultRate);
- ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-
- RefreshRate expectedPowerSavingConfig =
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
- assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
- RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
- assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- assertRatesEqual(expectedPowerSavingConfig,
- *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-
- // Sanity check that getRefreshRate() does not modify the underlying configs.
- ASSERT_EQ(2, mConfigs.getRefreshRates().size());
-}
-
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
- auto display = new Hwc2::mock::Display();
- std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
- auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
- config60.setVsyncPeriod(VSYNC_60);
- displayConfigs.push_back(config60.build());
- auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
- config90.setVsyncPeriod(VSYNC_90);
- displayConfigs.push_back(config90.build());
- mConfigs.populate(displayConfigs);
-
- const auto& rates = mConfigs.getRefreshRates();
- ASSERT_EQ(3, rates.size());
- const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
- ASSERT_NE(rates.end(), powerSavingRate);
ASSERT_NE(rates.end(), defaultRate);
ASSERT_NE(rates.end(), performanceRate);
- RefreshRate expectedPowerSavingConfig =
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
- assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
- RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
- assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
- RefreshRate expectedPerformanceConfig =
- RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
- assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+ RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60};
+ assertRatesEqual(expectedDefaultConfig, defaultRate->second);
+ RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90,
+ HWC2_CONFIG_ID_90};
+ assertRatesEqual(expectedPerformanceConfig, performanceRate->second);
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- assertRatesEqual(expectedPowerSavingConfig,
- *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ assertRatesEqual(expectedDefaultConfig,
+ refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
assertRatesEqual(expectedPerformanceConfig,
- *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
}
} // namespace
} // namespace scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 411ec61..cec0b32 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -22,7 +22,6 @@
#include <thread>
#include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockTimeStats.h"
using namespace std::chrono_literals;
@@ -42,9 +41,18 @@
RefreshRateStatsTest();
~RefreshRateStatsTest();
+ void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
+ mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
+ /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
+ mRefreshRateStats =
+ std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
+ /*currentConfig=*/0,
+ /*currentPowerMode=*/HWC_POWER_MODE_OFF);
+ }
+
mock::TimeStats mTimeStats;
- RefreshRateConfigs mRefreshRateConfigs;
- RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats};
+ std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+ std::unique_ptr<RefreshRateStats> mRefreshRateStats;
};
RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -63,63 +71,46 @@
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
- mRefreshRateConfigs.populate(configs);
-
- // There is one default config, so the refresh rates should have one item.
- EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size());
-}
-
TEST_F(RefreshRateStatsTest, oneConfigTest) {
- auto display = new Hwc2::mock::Display();
-
- auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
- config.setVsyncPeriod(VSYNC_90);
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
- configs.push_back(config.build());
-
- mRefreshRateConfigs.populate(configs);
+ init({{CONFIG_ID_90, VSYNC_90}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
- std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
- EXPECT_EQ(2, times.size());
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(1, times.size());
EXPECT_NE(0u, times.count("ScreenOff"));
- EXPECT_EQ(1u, times.count("90fps"));
- EXPECT_EQ(0, times["90fps"]);
// Setting up tests on mobile harness can be flaky with time passing, so testing for
// exact time changes can result in flaxy numbers. To avoid that remember old
// numbers to make sure the correct values are increasing in the next test.
int screenOff = times["ScreenOff"];
- int ninety = times["90fps"];
// Screen is off by default.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(0, times["90fps"]);
+ EXPECT_EQ(0u, times.count("90fps"));
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_LT(ninety, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90fps"));
+ EXPECT_LT(0, times["90fps"]);
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
- ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
// Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
// does not update refresh rates that come from the config.
EXPECT_LT(screenOff, times["ScreenOff"]);
@@ -127,93 +118,75 @@
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
- auto display = new Hwc2::mock::Display();
-
- auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
- config90.setVsyncPeriod(VSYNC_90);
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
- configs.push_back(config90.build());
-
- auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
- config60.setVsyncPeriod(VSYNC_60);
- configs.push_back(config60.build());
-
- mRefreshRateConfigs.populate(configs);
+ init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
- std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
- EXPECT_EQ(3, times.size());
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(1, times.size());
EXPECT_NE(0u, times.count("ScreenOff"));
- EXPECT_EQ(1u, times.count("60fps"));
- EXPECT_EQ(0, times["60fps"]);
- EXPECT_EQ(1u, times.count("90fps"));
- EXPECT_EQ(0, times["90fps"]);
// Setting up tests on mobile harness can be flaky with time passing, so testing for
// exact time changes can result in flaxy numbers. To avoid that remember old
// numbers to make sure the correct values are increasing in the next test.
int screenOff = times["ScreenOff"];
- int sixty = times["60fps"];
- int ninety = times["90fps"];
// Screen is off by default.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(sixty, times["60fps"]);
- EXPECT_EQ(ninety, times["90fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(sixty, times["60fps"]);
- EXPECT_LT(ninety, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90fps"));
+ EXPECT_LT(0, times["90fps"]);
// When power mode is normal, time for configs updates.
- mRefreshRateStats.setConfigMode(CONFIG_ID_60);
- ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_LT(sixty, times["60fps"]);
+ ASSERT_EQ(1u, times.count("60fps"));
+ EXPECT_LT(0, times["60fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
EXPECT_LT(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_60);
- ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_LT(sixty, times["60fps"]);
// Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
// does not update refresh rates that come from the config.
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_60);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1f8b111..571fdfd 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -3,13 +3,13 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
#include <mutex>
#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/Scheduler.h"
#include "mock/MockEventThread.h"
@@ -25,7 +25,8 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback()) {}
+ : EventThreadConnection(eventThread, ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -33,7 +34,7 @@
MOCK_METHOD0(requestNextVsync, void());
};
- scheduler::RefreshRateConfigs mRefreshRateConfigs;
+ std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
/**
* This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
@@ -47,7 +48,7 @@
std::unique_ptr<EventThread> makeEventThread(
const char* /* connectionName */, DispSync* /* dispSync */,
- nsecs_t /* phaseOffsetNs */,
+ nsecs_t /* phaseOffsetNs */, nsecs_t /* offsetThresholdForNextVsync */,
impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
return std::move(mEventThread);
}
@@ -72,19 +73,24 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+ mRefreshRateConfigs =
+ std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/0);
+
std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
- mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread));
+ mScheduler = std::make_unique<MockScheduler>(*mRefreshRateConfigs, std::move(eventThread));
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
mEventThreadConnection = new MockEventThreadConnection(mEventThread);
// createConnection call to scheduler makes a createEventConnection call to EventThread. Make
// sure that call gets executed and returns an EventThread::Connection object.
- EXPECT_CALL(*mEventThread, createEventConnection(_))
+ EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
+ mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16,
impl::EventThread::InterceptVSyncsCallback());
EXPECT_TRUE(mConnectionHandle != nullptr);
}
@@ -105,7 +111,10 @@
// exceptions, just gracefully continues.
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
- returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
+ returnedValue =
+ mScheduler->createDisplayEventConnection(nullptr,
+ ISurfaceComposer::
+ eConfigChangedSuppress));
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
@@ -126,7 +135,9 @@
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
+ mScheduler->createDisplayEventConnection(connectionHandle,
+ ISurfaceComposer::
+ eConfigChangedSuppress));
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
@@ -155,7 +166,9 @@
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
+ mScheduler->createDisplayEventConnection(mConnectionHandle,
+ ISurfaceComposer::
+ eConfigChangedSuppress));
EXPECT_TRUE(returnedValue != nullptr);
ASSERT_EQ(returnedValue, mEventThreadConnection);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index c3d2b8d..cb6980e 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -17,6 +17,7 @@
#pragma once
#include <gmock/gmock.h>
+#include <gui/ISurfaceComposer.h>
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
@@ -34,7 +35,8 @@
// Scheduler::Connection. This allows plugging in mock::EventThread.
sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) {
sp<EventThreadConnection> eventThreadConnection =
- new EventThreadConnection(eventThread.get(), ResyncCallback());
+ new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress);
const int64_t id = sNextId++;
mConnections.emplace(id,
std::make_unique<Scheduler::Connection>(new ConnectionHandle(id),
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 64d34ee..1c1b020 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -32,11 +32,11 @@
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceInterceptor.h"
-
#include "TimeStats/TimeStats.h"
namespace android {
@@ -342,6 +342,8 @@
auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
+ auto& mutableRefreshRateStats() { return mFlinger->mRefreshRateStats; }
+ auto& mutableTimeStats() { return mFlinger->mTimeStats; }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 12a349d..9ca116d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -35,6 +35,7 @@
MOCK_METHOD0(endResync, void());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(getPeriod, nsecs_t());
+ MOCK_METHOD0(getIntendedPeriod, nsecs_t());
MOCK_METHOD1(setRefreshSkipCount, void(int));
MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 5b5f8e7..ed35ebf 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -28,7 +28,8 @@
EventThread();
~EventThread() override;
- MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback));
+ MOCK_CONST_METHOD2(createEventConnection,
+ sp<EventThreadConnection>(ResyncCallback, ISurfaceComposer::ConfigChanged));
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 71048db..c3b5523 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -124,7 +124,7 @@
};
void AddImplicitLayers() {
- if (!is_instance_ || !driver::Debuggable())
+ if (!is_instance_)
return;
GetLayersFromSettings();
@@ -370,7 +370,8 @@
private:
bool EnableDebugCallback() const {
- return (is_instance_ && driver::Debuggable() &&
+ return (is_instance_ &&
+ android::GraphicsEnv::getInstance().isDebuggable() &&
property_get_bool("debug.vulkan.enable_callback", false));
}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index a53bb59..8ea0980 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -724,10 +724,6 @@
} // anonymous namespace
-bool Debuggable() {
- return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0);
-}
-
bool OpenHAL() {
return Hal::Open();
}
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 57c956d..075a15b 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -105,7 +105,6 @@
uint32_t driver_version;
};
-bool Debuggable();
bool OpenHAL();
const VkAllocationCallbacks& GetDefaultAllocator();
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index af1adcf..691f3b0 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -484,8 +484,7 @@
void DiscoverLayers() {
ATRACE_CALL();
- if (property_get_bool("ro.debuggable", false) &&
- prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ if (android::GraphicsEnv::getInstance().isDebuggable()) {
DiscoverLayersInPathList(kSystemLayerLibraryDir);
}
if (!android::GraphicsEnv::getInstance().getLayerPaths().empty())