Merge "Set the clock id in FrameTracer for TracePacket"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 3fa5430..20bfe65 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+ cmds/idlcli/
include/input/
libs/binder/fuzzer/
libs/binder/ndk/
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..8173c89
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,63 @@
+{
+ "presubmit": [
+ {
+ "name": "SurfaceFlinger_test",
+ "options": [
+ {
+ "include-filter": "*CredentialsTest.*"
+ },
+ {
+ "include-filter": "*SurfaceFlingerStress.*"
+ },
+ {
+ "include-filter": "*SurfaceInterceptorTest.*"
+ },
+ {
+ "include-filter": "*LayerTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerTypeTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerUpdateTest.*"
+ },
+ {
+ "include-filter": "*GeometryLatchingTest.*"
+ },
+ {
+ "include-filter": "*CropLatchingTest.*"
+ },
+ {
+ "include-filter": "*ChildLayerTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureChildOnlyTest.*"
+ },
+ {
+ "include-filter": "*DereferenceSurfaceControlTest.*"
+ },
+ {
+ "include-filter": "*BoundlessLayerTest.*"
+ },
+ {
+ "include-filter": "*MultiDisplayLayerBoundsTest.*"
+ },
+ {
+ "include-filter": "*InvalidHandleTest.*"
+ },
+ {
+ "include-filter": "*VirtualDisplayTest.*"
+ },
+ {
+ "include-filter": "*RelativeZTest.*"
+ }
+ ]
+ },
+ {
+ "name": "libsurfaceflinger_unittest"
+ }
+ ]
+}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e6e232c..95957a0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -97,6 +97,7 @@
static constexpr int kVerityPageSize = 4096;
static constexpr size_t kSha256Size = 32;
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+static constexpr const char* kFuseProp = "persist.sys.fuse";
namespace {
@@ -588,12 +589,21 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
if (flags & FLAG_CLEAR_CACHE_ONLY) {
// Clear only cached data from shared storage
auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
@@ -684,16 +694,26 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
+
path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
@@ -2574,12 +2594,19 @@
std::getline(in, target, ' ');
std::getline(in, ignored);
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
+ } else {
#if !BYPASS_SDCARDFS
- if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
- LOG(DEBUG) << "Found storage mount " << source << " at " << target;
- mStorageMounts[source] = target;
- }
+ if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
#endif
+ }
}
return ok();
}
diff --git a/include/binder b/include/binder
deleted file mode 120000
index 35a022a..0000000
--- a/include/binder
+++ /dev/null
@@ -1 +0,0 @@
-../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
new file mode 120000
index 0000000..0fc6db7
--- /dev/null
+++ b/include/binder/Binder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Binder.h
\ No newline at end of file
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 120000
index 0000000..370b260
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BinderService.h
\ No newline at end of file
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
new file mode 120000
index 0000000..93a6219
--- /dev/null
+++ b/include/binder/IBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBinder.h
\ No newline at end of file
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
new file mode 120000
index 0000000..8579878
--- /dev/null
+++ b/include/binder/IInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IInterface.h
\ No newline at end of file
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
new file mode 120000
index 0000000..5171c08
--- /dev/null
+++ b/include/binder/IMemory.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMemory.h
\ No newline at end of file
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
new file mode 120000
index 0000000..ecd4f81
--- /dev/null
+++ b/include/binder/IPCThreadState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPCThreadState.h
\ No newline at end of file
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
new file mode 120000
index 0000000..33d18cc
--- /dev/null
+++ b/include/binder/IServiceManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IServiceManager.h
\ No newline at end of file
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
new file mode 120000
index 0000000..71881fb
--- /dev/null
+++ b/include/binder/MemoryDealer.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryDealer.h
\ No newline at end of file
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
new file mode 120000
index 0000000..8fb51cc
--- /dev/null
+++ b/include/binder/MemoryHeapBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryHeapBase.h
\ No newline at end of file
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
new file mode 120000
index 0000000..23492be
--- /dev/null
+++ b/include/binder/Parcel.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcel.h
\ No newline at end of file
diff --git a/include/binder/ParcelFileDescriptor.h b/include/binder/ParcelFileDescriptor.h
new file mode 120000
index 0000000..777bd49
--- /dev/null
+++ b/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ParcelFileDescriptor.h
\ No newline at end of file
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
new file mode 120000
index 0000000..438e223
--- /dev/null
+++ b/include/binder/Parcelable.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcelable.h
\ No newline at end of file
diff --git a/include/binder/PermissionCache.h b/include/binder/PermissionCache.h
new file mode 120000
index 0000000..e910c12
--- /dev/null
+++ b/include/binder/PermissionCache.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionCache.h
\ No newline at end of file
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
new file mode 120000
index 0000000..785f2b4
--- /dev/null
+++ b/include/binder/PersistableBundle.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PersistableBundle.h
\ No newline at end of file
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
new file mode 120000
index 0000000..4cbe7a5
--- /dev/null
+++ b/include/binder/ProcessState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessState.h
\ No newline at end of file
diff --git a/include/binder/Stability.h b/include/binder/Stability.h
new file mode 120000
index 0000000..9b431d2
--- /dev/null
+++ b/include/binder/Stability.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Stability.h
\ No newline at end of file
diff --git a/include/binder/Status.h b/include/binder/Status.h
new file mode 120000
index 0000000..ccb994e
--- /dev/null
+++ b/include/binder/Status.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Status.h
\ No newline at end of file
diff --git a/include/ui b/include/ui
deleted file mode 120000
index 2fb3147..0000000
--- a/include/ui
+++ /dev/null
@@ -1 +0,0 @@
-../libs/ui/include/ui
\ No newline at end of file
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
new file mode 120000
index 0000000..9a195ea
--- /dev/null
+++ b/include/ui/DisplayInfo.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/DisplayInfo.h
\ No newline at end of file
diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h
new file mode 120000
index 0000000..d7bd737
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FloatRect.h
\ No newline at end of file
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
new file mode 120000
index 0000000..4085433
--- /dev/null
+++ b/include/ui/PixelFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PixelFormat.h
\ No newline at end of file
diff --git a/include/ui/Point.h b/include/ui/Point.h
new file mode 120000
index 0000000..443938b
--- /dev/null
+++ b/include/ui/Point.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Point.h
\ No newline at end of file
diff --git a/include/ui/PublicFormat.h b/include/ui/PublicFormat.h
new file mode 120000
index 0000000..7984c0e
--- /dev/null
+++ b/include/ui/PublicFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PublicFormat.h
\ No newline at end of file
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
new file mode 120000
index 0000000..a99c5f2
--- /dev/null
+++ b/include/ui/Rect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rect.h
\ No newline at end of file
diff --git a/include/ui/Region.h b/include/ui/Region.h
new file mode 120000
index 0000000..2e46e0f
--- /dev/null
+++ b/include/ui/Region.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Region.h
\ No newline at end of file
diff --git a/include/ui/Size.h b/include/ui/Size.h
new file mode 120000
index 0000000..c0da99b
--- /dev/null
+++ b/include/ui/Size.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Size.h
\ No newline at end of file
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 8d72a6b..79d9b79 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -109,7 +109,27 @@
#define __IINTF_CONCAT(x, y) (x ## y)
+
+#ifndef DO_NOT_CHECK_MANUAL_BINDER_INTERFACES
+
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ static_assert(internal::allowedManualInterface(NAME), \
+ "b/64223827: Manually written binder interfaces are " \
+ "considered error prone and frequently have bugs. " \
+ "The preferred way to add interfaces is to define " \
+ "an .aidl file to auto-generate the interface. If " \
+ "an interface must be manually written, add its " \
+ "name to the whitelist."); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+
+#else
+
+#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+
+#endif
+
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
const ::android::StaticString16 \
I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\
const ::android::String16 I##INTERFACE::descriptor( \
@@ -192,6 +212,122 @@
// ----------------------------------------------------------------------
+namespace internal {
+constexpr const char* const kManualInterfaces[] = {
+ "android.app.IActivityManager",
+ "android.app.IUidObserver",
+ "android.drm.IDrm",
+ "android.dvr.IVsyncCallback",
+ "android.dvr.IVsyncService",
+ "android.gfx.tests.ICallback",
+ "android.gfx.tests.IIPCTest",
+ "android.gfx.tests.ISafeInterfaceTest",
+ "android.graphicsenv.IGpuService",
+ "android.gui.DisplayEventConnection",
+ "android.gui.IConsumerListener",
+ "android.gui.IGraphicBufferConsumer",
+ "android.gui.IRegionSamplingListener",
+ "android.gui.ITransactionComposerListener",
+ "android.gui.SensorEventConnection",
+ "android.gui.SensorServer",
+ "android.hardware.ICamera",
+ "android.hardware.ICameraClient",
+ "android.hardware.ICameraRecordingProxy",
+ "android.hardware.ICameraRecordingProxyListener",
+ "android.hardware.ICrypto",
+ "android.hardware.IOMXObserver",
+ "android.hardware.ISoundTrigger",
+ "android.hardware.ISoundTriggerClient",
+ "android.hardware.ISoundTriggerHwService",
+ "android.hardware.IStreamListener",
+ "android.hardware.IStreamSource",
+ "android.input.IInputFlinger",
+ "android.input.ISetInputWindowsListener",
+ "android.media.IAudioFlinger",
+ "android.media.IAudioFlingerClient",
+ "android.media.IAudioPolicyService",
+ "android.media.IAudioPolicyServiceClient",
+ "android.media.IAudioService",
+ "android.media.IAudioTrack",
+ "android.media.IDataSource",
+ "android.media.IDrmClient",
+ "android.media.IEffect",
+ "android.media.IEffectClient",
+ "android.media.IMediaAnalyticsService",
+ "android.media.IMediaCodecList",
+ "android.media.IMediaDrmService",
+ "android.media.IMediaExtractor",
+ "android.media.IMediaExtractorService",
+ "android.media.IMediaHTTPConnection",
+ "android.media.IMediaHTTPService",
+ "android.media.IMediaLogService",
+ "android.media.IMediaMetadataRetriever",
+ "android.media.IMediaPlayer",
+ "android.media.IMediaPlayerClient",
+ "android.media.IMediaPlayerService",
+ "android.media.IMediaRecorder",
+ "android.media.IMediaRecorderClient",
+ "android.media.IMediaResourceMonitor",
+ "android.media.IMediaSource",
+ "android.media.IRemoteDisplay",
+ "android.media.IRemoteDisplayClient",
+ "android.media.IResourceManagerClient",
+ "android.media.IResourceManagerService",
+ "android.os.IComplexTypeInterface",
+ "android.os.IPermissionController",
+ "android.os.IPingResponder",
+ "android.os.IPowerManager",
+ "android.os.IProcessInfoService",
+ "android.os.ISchedulingPolicyService",
+ "android.os.IStringConstants",
+ "android.os.storage.IObbActionListener",
+ "android.os.storage.IStorageEventListener",
+ "android.os.storage.IStorageManager",
+ "android.os.storage.IStorageShutdownObserver",
+ "android.service.vr.IPersistentVrStateCallbacks",
+ "android.service.vr.IVrManager",
+ "android.service.vr.IVrStateCallbacks",
+ "android.ui.ISurfaceComposer",
+ "android.ui.ISurfaceComposerClient",
+ "android.utils.IMemory",
+ "android.utils.IMemoryHeap",
+ "com.android.car.procfsinspector.IProcfsInspector",
+ "com.android.internal.app.IAppOpsCallback",
+ "com.android.internal.app.IAppOpsService",
+ "com.android.internal.app.IBatteryStats",
+ "com.android.internal.os.IResultReceiver",
+ "com.android.internal.os.IShellCallback",
+ "drm.IDrmManagerService",
+ "drm.IDrmServiceListener",
+ "IAAudioClient",
+ "IAAudioService",
+ "VtsFuzzer",
+ nullptr,
+};
+
+constexpr const char* const kDownstreamManualInterfaces[] = {
+ // Add downstream interfaces here.
+ nullptr,
+};
+
+constexpr bool equals(const char* a, const char* b) {
+ if (*a != *b) return false;
+ if (*a == '\0') return true;
+ return equals(a + 1, b + 1);
+}
+
+constexpr bool inList(const char* a, const char* const* whitelist) {
+ if (*whitelist == nullptr) return false;
+ if (equals(a, *whitelist)) return true;
+ return inList(a, whitelist + 1);
+}
+
+constexpr bool allowedManualInterface(const char* name) {
+ return inList(name, kManualInterfaces) ||
+ inList(name, kDownstreamManualInterfaces);
+}
+
+} // namespace internal
} // namespace android
#endif // ANDROID_IINTERFACE_H
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 56b94c1..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>
@@ -68,13 +70,34 @@
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) {
@@ -94,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/gui/Android.bp b/libs/gui/Android.bp
index 4f605e0..1ae148c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -50,6 +50,7 @@
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DebugEGLImageTracker.cpp",
+ "DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
"GuiConfig.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 06a5f06..29ea84e 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -19,6 +19,7 @@
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/GLConsumer.h>
#include <chrono>
@@ -131,13 +132,20 @@
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
- t->setCrop(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
+ t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
if (applyTransaction) {
t->apply();
}
}
+Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
+ if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
+ return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+ }
+ return item.mCrop;
+}
+
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::lock_guard _lock{mMutex};
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
new file mode 100644
index 0000000..54f383e
--- /dev/null
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 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 "DisplayEventDispatcher"
+
+#include <cinttypes>
+#include <cstdint>
+
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+// Number of events to read at a time from the DisplayEventDispatcher pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::ConfigChanged configChanged)
+ : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+ ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
+}
+
+status_t DisplayEventDispatcher::initialize() {
+ status_t result = mReceiver.initCheck();
+ if (result) {
+ ALOGW("Failed to initialize display event receiver, status=%d", result);
+ return result;
+ }
+
+ int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
+ if (rc < 0) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+void DisplayEventDispatcher::dispose() {
+ ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
+
+ if (!mReceiver.initCheck()) {
+ mLooper->removeFd(mReceiver.getFd());
+ }
+}
+
+status_t DisplayEventDispatcher::scheduleVsync() {
+ if (!mWaitingForVsync) {
+ ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+
+ // Drain all pending events.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+ ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+ }
+
+ status_t status = mReceiver.requestNextVsync();
+ if (status) {
+ ALOGW("Failed to request next vsync, status=%d", status);
+ return status;
+ }
+
+ mWaitingForVsync = true;
+ }
+ return OK;
+}
+
+int DisplayEventDispatcher::handleEvent(int, int events, void*) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x",
+ events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ events);
+ return 1; // keep the callback
+ }
+
+ // Drain all pending events, keep the last vsync.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+ ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
+ this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+ mWaitingForVsync = false;
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+ }
+
+ return 1; // keep the callback
+}
+
+bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
+ PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount) {
+ bool gotVsync = false;
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ ssize_t n;
+ while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ // Later vsync events will just overwrite the info from earlier
+ // ones. That's fine, we only care about the most recent.
+ gotVsync = true;
+ *outTimestamp = ev.header.timestamp;
+ *outDisplayId = ev.header.displayId;
+ *outCount = ev.vsync.count;
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
+ ev.config.configId);
+ break;
+ default:
+ ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
+ }
+ return gotVsync;
+}
+} // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index f1758a2..dd0b470 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -62,6 +62,7 @@
BLASTBufferQueue(const BLASTBufferQueue& rhs);
void processNextBufferLocked() REQUIRES(mMutex);
+ Rect computeCrop(const BufferItem& item);
sp<SurfaceControl> mSurfaceControl;
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
new file mode 100644
index 0000000..f0b7ff5
--- /dev/null
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+class DisplayEventDispatcher : public LooperCallback {
+public:
+ explicit DisplayEventDispatcher(
+ const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress);
+
+ status_t initialize();
+ void dispose();
+ status_t scheduleVsync();
+
+protected:
+ virtual ~DisplayEventDispatcher() = default;
+
+private:
+ sp<Looper> mLooper;
+ DisplayEventReceiver mReceiver;
+ bool mWaitingForVsync;
+
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+ bool connected) = 0;
+ virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId) = 0;
+
+ virtual int handleEvent(int receiveFd, int events, void* data);
+ bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount);
+};
+} // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 6ecdae5..ae6c5cf 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -131,10 +131,10 @@
producer = igbProducer;
}
- void fillBuffer(uint32_t* bufData, uint32_t width, uint32_t height, uint32_t stride, uint8_t r,
- uint8_t g, uint8_t b) {
- for (uint32_t row = 0; row < height; row++) {
- for (uint32_t col = 0; col < width; col++) {
+ void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
+ uint8_t b) {
+ for (uint32_t row = rect.top; row < rect.bottom; row++) {
+ for (uint32_t col = rect.left; col < rect.right; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
*pixel = r;
*(pixel + 1) = g;
@@ -144,7 +144,7 @@
}
}
- void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b) {
+ void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region) {
const auto width = mScreenCaptureBuf->getWidth();
const auto height = mScreenCaptureBuf->getHeight();
const auto stride = mScreenCaptureBuf->getStride();
@@ -156,9 +156,16 @@
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
- EXPECT_EQ(r, *(pixel));
- EXPECT_EQ(g, *(pixel + 1));
- EXPECT_EQ(b, *(pixel + 2));
+ if (row >= region.top && row < region.bottom && col >= region.left &&
+ col < region.right) {
+ EXPECT_EQ(r, *(pixel));
+ EXPECT_EQ(g, *(pixel + 1));
+ EXPECT_EQ(b, *(pixel + 2));
+ } else {
+ EXPECT_EQ(0, *(pixel));
+ EXPECT_EQ(0, *(pixel + 1));
+ EXPECT_EQ(0, *(pixel + 2));
+ }
}
}
mScreenCaptureBuf->unlock();
@@ -225,7 +232,7 @@
uint32_t* bufData;
buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
reinterpret_cast<void**>(&bufData));
- fillBuffer(bufData, buf->getWidth(), buf->getHeight(), buf->getStride(), r, g, b);
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
buf->unlock();
IGraphicBufferProducer::QueueBufferOutput qbOutput;
@@ -236,7 +243,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
- sleep(1);
+ adapter.waitForCallbacks();
// capture screen and verify that it is red
bool capturedSecureLayers;
@@ -245,7 +252,8 @@
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
mDisplayWidth, mDisplayHeight,
/*useIdentityTransform*/ false));
- ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
TEST_F(BLASTBufferQueueTest, TripleBuffering) {
@@ -286,4 +294,110 @@
}
adapter.waitForCallbacks();
}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight / 2),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ int32_t bufferSideLength =
+ (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
+ int32_t finalCropSideLength = bufferSideLength / 2;
+
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSideLength, bufferSideLength,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), 0, 0, 0);
+ fillBuffer(bufData,
+ Rect(finalCropSideLength / 2, 0, buf->getWidth() - finalCropSideLength / 2,
+ buf->getHeight()),
+ buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(bufferSideLength, finalCropSideLength),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+}
+
} // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 63e0734..05ff93e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -22,7 +22,7 @@
#include <thread>
#include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/DisplayEventDispatcher.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <utils/Looper.h>
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index a9b8d66..7a497ea 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -46,7 +46,6 @@
],
shared_libs: [
- "libandroidfw",
"libgui",
"liblog",
"libnativewindow",
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index afa6a2b..3775e2b 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -69,6 +69,10 @@
include_dirs: [
"frameworks/native/include",
],
+ export_include_dirs: [
+ "include",
+ "include_private",
+ ],
// Uncomment the following line to enable VALIDATE_REGIONS traces
//defaults: ["libui-validate-regions-defaults"],
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 5dc4530..0e23ddf 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -381,7 +381,8 @@
status_t Gralloc2Allocator::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 {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
@@ -404,19 +405,33 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
-
*outStride = tmpStride;
});
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index eb43765..e189281 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -362,7 +362,8 @@
status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -381,16 +382,31 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
*outStride = tmpStride;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 73945cf..afe26b7 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -327,7 +327,8 @@
status_t Gralloc4Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -346,16 +347,31 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
*outStride = tmpStride;
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 579e68e..05fc590 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,7 +27,6 @@
#include <ui/BufferHubBuffer.h>
#endif // LIBUI_IN_VNDK
-#include <ui/Gralloc2.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <utils/Trace.h>
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index efe0931..b2b9680 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -111,10 +111,10 @@
ALOGD("%s", s.c_str());
}
-status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage,
- buffer_handle_t* handle, uint32_t* stride,
- std::string requestorName) {
+status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName, bool importBuffer) {
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
@@ -137,8 +137,18 @@
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
- status_t error =
- mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+ status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride,
+ handle, importBuffer);
+ if (error != NO_ERROR) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": %d",
+ width, height, layerCount, format, usage, error);
+ return NO_MEMORY;
+ }
+
+ if (!importBuffer) {
+ return NO_ERROR;
+ }
size_t bufSize;
// if stride has no meaning or is too large,
@@ -150,35 +160,44 @@
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);
- alloc_rec_t rec;
- rec.width = width;
- rec.height = height;
- rec.stride = *stride;
- rec.format = format;
- rec.layerCount = layerCount;
- rec.usage = usage;
- rec.size = bufSize;
- rec.requestorName = std::move(requestorName);
- list.add(*handle, rec);
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.width = width;
+ rec.height = height;
+ rec.stride = *stride;
+ rec.format = format;
+ rec.layerCount = layerCount;
+ rec.usage = usage;
+ rec.size = bufSize;
+ rec.requestorName = std::move(requestorName);
+ list.add(*handle, rec);
- return NO_ERROR;
- } else {
- ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
- "usage %" PRIx64 ": %d",
- width, height, layerCount, format, usage,
- error);
- return NO_MEMORY;
- }
+ return NO_ERROR;
+}
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ true);
}
+status_t GraphicBufferAllocator::allocateRawHandle(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle,
+ uint32_t* stride, std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ false);
+}
+
+// DEPRECATED
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride,
uint64_t /*graphicBufferId*/, std::string requestorName) {
- return allocate(width, height, format, layerCount, usage, handle, stride, requestorName);
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ true);
}
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1222cd6..83ebeca 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -23,11 +23,10 @@
#include <utils/Log.h>
+#include <ui/Point.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <ui/Point.h>
-
-#include <private/ui/RegionHelper.h>
+#include <ui/RegionHelper.h>
// ----------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 6cc23f0..c28f7a5 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -94,7 +94,8 @@
*/
virtual 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 = 0;
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const = 0;
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 948f597..12c772a 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -85,7 +85,7 @@
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;
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 0965f52..bfbc2aa 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -83,7 +83,7 @@
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;
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 14b65bc..60115f9 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -83,7 +83,7 @@
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;
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 9f6159a..34a5b17 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -42,16 +42,34 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
- // DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId
+ /**
+ * Allocates and imports a gralloc buffer.
+ *
+ * The handle must be freed with GraphicBufferAllocator::free() when no longer needed.
+ */
+ status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
+ /**
+ * Allocates and does NOT import a gralloc buffer. Buffers cannot be used until they have
+ * been imported. This function is for advanced use cases only.
+ *
+ * The raw native handle must be freed by calling native_handle_close() followed by
+ * native_handle_delete().
+ */
+ status_t allocateRawHandle(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
+ /**
+ * DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId.
+ */
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
std::string requestorName);
- status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
- uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
- std::string requestorName);
-
status_t free(buffer_handle_t handle);
size_t getTotalSize() const;
@@ -71,6 +89,10 @@
std::string requestorName;
};
+ status_t allocateHelper(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName, bool importBuffer);
+
static Mutex sLock;
static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
diff --git a/include/private/ui/RegionHelper.h b/libs/ui/include_private/ui/RegionHelper.h
similarity index 70%
rename from include/private/ui/RegionHelper.h
rename to libs/ui/include_private/ui/RegionHelper.h
index 0ec3e94..92cfba8 100644
--- a/include/private/ui/RegionHelper.h
+++ b/libs/ui/include_private/ui/RegionHelper.h
@@ -17,18 +17,17 @@
#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
#define ANDROID_UI_PRIVATE_REGION_HELPER_H
-#include <limits>
#include <stdint.h>
#include <sys/types.h>
+#include <limits>
#include <limits>
namespace android {
// ----------------------------------------------------------------------------
-template<typename RECT>
-class region_operator
-{
+template <typename RECT>
+class region_operator {
public:
typedef typename RECT::value_type TYPE;
static const TYPE max_value = std::numeric_limits<TYPE>::max();
@@ -40,39 +39,32 @@
* their corresponding value with the above formulae and use
* it when instantiating a region_operator.
*/
- static const uint32_t LHS = 0x5; // 0b101
- static const uint32_t RHS = 0x6; // 0b110
- enum {
- op_nand = LHS & ~RHS,
- op_and = LHS & RHS,
- op_or = LHS | RHS,
- op_xor = LHS ^ RHS
- };
+ static const uint32_t LHS = 0x5; // 0b101
+ static const uint32_t RHS = 0x6; // 0b110
+ enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS };
struct region {
RECT const* rects;
size_t count;
TYPE dx;
TYPE dy;
- inline region(const region& rhs)
- : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
- inline region(RECT const* _r, size_t _c)
- : rects(_r), count(_c), dx(), dy() { }
+ inline region(const region& rhs)
+ : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) {}
+ inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() {}
inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
- : rects(_r), count(_c), dx(_dx), dy(_dy) { }
+ : rects(_r), count(_c), dx(_dx), dy(_dy) {}
};
class region_rasterizer {
friend class region_operator;
virtual void operator()(const RECT& rect) = 0;
+
public:
- virtual ~region_rasterizer() { }
+ virtual ~region_rasterizer() {}
};
-
+
inline region_operator(uint32_t op, const region& lhs, const region& rhs)
- : op_mask(op), spanner(lhs, rhs)
- {
- }
+ : op_mask(op), spanner(lhs, rhs) {}
void operator()(region_rasterizer& rasterizer) {
RECT current(Rect::EMPTY_RECT);
@@ -83,31 +75,26 @@
do {
int inner_inside = spannerInner.next(current.left, current.right);
if ((op_mask >> inner_inside) & 1) {
- if (current.left < current.right &&
- current.top < current.bottom) {
+ if (current.left < current.right && current.top < current.bottom) {
rasterizer(current);
}
}
- } while(!spannerInner.isDone());
- } while(!spanner.isDone());
+ } while (!spannerInner.isDone());
+ } while (!spanner.isDone());
}
-private:
+private:
uint32_t op_mask;
- class SpannerBase
- {
+ class SpannerBase {
public:
SpannerBase()
- : lhs_head(max_value), lhs_tail(max_value),
- rhs_head(max_value), rhs_tail(max_value) {
- }
+ : lhs_head(max_value),
+ lhs_tail(max_value),
+ rhs_head(max_value),
+ rhs_tail(max_value) {}
- enum {
- lhs_before_rhs = 0,
- lhs_after_rhs = 1,
- lhs_coincide_rhs = 2
- };
+ enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 };
protected:
TYPE lhs_head;
@@ -115,9 +102,7 @@
TYPE rhs_head;
TYPE rhs_tail;
- inline int next(TYPE& head, TYPE& tail,
- bool& more_lhs, bool& more_rhs)
- {
+ inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) {
int inside;
more_lhs = false;
more_rhs = false;
@@ -157,32 +142,26 @@
}
};
- class Spanner : protected SpannerBase
- {
+ class Spanner : protected SpannerBase {
friend class region_operator;
region lhs;
region rhs;
public:
- inline Spanner(const region& _lhs, const region& _rhs)
- : lhs(_lhs), rhs(_rhs)
- {
+ inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {
if (lhs.count) {
- SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
- SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
+ SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
+ SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
}
if (rhs.count) {
- SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
- SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
+ SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
+ SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
}
}
- inline bool isDone() const {
- return !rhs.count && !lhs.count;
- }
+ inline bool isDone() const { return !rhs.count && !lhs.count; }
- inline int next(TYPE& top, TYPE& bottom)
- {
+ inline int next(TYPE& top, TYPE& bottom) {
bool more_lhs = false;
bool more_rhs = false;
int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs);
@@ -196,22 +175,21 @@
}
private:
- static inline
- void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
+ static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
// got to next span
size_t count = reg.count;
- RECT const * rects = reg.rects;
- RECT const * const end = rects + count;
+ RECT const* rects = reg.rects;
+ RECT const* const end = rects + count;
const int top = rects->top;
while (rects != end && rects->top == top) {
rects++;
count--;
}
if (rects != end) {
- aTop = rects->top + reg.dy;
+ aTop = rects->top + reg.dy;
aBottom = rects->bottom + reg.dy;
} else {
- aTop = max_value;
+ aTop = max_value;
aBottom = max_value;
}
reg.rects = rects;
@@ -219,21 +197,17 @@
}
};
- class SpannerInner : protected SpannerBase
- {
+ class SpannerInner : protected SpannerBase {
region lhs;
region rhs;
-
+
public:
- inline SpannerInner(const region& _lhs, const region& _rhs)
- : lhs(_lhs), rhs(_rhs)
- {
- }
+ inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {}
inline void prepare(int inside) {
if (inside == SpannerBase::lhs_before_rhs) {
if (lhs.count) {
- SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
}
SpannerBase::rhs_head = max_value;
@@ -242,28 +216,26 @@
SpannerBase::lhs_head = max_value;
SpannerBase::lhs_tail = max_value;
if (rhs.count) {
- SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
}
} else {
if (lhs.count) {
- SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
}
if (rhs.count) {
- SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
}
}
}
inline bool isDone() const {
- return SpannerBase::lhs_head == max_value &&
- SpannerBase::rhs_head == max_value;
+ return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value;
}
- inline int next(TYPE& left, TYPE& right)
- {
+ inline int next(TYPE& left, TYPE& right) {
bool more_lhs = false;
bool more_rhs = false;
int inside = SpannerBase::next(left, right, more_lhs, more_rhs);
@@ -277,17 +249,16 @@
}
private:
- static inline
- void advance(region& reg, TYPE& left, TYPE& right) {
+ static inline void advance(region& reg, TYPE& left, TYPE& right) {
if (reg.rects && reg.count) {
const int cur_span_top = reg.rects->top;
reg.rects++;
reg.count--;
if (!reg.count || reg.rects->top != cur_span_top) {
- left = max_value;
+ left = max_value;
right = max_value;
} else {
- left = reg.rects->left + reg.dx;
+ left = reg.rects->left + reg.dx;
right = reg.rects->right + reg.dx;
}
}
@@ -298,6 +269,6 @@
};
// ----------------------------------------------------------------------------
-};
+}; // namespace android
#endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 22c80a4..7660e9f 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -36,7 +36,7 @@
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),
+ buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e7640dd..1043390 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -46,7 +46,6 @@
}
void InputManager::initialize() {
- mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
@@ -57,9 +56,9 @@
return result;
}
- result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ result = mReader->start();
if (result) {
- ALOGE("Could not start InputReader thread due to error %d.", result);
+ ALOGE("Could not start InputReader due to error %d.", result);
mDispatcherThread->requestExit();
return result;
@@ -69,9 +68,9 @@
}
status_t InputManager::stop() {
- status_t result = mReaderThread->requestExitAndWait();
+ status_t result = mReader->stop();
if (result) {
- ALOGW("Could not stop InputReader thread due to error %d.", result);
+ ALOGW("Could not stop InputReader due to error %d.", result);
}
result = mDispatcherThread->requestExitAndWait();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 40f66d8..2a7ed0f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager uses two threads.
+ * The input manager has two components.
*
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- * applies policy, and posts messages to a queue managed by the DispatcherThread.
+ * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
+ * policy, and posts messages to a queue managed by the InputDispatcherThread.
* 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
* queue and asynchronously dispatches them to applications.
*
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state. Moreover, all communication is done one way from the InputReaderThread
+ * By design, the InputReader class and InputDispatcherThread class do not share any
+ * internal state. Moreover, all communication is done one way from the InputReader
* into the InputDispatcherThread and never the reverse. Both classes may interact with the
* InputDispatchPolicy, however.
*
@@ -102,7 +102,6 @@
private:
sp<InputReaderInterface> mReader;
- sp<InputReaderThread> mReaderThread;
sp<InputClassifierInterface> mClassifier;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 0422d83..2d6f2c1 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,20 +33,6 @@
namespace android {
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
-}
-
// --- InputReaderConfiguration ---
std::string InputReaderConfiguration::changesToString(uint32_t changes) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9c0e08e..b9bec44 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -298,6 +298,12 @@
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
+
+ // We are about to enter an infinitely long sleep, because we have no commands or
+ // pending or queued events
+ if (nextWakeupTime == LONG_LONG_MAX) {
+ mDispatcherEnteredIdle.notify_all();
+ }
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
@@ -4582,4 +4588,22 @@
mDispatcherIsAlive.wait(_l);
}
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+ /**
+ * Timeout should represent the longest possible time that a device might spend processing
+ * events and commands.
+ */
+ constexpr std::chrono::duration TIMEOUT = 100ms;
+ std::unique_lock lock(mLock);
+ mLooper->wake();
+ std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+ return result == std::cv_status::no_timeout;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5d7d812..38f8674 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,6 +81,7 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
+ virtual bool waitForIdle() override;
virtual void dispatchOnce() override;
@@ -129,6 +130,7 @@
std::mutex mLock;
std::condition_variable mDispatcherIsAlive;
+ std::condition_variable mDispatcherEnteredIdle;
sp<Looper> mLooper;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3082738..db9fba6 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -63,6 +63,14 @@
/* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
virtual void monitor() = 0;
+ /**
+ * Wait until dispatcher is idle. That means, there are no further events to be processed,
+ * and all of the policy callbacks have been completed.
+ * Return true if the dispatcher is idle.
+ * Return false if the timeout waiting for the dispatcher to become idle has expired.
+ */
+ virtual bool waitForIdle() = 0;
+
/* Runs a single iteration of the dispatch loop.
* Nominally processes one queued event, a timeout, or a response from an input consumer.
*
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5d576b9..56c0a73 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,12 +19,12 @@
#include "PointerControllerInterface.h"
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
-#include <input/DisplayViewport.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
-#include <utils/Thread.h>
+#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <stddef.h>
@@ -44,7 +44,16 @@
namespace android {
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
class InputReaderInterface : public virtual RefBase {
protected:
InputReaderInterface() { }
@@ -56,18 +65,17 @@
* This method may be called on any thread (usually by the input manager). */
virtual void dump(std::string& dump) = 0;
- /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+ /* Called by the heartbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
/* Returns true if the input device is enabled. */
virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
- /* Runs a single iteration of the processing loop.
- * Nominally reads and processes one incoming message from the EventHub.
- *
- * This method should be called on the input reader thread.
- */
- virtual void loopOnce() = 0;
+ /* Makes the reader start processing events from the kernel. */
+ virtual status_t start() = 0;
+
+ /* Makes the reader stop processing any more events. */
+ virtual status_t stop() = 0;
/* Gets information about all input devices.
*
@@ -104,17 +112,7 @@
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
};
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(const sp<InputReaderInterface>& reader);
- virtual ~InputReaderThread();
-
-private:
- sp<InputReaderInterface> mReader;
-
- virtual bool threadLoop();
-};
+// --- InputReaderConfiguration ---
/*
* Input reader configuration.
@@ -285,6 +283,8 @@
std::vector<DisplayViewport> mDisplays;
};
+// --- TouchAffineTransformation ---
+
struct TouchAffineTransformation {
float x_scale;
float x_ymix;
@@ -307,6 +307,8 @@
void applyTo(float& x, float& y) const;
};
+// --- InputReaderPolicyInterface ---
+
/*
* Input reader policy interface.
*
@@ -316,8 +318,8 @@
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
*/
class InputReaderPolicyInterface : public virtual RefBase {
protected:
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 1c5adc3..05f0db1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,16 +38,38 @@
#include <unistd.h>
#include <log/log.h>
+#include <utils/Errors.h>
#include <android-base/stringprintf.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
-
+#include <utils/Thread.h>
using android::base::StringPrintf;
namespace android {
+// --- InputReader::InputReaderThread ---
+
+/* Thread that reads raw events from the event hub and processes them, endlessly. */
+class InputReader::InputReaderThread : public Thread {
+public:
+ explicit InputReaderThread(InputReader* reader)
+ : Thread(/* canCallJava */ true), mReader(reader) {}
+
+ ~InputReaderThread() {}
+
+private:
+ InputReader* mReader;
+
+ bool threadLoop() override {
+ mReader->loopOnce();
+ return true;
+ }
+};
+
+// --- InputReader ---
+
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
@@ -61,6 +83,7 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
+ mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -76,6 +99,28 @@
}
}
+status_t InputReader::start() {
+ if (mThread->isRunning()) {
+ return ALREADY_EXISTS;
+ }
+ return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t InputReader::stop() {
+ if (!mThread->isRunning()) {
+ return OK;
+ }
+ if (gettid() == mThread->getTid()) {
+ ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+ return INVALID_OPERATION;
+ }
+ // Directly calling requestExitAndWait() causes the thread to not exit
+ // if mEventHub is waiting for a long timeout.
+ mThread->requestExit();
+ mEventHub->wake();
+ return mThread->requestExitAndWait();
+}
+
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 557eb3b..5024906 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -38,12 +38,12 @@
* that it sends to the input listener. Some functions of the input reader, such as early
* event filtering in low power states, are controlled by a separate policy object.
*
- * The InputReader owns a collection of InputMappers. Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
* components running on arbitrary threads. To keep things manageable, the InputReader
* uses a single Mutex to guard its state. The Mutex may be held while calling into the
* EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
*/
class InputReader : public InputReaderInterface {
public:
@@ -55,7 +55,8 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
- virtual void loopOnce() override;
+ virtual status_t start() override;
+ virtual status_t stop() override;
virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
@@ -86,6 +87,10 @@
const InputDeviceIdentifier& identifier,
uint32_t classes);
+ // With each iteration of the loop, InputReader reads and processes one incoming message from
+ // the EventHub.
+ void loopOnce();
+
class ContextImpl : public InputReaderContext {
InputReader* mReader;
@@ -111,6 +116,9 @@
friend class ContextImpl;
private:
+ class InputReaderThread;
+ sp<InputReaderThread> mThread;
+
Mutex mLock;
Condition mReaderIsAliveCondition;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 4f28261..c8d39cf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -51,7 +51,6 @@
public:
FakeInputDispatcherPolicy() {
- mOnPointerDownToken.clear();
}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
@@ -66,17 +65,41 @@
void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+ void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+ ASSERT_TRUE(mConfigurationChangedTime)
+ << "Timed out waiting for configuration changed call";
+ ASSERT_EQ(*mConfigurationChangedTime, when);
+ mConfigurationChangedTime = std::nullopt;
+ }
+
+ void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+ ASSERT_TRUE(mLastNotifySwitch);
+ // We do not check sequenceNum because it is not exposed to the policy
+ EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+ EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+ EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+ EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+ mLastNotifySwitch = std::nullopt;
+ }
+
void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
- ASSERT_EQ(mOnPointerDownToken, touchedToken)
- << "Expected token from onPointerDownOutsideFocus was not matched";
- reset();
+ ASSERT_EQ(touchedToken, mOnPointerDownToken);
+ mOnPointerDownToken.clear();
+ }
+
+ void assertOnPointerDownWasNotCalled() {
+ ASSERT_TRUE(mOnPointerDownToken == nullptr)
+ << "Expected onPointerDownOutsideFocus to not have been called";
}
private:
std::unique_ptr<InputEvent> mFilteredEvent;
+ std::optional<nsecs_t> mConfigurationChangedTime;
sp<IBinder> mOnPointerDownToken;
+ std::optional<NotifySwitchArgs> mLastNotifySwitch;
- virtual void notifyConfigurationChanged(nsecs_t) {
+ virtual void notifyConfigurationChanged(nsecs_t when) override {
+ mConfigurationChangedTime = when;
}
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
@@ -128,7 +151,13 @@
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) {
+ virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
+ /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+ * essentially a passthrough for notifySwitch.
+ */
+ mLastNotifySwitch =
+ NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask);
}
virtual void pokeUserActivity(nsecs_t, int32_t) {
@@ -163,8 +192,6 @@
mFilteredEvent = nullptr;
}
-
- void reset() { mOnPointerDownToken.clear(); }
};
@@ -351,9 +378,30 @@
<< "Should reject motion events with duplicate pointer ids.";
}
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+ constexpr nsecs_t eventTime = 20;
+ NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime);
+ mDispatcher->notifyConfigurationChanged(&args);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+
+ mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+ NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/,
+ 1 /*switchValues*/, 2 /*switchMask*/);
+ mDispatcher->notifySwitch(&args);
+
+ // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+ args.policyFlags |= POLICY_FLAG_TRUSTED;
+ mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
// --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
+static constexpr int32_t DISPATCHING_TIMEOUT = 100;
class FakeApplicationHandle : public InputApplicationHandle {
public:
@@ -536,9 +584,7 @@
InputWindowHandle::releaseChannel();
}
protected:
- virtual bool handled() {
- return true;
- }
+ virtual bool handled() override { return true; }
bool mFocused;
Rect mFrame;
@@ -761,6 +807,51 @@
windowRight->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocus();
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+ AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Window should receive motion down event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
+ 0 /*expectedFlags*/);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
@@ -924,7 +1015,7 @@
motionArgs = generateMotionArgs(
AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
mDispatcher->notifyMotion(&motionArgs);
-
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
} else {
@@ -939,6 +1030,7 @@
mDispatcher->notifyKey(&keyArgs);
keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
mDispatcher->notifyKey(&keyArgs);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
@@ -1029,9 +1121,8 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
}
@@ -1042,10 +1133,9 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1053,10 +1143,9 @@
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1068,10 +1157,9 @@
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c1c9122..d6624c9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1113,6 +1113,9 @@
classes);
}
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
protected:
virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier,
@@ -1133,12 +1136,8 @@
protected:
sp<FakeInputReaderPolicy> mFakePolicy;
- virtual void SetUp() {
- mFakePolicy = new FakeInputReaderPolicy();
- }
- virtual void TearDown() {
- mFakePolicy.clear();
- }
+ virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+ virtual void TearDown() override { mFakePolicy.clear(); }
};
/**
@@ -1321,19 +1320,18 @@
sp<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<InstrumentedInputReader> mReader;
+ std::unique_ptr<InstrumentedInputReader> mReader;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
}
- virtual void TearDown() {
- mReader.clear();
-
+ virtual void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -1354,16 +1352,12 @@
void disableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->addDisabledDevice(deviceId);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
void enableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->removeDisabledDevice(deviceId);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
- }
-
- void configureDevice(uint32_t changes, InputDevice* device) {
- device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1417,7 +1411,6 @@
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
@@ -1425,7 +1418,6 @@
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), false);
@@ -1438,7 +1430,6 @@
enableDevice(deviceId, device);
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
}
@@ -1629,7 +1620,6 @@
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
device->addMapper(mapper);
mReader->setNextDevice(device);
- addDevice(deviceId, "fake", deviceClass, nullptr);
const uint8_t hdmi1 = 1;
@@ -1637,13 +1627,21 @@
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
// Add default and second display.
+ mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
+
+ // Add the device, and make sure all of the callbacks are triggered.
+ // The device is added after the input port associations are processed since
+ // we do not yet support dynamic device-to-display associations.
+ ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
// Device should only dispatch to the specified display.
ASSERT_EQ(deviceId, device->getId());
@@ -1652,6 +1650,7 @@
// Can't dispatch event from a disabled device.
disableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
@@ -1674,7 +1673,7 @@
InputDevice* mDevice;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1688,7 +1687,7 @@
DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
delete mDevice;
delete mFakeContext;
@@ -1912,7 +1911,7 @@
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1926,7 +1925,7 @@
mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
delete mDevice;
delete mFakeContext;
mFakeListener.clear();
@@ -2589,7 +2588,7 @@
sp<FakePointerController> mFakePointerController;
- virtual void SetUp() {
+ virtual void SetUp() override {
InputMapperTest::SetUp();
mFakePointerController = new FakePointerController();
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index d51d34b..945abd7 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
BufferQueueLayer::~BufferQueueLayer() {
+ mContentsChangedListener->abandon();
mConsumer->abandon();
}
@@ -399,8 +400,7 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- const bool isHDR = item.mHdrMetadata.validTypes != 0;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime, isHDR);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
@@ -480,7 +480,9 @@
mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
mTextureName, this);
mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
- mConsumer->setContentsChangedListener(this);
+
+ mContentsChangedListener = new ContentsChangedListener(this);
+ mConsumer->setContentsChangedListener(mContentsChangedListener);
mConsumer->setName(String8(mName.data(), mName.size()));
// BufferQueueCore::mMaxDequeuedBufferCount is default to 1
@@ -552,4 +554,57 @@
return layer;
}
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameAvailable(item);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameReplaced(item);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onSidebandStreamChanged();
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameDequeued(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameDetached(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameCancelled(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::abandon() {
+ Mutex::Autolock lock(mMutex);
+ mBufferQueueLayer = nullptr;
+}
+
+// -----------------------------------------------------------------------
+
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 1b1fccd..b040556 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -29,7 +29,7 @@
* This also implements onFrameAvailable(), which notifies SurfaceFlinger
* that new data has arrived.
*/
-class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferQueueLayer : public BufferLayer {
public:
// Only call while mStateLock is held
explicit BufferQueueLayer(const LayerCreationArgs&);
@@ -84,18 +84,37 @@
void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
sp<Layer> createClone() override;
- // -----------------------------------------------------------------------
- // Interface implementation for BufferLayerConsumer::ContentsChangedListener
- // -----------------------------------------------------------------------
+ void onFrameAvailable(const BufferItem& item);
+ void onFrameReplaced(const BufferItem& item);
+ void onSidebandStreamChanged();
+ void onFrameDequeued(const uint64_t bufferId);
+ void onFrameDetached(const uint64_t bufferId);
+ void onFrameCancelled(const uint64_t bufferId);
+
protected:
void gatherBufferInfo() override;
- void onFrameAvailable(const BufferItem& item) override;
- void onFrameReplaced(const BufferItem& item) override;
- void onSidebandStreamChanged() override;
- void onFrameDequeued(const uint64_t bufferId) override;
- void onFrameDetached(const uint64_t bufferId) override;
- void onFrameCancelled(const uint64_t bufferId) override;
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayerConsumer::ContentsChangedListener
+ // -----------------------------------------------------------------------
+ class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
+ public:
+ ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
+ : mBufferQueueLayer(bufferQueueLayer) {}
+ void abandon();
+
+ protected:
+ void onFrameAvailable(const BufferItem& item) override;
+ void onFrameReplaced(const BufferItem& item) override;
+ void onSidebandStreamChanged() override;
+ void onFrameDequeued(const uint64_t bufferId) override;
+ void onFrameDetached(const uint64_t bufferId) override;
+ void onFrameCancelled(const uint64_t bufferId) override;
+
+ private:
+ BufferQueueLayer* mBufferQueueLayer = nullptr;
+ Mutex mMutex;
+ };
// -----------------------------------------------------------------------
public:
@@ -130,6 +149,8 @@
// thread-safe
std::atomic<int32_t> mQueuedFrames{0};
std::atomic<bool> mSidebandStreamChanged{false};
+
+ sp<ContentsChangedListener> mContentsChangedListener;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index d68fe8e..1e471e5 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -247,8 +247,7 @@
FrameTracer::FrameEvent::POST);
mCurrentState.desiredPresentTime = desiredPresentTime;
- const bool isHDR = mCurrentState.hdrMetadata.validTypes != 0;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime, isHDR);
+ mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
return true;
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 738a2a4..78f8104 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -20,6 +20,7 @@
"liblayers_proto",
"liblog",
"libnativewindow",
+ "libprotobuf-cpp-lite",
"libsync",
"libtimestats_proto",
"libui",
@@ -28,6 +29,7 @@
static_libs: [
"libmath",
"librenderengine",
+ "libtimestats",
"libtrace_proto",
],
header_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 8687d0c..e3650f3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -16,10 +16,11 @@
#pragma once
-#include <memory>
-
+#include <TimeStats/TimeStats.h>
#include <utils/Timers.h>
+#include <memory>
+
namespace android {
class HWComposer;
@@ -55,6 +56,9 @@
virtual renderengine::RenderEngine& getRenderEngine() const = 0;
virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+ virtual TimeStats& getTimeStats() const = 0;
+ virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
+
virtual bool needsAnotherUpdate() const = 0;
virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e585769..0a70165 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -93,6 +93,13 @@
virtual std::optional<renderengine::LayerSettings> prepareClientComposition(
ClientCompositionTargetSettings&) = 0;
+ // Returns the LayerSettings used to draw shadows around a layer. It is passed
+ // to RenderEngine::drawLayers. Returns nullopt_t if the layer does not render
+ // shadows.
+ virtual std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+ const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) = 0;
+
// Called after the layer is displayed to update the presentation fence
virtual void onLayerDisplayed(const sp<Fence>&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 5f42ea1..076fdad 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -272,7 +272,7 @@
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
virtual std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) = 0;
+ bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
virtual void appendRegionFlashRequests(
const Region& flashRegion,
std::vector<renderengine::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index f416c9c..450b9ca 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -36,6 +36,9 @@
renderengine::RenderEngine& getRenderEngine() const override;
void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+ TimeStats& getTimeStats() const override;
+ void setTimeStats(const std::shared_ptr<TimeStats>&) override;
+
bool needsAnotherUpdate() const override;
nsecs_t getLastFrameRefreshTimestamp() const override;
@@ -56,6 +59,7 @@
private:
std::unique_ptr<HWComposer> mHwComposer;
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+ std::shared_ptr<TimeStats> mTimeStats;
bool mNeedsAnotherUpdate = false;
nsecs_t mRefreshStartTime = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a2342ae..159e928 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -97,7 +97,8 @@
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) override;
+ bool supportsProtectedContent, Region& clearRegion,
+ ui::Dataspace outputDataspace) override;
void appendRegionFlashRequests(const Region&,
std::vector<renderengine::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 8e6f2e2..104e20d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -41,6 +41,9 @@
MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+ MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+ MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
+
MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 3eada3c..739490f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -38,6 +38,10 @@
MOCK_METHOD1(prepareClientComposition,
std::optional<renderengine::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
+ MOCK_METHOD3(prepareShadowClientComposition,
+ std::optional<renderengine::LayerSettings>(const renderengine::LayerSettings&,
+ const Rect&, ui::Dataspace));
+
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 02e68fc..7f5bd06 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -106,8 +106,8 @@
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
- MOCK_METHOD2(generateClientCompositionRequests,
- std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD3(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index be8646c..5eabecd 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -64,6 +64,14 @@
mRenderEngine = std::move(renderEngine);
}
+TimeStats& CompositionEngine::getTimeStats() const {
+ return *mTimeStats.get();
+}
+
+void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+ mTimeStats = timeStats;
+}
+
bool CompositionEngine::needsAnotherUpdate() const {
return mNeedsAnotherUpdate;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6877f8b..7e5a720 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -799,7 +799,8 @@
// Generate the client composition requests for the layers on this output.
std::vector<renderengine::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
- clientCompositionDisplay.clearRegion);
+ clientCompositionDisplay.clearRegion,
+ clientCompositionDisplay.outputDataspace);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
// If we the display is secure, protected content support is enabled, and at
@@ -838,9 +839,18 @@
setExpensiveRenderingExpected(true);
}
+ const nsecs_t renderEngineStart = systemTime();
renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
&readyFence);
+ auto& timeStats = getCompositionEngine().getTimeStats();
+ if (readyFence.get() < 0) {
+ timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+ } else {
+ timeStats.recordRenderEngineDuration(renderEngineStart,
+ std::make_shared<FenceTime>(
+ new Fence(dup(readyFence.get()))));
+ }
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(false);
@@ -850,7 +860,7 @@
}
std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) {
+ bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
std::vector<renderengine::LayerSettings> clientCompositionLayers;
ALOGV("Rendering client layers");
@@ -902,6 +912,13 @@
layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
layerSettings.alpha = half(0.0);
layerSettings.disableBlending = true;
+ } else {
+ std::optional<renderengine::LayerSettings> shadowLayer =
+ layerFE.prepareShadowClientComposition(*result, outputState.viewport,
+ outputDataspace);
+ if (shadowLayer) {
+ clientCompositionLayers.push_back(*shadowLayer);
+ }
}
layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 49e7c70..989494d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -25,6 +25,7 @@
#include <renderengine/mock/RenderEngine.h>
#include "MockHWComposer.h"
+#include "TimeStats/TimeStats.h"
namespace android::compositionengine {
namespace {
@@ -41,6 +42,8 @@
android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
renderengine::mock::RenderEngine* mRenderEngine =
new StrictMock<renderengine::mock::RenderEngine>();
+ std::shared_ptr<TimeStats> mTimeStats;
+
impl::CompositionEngine mEngine;
CompositionRefreshArgs mRefreshArgs;
@@ -71,6 +74,12 @@
EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
}
+TEST_F(CompositionEngineTest, canSetTimeStats) {
+ mEngine.setTimeStats(mTimeStats);
+
+ EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+}
+
/*
* CompositionEngine::present
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 110760c..37b62d8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -817,7 +817,7 @@
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
- compositionengine::LayerFESet mGeomSnapshots;
+ LayerFESet mGeomSnapshots;
};
TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
@@ -866,7 +866,7 @@
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
- compositionengine::LayerFESet mGeomSnapshots;
+ LayerFESet mGeomSnapshots;
Region mCoverageAboveCoveredLayersToSet;
Region mCoverageAboveOpaqueLayersToSet;
Region mCoverageDirtyRegionToSet;
@@ -992,8 +992,8 @@
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
- compositionengine::LayerFESet mGeomSnapshots;
- compositionengine::Output::CoverageState mCoverageState{mGeomSnapshots};
+ LayerFESet mGeomSnapshots;
+ Output::CoverageState mCoverageState{mGeomSnapshots};
Layer mLayer1;
Layer mLayer2;
Layer mLayer3;
@@ -1033,7 +1033,408 @@
* Output::ensureOutputLayerIfVisible()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*));
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(
+ std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ };
+
+ OutputEnsureOutputLayerIfVisibleTest() {
+ EXPECT_CALL(*mLayer, getLayerFE()).WillRepeatedly(Return(mLayerFE));
+ EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ EXPECT_CALL(*mLayer, editFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+
+ EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mOutputLayer));
+
+ EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(*mLayer.get()));
+
+ mOutput.mState.bounds = Rect(0, 0, 200, 300);
+ mOutput.mState.viewport = Rect(0, 0, 200, 300);
+ mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
+
+ mLayerFEState.isVisible = true;
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
+
+ mOutputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
+ mOutputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
+
+ mGeomSnapshots.insert(mLayerFE);
+ }
+
+ static const Region kEmptyRegion;
+ static const Region kFullBoundsNoRotation;
+ static const Region kRightHalfBoundsNoRotation;
+ static const Region kLowerHalfBoundsNoRotation;
+ static const Region kFullBounds90Rotation;
+
+ StrictMock<OutputPartialMock> mOutput;
+ LayerFESet mGeomSnapshots;
+ Output::CoverageState mCoverageState{mGeomSnapshots};
+
+ std::shared_ptr<mock::Layer> mLayer{new StrictMock<mock::Layer>()};
+ sp<StrictMock<mock::LayerFE>> mLayerFE{new StrictMock<mock::LayerFE>()};
+ LayerFECompositionState mLayerFEState;
+ mock::OutputLayer mOutputLayer;
+ impl::OutputLayerCompositionState mOutputLayerState;
+};
+
+const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBoundsNoRotation =
+ Region(Rect(0, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kRightHalfBoundsNoRotation =
+ Region(Rect(0, 100, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation =
+ Region(Rect(50, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
+ Region(Rect(0, 0, 200, 100));
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, doesNothingIfNoLayerFE) {
+ EXPECT_CALL(*mLayer, getLayerFE).WillOnce(Return(sp<LayerFE>()));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
+ EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+ EXPECT_CALL(*mLayerFE.get(),
+ latchCompositionState(Ref(mLayerFEState),
+ compositionengine::LayerFE::StateSubset::BasicGeometry));
+
+ mGeomSnapshots.clear();
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
+ EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) {
+ mLayerFEState.isVisible = false;
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) {
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
+ mOutput.mState.bounds = Rect(0, 0, 0, 0);
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = false;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kRightHalfBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = false;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kRightHalfBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = false;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = false;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+ mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+ mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+ mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+ mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyArbitraryTransformLayer) {
+ ui::Transform arbitraryTransform;
+ arbitraryTransform.set(1, 1, -1, 1);
+ arbitraryTransform.set(0, 100);
+
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+ mLayerFEState.geomLayerTransform = arbitraryTransform;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kRegion = Region(Rect(0, 0, 300, 300));
+ const Region kRegionClipped = Region(Rect(0, 0, 200, 300));
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kRegion));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kRegion));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) {
+ mLayerFEState.isOpaque = false;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+ const Region kExpectedAboveCoveredRegion = Region(Rect(0, 0, 150, 200));
+ const Region kExpectedAboveOpaqueRegion = Region(Rect(50, 0, 150, 200));
+ const Region kExpectedLayerVisibleRegion = Region(Rect(0, 0, 50, 200));
+ const Region kExpectedLayerCoveredRegion = Region(Rect(50, 0, 100, 200));
+ const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(0, 100, 50, 200));
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+}
/*
* Output::present()
@@ -2171,7 +2572,7 @@
sp<Fence> layer2Fence = new Fence();
sp<Fence> layer3Fence = new Fence();
- compositionengine::Output::FrameFences frameFences;
+ Output::FrameFences frameFences;
frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
@@ -2202,7 +2603,7 @@
sp<Fence> layer1Fence = new Fence();
sp<Fence> layer2Fence = new Fence();
sp<Fence> layer3Fence = new Fence();
- compositionengine::Output::FrameFences frameFences;
+ Output::FrameFences frameFences;
frameFences.clientTargetAcquireFence = clientTargetAcquireFence;
frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
@@ -2241,7 +2642,7 @@
// Set up a fake present fence
sp<Fence> presentFence = new Fence();
- compositionengine::Output::FrameFences frameFences;
+ Output::FrameFences frameFences;
frameFences.presentFence = presentFence;
EXPECT_CALL(*mRenderSurface, flip());
@@ -2279,8 +2680,8 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD2(generateClientCompositionRequests,
- std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD3(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -2310,10 +2711,14 @@
.WillRepeatedly(Return(&mOutputLayer2));
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+ EXPECT_CALL(mCompositionEngine, getTimeStats())
+ .WillRepeatedly(ReturnRef(*mTimeStats.get()));
}
StrictMock<mock::CompositionEngine> mCompositionEngine;
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+ // TODO: make this is a proper mock.
+ std::shared_ptr<TimeStats> mTimeStats = std::make_shared<android::impl::TimeStats>();
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
StrictMock<mock::OutputLayer> mOutputLayer1;
@@ -2358,7 +2763,7 @@
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _)).Times(1);
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _, _)).Times(1);
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1);
@@ -2375,9 +2780,10 @@
struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) override {
+ bool supportsProtectedContent, Region& clearRegion,
+ ui::Dataspace dataspace) override {
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
- clearRegion);
+ clearRegion, dataspace);
}
};
@@ -2436,6 +2842,8 @@
EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
+ EXPECT_CALL(leftLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
@@ -2445,6 +2853,8 @@
EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+ EXPECT_CALL(rightLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
@@ -2468,8 +2878,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
@@ -2518,8 +2928,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -2596,8 +3006,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
const half3 clearColor{0.f, 0.f, 0.f};
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 89123df..84ec597 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -132,11 +132,11 @@
}
// ----------------------------------------------------------------------------
-void DisplayDevice::setActiveConfig(int mode) {
+void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) {
mActiveConfig = mode;
}
-int DisplayDevice::getActiveConfig() const {
+HwcConfigIndexType DisplayDevice::getActiveConfig() const {
return mActiveConfig;
}
@@ -285,7 +285,7 @@
result.append(" ");
StringAppendF(&result, "powerMode=%d, ", mPowerMode);
- StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
+ StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
getCompositionDisplay()->dump(result);
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ce4e1e6..79a1185 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -43,6 +43,7 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "RenderArea.h"
+#include "Scheduler/HwcStrongTypes.h"
namespace android {
@@ -141,8 +142,8 @@
/* ------------------------------------------------------------------------
* Display active config management.
*/
- int getActiveConfig() const;
- void setActiveConfig(int mode);
+ HwcConfigIndexType getActiveConfig() const;
+ void setActiveConfig(HwcConfigIndexType mode);
// release HWC resources (if any) for removable displays
void disconnect();
@@ -186,7 +187,7 @@
// Current power mode
int mPowerMode;
// Current active config
- int mActiveConfig;
+ HwcConfigIndexType mActiveConfig;
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
@@ -246,6 +247,7 @@
uint32_t reqHeight, ui::Dataspace reqDataSpace,
ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
: RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
+ device->getViewport(),
getDisplayRotation(rotation, device->getInstallOrientation())),
mDevice(device),
mSourceCrop(sourceCrop),
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3d00352..35fc4be 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -571,6 +571,53 @@
return layerSettings;
}
+std::optional<renderengine::LayerSettings> Layer::prepareShadowClientComposition(
+ const renderengine::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) {
+ renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+ if (shadow.length <= 0.f) {
+ return {};
+ }
+
+ const float casterAlpha = casterLayerSettings.alpha;
+ const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
+ casterLayerSettings.source.buffer.isOpaque);
+
+ renderengine::LayerSettings shadowLayer = casterLayerSettings;
+ shadowLayer.shadow = shadow;
+
+ // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
+ // Otherwise the generated shadow will only be shown around the casting layer.
+ shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+ shadowLayer.shadow.ambientColor *= casterAlpha;
+ shadowLayer.shadow.spotColor *= casterAlpha;
+ shadowLayer.sourceDataspace = outputDataspace;
+ shadowLayer.source.buffer.buffer = nullptr;
+
+ if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
+ return {};
+ }
+
+ float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
+ const FloatRect& cornerRadiusCropRect = casterLayerSettings.geometry.roundedCornersCrop;
+ const FloatRect& casterRect = shadowLayer.geometry.boundaries;
+
+ // crop used to set the corner radius may be larger than the content rect. Adjust the corner
+ // radius accordingly.
+ if (casterCornerRadius > 0.f) {
+ float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
+ std::abs(cornerRadiusCropRect.left - casterRect.left));
+ if (cropRectOffset > casterCornerRadius) {
+ casterCornerRadius = 0;
+ } else {
+ casterCornerRadius -= cropRectOffset;
+ }
+ shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
+ }
+
+ return shadowLayer;
+}
+
Hwc2::IComposerClient::Composition Layer::getCompositionType(
const sp<const DisplayDevice>& display) const {
const auto outputLayer = findOutputLayerForDisplay(display);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3c92c22..843d3ae 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -510,6 +510,9 @@
void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
std::optional<renderengine::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+ std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+ const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) override;
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
const char* getDebugName() const override;
@@ -713,6 +716,14 @@
Region debugGetVisibleRegionOnDefaultDisplay() const;
+ /**
+ * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+ * INVALID_RECT if the layer has no buffer and no crop.
+ * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+ * bounds are constrained by its parent bounds.
+ */
+ Rect getCroppedBufferSize(const Layer::State& s) const;
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -921,13 +932,6 @@
const LayerVector::Visitor& visitor);
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
- /**
- * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
- * INVALID_RECT if the layer has no buffer and no crop.
- * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
- * bounds are constrained by its parent bounds.
- */
- Rect getCroppedBufferSize(const Layer::State& s) const;
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 976fedb..38a80a7 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -20,8 +20,6 @@
namespace android {
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-
RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
: mFlinger(flinger), mClient(new Client(&mFlinger)) {
createLayer();
@@ -51,8 +49,8 @@
return true;
}
-void RefreshRateOverlay::changeRefreshRate(RefreshRateType type) {
- const half3& color = (type == RefreshRateType::PERFORMANCE) ? GREEN : RED;
+void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
+ const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED;
mLayer->setColor(color);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index ce29bc3..414bc47 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,13 +19,13 @@
namespace android {
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
class RefreshRateOverlay {
public:
RefreshRateOverlay(SurfaceFlinger& flinger);
- void changeRefreshRate(RefreshRateType type);
+ void changeRefreshRate(const RefreshRate& refreshRate);
private:
bool createLayer();
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index edc6442..532572f 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -22,13 +22,14 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace,
+ ui::Dataspace reqDataSpace, const Rect& displayViewport,
ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: mReqWidth(reqWidth),
mReqHeight(reqHeight),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
- mRotationFlags(rotation) {}
+ mRotationFlags(rotation),
+ mDisplayViewport(displayViewport) {}
virtual ~RenderArea() = default;
@@ -80,12 +81,16 @@
virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
+ // Returns the source display viewport.
+ const Rect& getDisplayViewport() const { return mDisplayViewport; }
+
private:
const uint32_t mReqWidth;
const uint32_t mReqHeight;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const ui::Transform::orientation_flags mRotationFlags;
+ const Rect mDisplayViewport;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 8d9adc8..ff800c3 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -36,6 +36,7 @@
#include <utils/Trace.h>
#include "EventThread.h"
+#include "HwcStrongTypes.h"
using namespace std::chrono_literals;
@@ -101,10 +102,11 @@
return event;
}
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
+ HwcConfigIndexType configId) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
- event.config.configId = configId;
+ event.config.configId = configId.value();
return event;
}
@@ -290,7 +292,7 @@
mCondition.notify_all();
}
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeConfigChanged(displayId, configId));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a029586..a42546c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -33,6 +33,7 @@
#include <private/gui/BitTube.h>
#include <utils/Errors.h>
+#include "HwcStrongTypes.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -109,7 +110,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active config and apps needs to be notified about the change
- virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+ virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) = 0;
virtual void dump(std::string& result) const = 0;
@@ -146,7 +147,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+ void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) override;
void dump(std::string& result) const override;
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
new file mode 100644
index 0000000..cfbbdfe
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -0,0 +1,27 @@
+/*
+ * 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 "StrongTyping.h"
+
+namespace android {
+
+// Strong types for the different indexes as they are referring to a different base.
+using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
+using HwcConfigGroupType = StrongTyping<int, struct HwcConfigGroupTypeTag, Compare>;
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8b71728..146ec1b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- return layer.isVisible() && (info.isHDR() || info.getLastUpdatedTime() >= threshold);
+ return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
}
bool traceEnabled() {
@@ -69,7 +69,7 @@
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, bool isHDR, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -78,7 +78,6 @@
const auto& info = it->second;
info->setLastPresentTime(presentTime, now);
- info->setIsHDR(isHDR);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -89,7 +88,6 @@
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
float maxRefreshRate = 0;
- bool isHDR = false;
std::lock_guard lock(mLock);
@@ -108,13 +106,12 @@
trace(layer, std::round(refreshRate));
}
}
- isHDR |= info->isHDR();
}
if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("%s: maxRefreshRate=%.2f, isHDR=%d", __FUNCTION__, maxRefreshRate, isHDR);
+ ALOGD("%s: maxRefreshRate=%.2f", __FUNCTION__, maxRefreshRate);
}
- return {maxRefreshRate, isHDR};
+ return {maxRefreshRate};
}
void LayerHistory::partitionLayers(nsecs_t now) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bd9aca1..745c4c1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -46,11 +46,10 @@
void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate);
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, bool isHDR, nsecs_t now);
+ void record(Layer*, nsecs_t presentTime, nsecs_t now);
struct Summary {
float maxRefreshRate; // Maximum refresh rate among recently active layers.
- bool isHDR; // True if any recently active layer has HDR content.
};
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index b86709f..cb81ca2 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -140,9 +140,6 @@
// updated time, the updated time is the present time.
void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
- bool isHDR() const { return mIsHDR; }
- void setIsHDR(bool isHDR) { mIsHDR = isHDR; }
-
bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
@@ -167,7 +164,6 @@
nsecs_t mLastPresentTime = 0;
RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
PresentTimeHistory mPresentTimeHistory;
- bool mIsHDR = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 6be88f8..12832a6 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -48,16 +48,18 @@
getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
.value_or(std::numeric_limits<nsecs_t>::max());
- const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
- const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
-
- mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
- mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
+ mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync);
+ mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
}
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
- RefreshRateType refreshRateType) const {
- return mOffsets.at(refreshRateType);
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
+ // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic
+ // number for refresh rate
+ if (fps > 65.0f) {
+ return mHighFpsOffsets;
+ } else {
+ return mDefaultOffsets;
+ }
}
void PhaseOffsets::dump(std::string& result) const {
@@ -80,13 +82,13 @@
const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
- return {{RefreshRateType::DEFAULT, earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+ return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
- {RefreshRateType::DEFAULT, earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+ {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
- {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+ {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
thresholdForNextVsync};
}
@@ -104,13 +106,13 @@
const auto highFpsEarlyGlAppOffsetNs =
getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
- return {{RefreshRateType::PERFORMANCE, highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
+ return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
- {RefreshRateType::PERFORMANCE, highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
+ {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
- {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+ {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
thresholdForNextVsync};
}
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 2c52432..7747f0c 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -32,7 +32,6 @@
class PhaseOffsets {
public:
using Offsets = VSyncModulator::OffsetsConfig;
- using RefreshRateType = RefreshRateConfigs::RefreshRateType;
virtual ~PhaseOffsets();
@@ -43,9 +42,9 @@
}
virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(RefreshRateType) const = 0;
+ virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
- virtual void setRefreshRateType(RefreshRateType) = 0;
+ virtual void setRefreshRateFps(float fps) = 0;
virtual void dump(std::string& result) const = 0;
};
@@ -57,18 +56,14 @@
PhaseOffsets();
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(RefreshRateType) const override;
+ Offsets getOffsetsForRefreshRate(float fps) const override;
// Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override {
- return getOffsetsForRefreshRate(mRefreshRateType);
- }
+ Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
// This function should be called when the device is switching between different
// refresh rates, to properly update the offsets.
- void setRefreshRateType(RefreshRateType refreshRateType) override {
- mRefreshRateType = refreshRateType;
- }
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
@@ -77,9 +72,10 @@
static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
- std::atomic<RefreshRateType> mRefreshRateType = RefreshRateType::DEFAULT;
+ std::atomic<float> mRefreshRateFps = 0;
- std::unordered_map<RefreshRateType, Offsets> mOffsets;
+ Offsets mDefaultOffsets;
+ Offsets mHighFpsOffsets;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 7dc98cc..23fb96a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -13,135 +13,169 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+// #define LOG_NDEBUG 0
#include "RefreshRateConfigs.h"
namespace android::scheduler {
+
+using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
// 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, RefreshRate>& RefreshRateConfigs::getRefreshRateMap() const {
- LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
- return mRefreshRateMap;
-}
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFramerate) const {
+ std::lock_guard lock(mLock);
+ // Find the appropriate refresh rate with minimal error
+ auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
+ [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
+ return std::abs(lhs->fps - contentFramerate) <
+ std::abs(rhs->fps - contentFramerate);
+ });
-const RefreshRate& RefreshRateConfigs::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;
- }
-}
+ // 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
+ const RefreshRate* bestSoFar = *iter;
+ constexpr float MARGIN = 0.05f;
+ float ratio = (*iter)->fps / contentFramerate;
+ if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+ while (iter != mAvailableRefreshRates.cend()) {
+ ratio = (*iter)->fps / contentFramerate;
-std::pair<RefreshRateType, const RefreshRate&> RefreshRateConfigs::getCurrentRefreshRate() const {
- int currentConfig = mCurrentConfig;
- if (mRefreshRateSwitchingSupported) {
- for (const auto& [type, refresh] : mRefreshRateMap) {
- if (refresh.configId == currentConfig) {
- return {type, refresh};
+ if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+ bestSoFar = *iter;
+ break;
}
- }
- LOG_ALWAYS_FATAL();
- }
- return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
-}
-
-const RefreshRate& RefreshRateConfigs::getRefreshRateFromConfigId(int configId) const {
- LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
- return mRefreshRates[configId];
-}
-
-RefreshRateType RefreshRateConfigs::getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
- if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
-
- for (const auto& [type, refreshRate] : mRefreshRateMap) {
- if (refreshRate.hwcId == hwcId) {
- return type;
+ ++iter;
}
}
- return RefreshRateType::DEFAULT;
+ return *bestSoFar;
}
-void RefreshRateConfigs::setCurrentConfig(int config) {
- LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
- mCurrentConfig = config;
+const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
+ return mRefreshRates;
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.front();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.back();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return *mCurrentRefreshRate;
+}
+
+void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+ std::lock_guard lock(mLock);
+ mCurrentRefreshRate = &mRefreshRates.at(configId);
}
RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
- const std::vector<InputConfig>& configs, int currentConfig) {
- init(refreshRateSwitching, configs, currentConfig);
+ const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig)
+ : mRefreshRateSwitching(refreshRateSwitching) {
+ init(configs, currentHwcConfig);
}
RefreshRateConfigs::RefreshRateConfigs(
bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig) {
+ HwcConfigIndexType currentConfigId)
+ : mRefreshRateSwitching(refreshRateSwitching) {
std::vector<InputConfig> inputConfigs;
- for (const auto& config : configs) {
- inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
+ for (auto configId = HwcConfigIndexType(0); configId < HwcConfigIndexType(configs.size());
+ ++configId) {
+ auto configGroup = HwcConfigGroupType(configs[configId.value()]->getConfigGroup());
+ inputConfigs.push_back(
+ {configId, configGroup, configs[configId.value()]->getVsyncPeriod()});
}
- init(refreshRateSwitching, inputConfigs, currentConfig);
+ init(inputConfigs, currentConfigId);
}
-void RefreshRateConfigs::init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- mRefreshRateSwitchingSupported = refreshRateSwitching;
+void RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+ float maxRefreshRate) {
+ std::lock_guard lock(mLock);
+ mCurrentGroupId = mRefreshRates.at(defaultConfigId).configGroup;
+ mMinRefreshRateFps = minRefreshRate;
+ mMaxRefreshRateFps = maxRefreshRate;
+ constructAvailableRefreshRates();
+}
+
+void RefreshRateConfigs::getSortedRefreshRateList(
+ const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+ std::vector<const RefreshRate*>* outRefreshRates) {
+ outRefreshRates->clear();
+ outRefreshRates->reserve(mRefreshRates.size());
+ for (const auto& [type, refreshRate] : mRefreshRates) {
+ if (shouldAddRefreshRate(refreshRate)) {
+ ALOGV("getSortedRefreshRateList: config %d added to list policy",
+ refreshRate.configId.value());
+ outRefreshRates->push_back(&refreshRate);
+ }
+ }
+
+ std::sort(outRefreshRates->begin(), outRefreshRates->end(),
+ [](const auto refreshRate1, const auto refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+}
+
+void RefreshRateConfigs::constructAvailableRefreshRates() {
+ // Filter configs based on current policy and sort based on vsync period
+ ALOGV("constructRefreshRateMap: group %d min %.2f max %.2f", mCurrentGroupId.value(),
+ mMinRefreshRateFps, mMaxRefreshRateFps);
+ getSortedRefreshRateList(
+ [this](const RefreshRate& refreshRate) REQUIRES(mLock) {
+ return refreshRate.configGroup == mCurrentGroupId &&
+ refreshRate.fps >= mMinRefreshRateFps &&
+ refreshRate.fps <= mMaxRefreshRateFps;
+ },
+ &mAvailableRefreshRates);
+}
+
+// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
+void RefreshRateConfigs::init(const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig) NO_THREAD_SAFETY_ANALYSIS {
LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
- mCurrentConfig = currentConfig;
+ LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
- 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};
+ auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
+ const float fps = 1e9f / config.vsyncPeriod;
+ return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
+ base::StringPrintf("%2.ffps", fps), fps);
};
- for (int i = 0; i < configs.size(); ++i) {
- mRefreshRates.push_back(buildRefreshRate(i));
+ for (const auto& config : configs) {
+ mRefreshRates.emplace(config.configId, buildRefreshRate(config));
+ if (config.configId == currentHwcConfig) {
+ mCurrentRefreshRate = &mRefreshRates.at(config.configId);
+ mCurrentGroupId = config.configGroup;
+ }
}
- 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];
+ std::vector<const RefreshRate*> sortedConfigs;
+ getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ mMinSupportedRefreshRate = sortedConfigs.front();
+ mMaxSupportedRefreshRate = sortedConfigs.back();
+ constructAvailableRefreshRates();
}
-} // namespace android::scheduler
\ No newline at end of file
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 90bba24..fb14dc7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -23,7 +23,9 @@
#include <type_traits>
#include "DisplayHardware/HWComposer.h"
+#include "HwcStrongTypes.h"
#include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
@@ -41,71 +43,123 @@
*/
class RefreshRateConfigs {
public:
- // 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 { DEFAULT, PERFORMANCE };
-
struct RefreshRate {
+ RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
+ HwcConfigGroupType configGroup, std::string name, float fps)
+ : configId(configId),
+ vsyncPeriod(vsyncPeriod),
+ configGroup(configGroup),
+ name(std::move(name)),
+ fps(fps) {}
// This config ID corresponds to the position of the config in the vector that is stored
// on the device.
- int configId;
- // Human readable name of the refresh rate.
- std::string name;
- // Refresh rate in frames per second, rounded to the nearest integer.
- uint32_t fps = 0;
+ const HwcConfigIndexType configId;
// Vsync period in nanoseconds.
- nsecs_t vsyncPeriod;
- // Hwc config Id (returned from HWC2::Display::Config::getId())
- hwc2_config_t hwcId;
+ const nsecs_t vsyncPeriod;
+ // This configGroup for the config.
+ const HwcConfigGroupType configGroup;
+ // Human readable name of the refresh rate.
+ const std::string name;
+ // Refresh rate in frames per second
+ const float fps = 0;
+
+ bool operator!=(const RefreshRate& other) const {
+ return configId != other.configId || vsyncPeriod != other.vsyncPeriod ||
+ configGroup != other.configGroup;
+ }
+
+ bool operator==(const RefreshRate& other) const { return !(*this != other); }
};
+ using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
+
+ // Sets the current policy to choose refresh rates.
+ void setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate, float maxRefreshRate)
+ EXCLUDES(mLock);
+
// Returns true if this device is doing refresh rate switching. This won't change at runtime.
- bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+ bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
- // 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, RefreshRate>& getRefreshRateMap() const;
+ // Returns all available refresh rates according to the current policy.
+ const RefreshRate& getRefreshRateForContent(float contentFramerate) const EXCLUDES(mLock);
- const RefreshRate& getRefreshRateFromType(RefreshRateType type) const;
+ // Returns all the refresh rates supported by the device. This won't change at runtime.
+ const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
- std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const;
+ // Returns the lowest refresh rate supported by the device. This won't change at runtime.
+ const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
- const RefreshRate& getRefreshRateFromConfigId(int configId) const;
+ // Returns the lowest refresh rate according to the current policy. May change in runtime.
+ const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
- RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const;
+ // Returns the highest refresh rate supported by the device. This won't change at runtime.
+ const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
- void setCurrentConfig(int config);
+ // Returns the highest refresh rate according to the current policy. May change in runtime.
+ const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+
+ // Returns the current refresh rate
+ const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+
+ // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+ // runtime.
+ const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+ return mRefreshRates.at(configId);
+ };
+
+ // Stores the current configId the device operates at
+ void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
struct InputConfig {
- hwc2_config_t hwcId = 0;
+ HwcConfigIndexType configId = HwcConfigIndexType(0);
+ HwcConfigGroupType configGroup = HwcConfigGroupType(0);
nsecs_t vsyncPeriod = 0;
};
RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig);
-
+ HwcConfigIndexType currentHwcConfig);
RefreshRateConfigs(bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig);
+ HwcConfigIndexType currentConfigId);
private:
- void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig);
- // Whether this device is doing refresh rate switching or not. This must not change after this
- // object is initialized.
- bool mRefreshRateSwitchingSupported;
+ void init(const std::vector<InputConfig>& configs, HwcConfigIndexType currentHwcConfig);
+
+ void constructAvailableRefreshRates() REQUIRES(mLock);
+
+ void getSortedRefreshRateList(
+ const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+ std::vector<const RefreshRate*>* outRefreshRates);
+
// 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;
+ AllRefreshRatesMapType mRefreshRates;
+
+ // The list of refresh rates which are available in the current policy, ordered by vsyncPeriod
+ // (the first element is the lowest refresh rate)
+ std::vector<const RefreshRate*> mAvailableRefreshRates GUARDED_BY(mLock);
+
+ // 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.
+ const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
+
+ // The current config group. 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.
+ HwcConfigGroupType mCurrentGroupId GUARDED_BY(mLock);
+
+ // The min and max FPS allowed by the policy. This will change at runtime and set by
+ // SurfaceFlinger on the main thread.
+ float mMinRefreshRateFps GUARDED_BY(mLock) = 0;
+ float mMaxRefreshRateFps GUARDED_BY(mLock) = std::numeric_limits<float>::max();
+
+ // The min and max refresh rates supported by the device.
+ // This will not change at runtime.
+ const RefreshRate* mMinSupportedRefreshRate;
+ const RefreshRate* mMaxSupportedRefreshRate;
+
+ const bool mRefreshRateSwitching;
+
+ mutable std::mutex mLock;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 8afc93e..a384dbe 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -25,8 +25,7 @@
#include "android-base/stringprintf.h"
#include "utils/Timers.h"
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
/**
* Class to encapsulate statistics about refresh rates that the display is using. When the power
@@ -42,10 +41,10 @@
public:
RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
- int currentConfigMode, int currentPowerMode)
+ HwcConfigIndexType currentConfigId, int currentPowerMode)
: mRefreshRateConfigs(refreshRateConfigs),
mTimeStats(timeStats),
- mCurrentConfigMode(currentConfigMode),
+ mCurrentConfigMode(currentConfigId),
mCurrentPowerMode(currentPowerMode) {}
// Sets power mode.
@@ -59,12 +58,12 @@
// Sets config mode. If the mode has changed, it records how much time was spent in the previous
// mode.
- void setConfigMode(int mode) {
- if (mCurrentConfigMode == mode) {
+ void setConfigMode(HwcConfigIndexType configId) {
+ if (mCurrentConfigMode == configId) {
return;
}
flushTime();
- mCurrentConfigMode = mode;
+ mCurrentConfigMode = configId;
}
// Returns a map between human readable refresh rate and number of seconds the device spent in
@@ -78,11 +77,11 @@
std::unordered_map<std::string, int64_t> totalTime;
// 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& [configId, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] = 0;
}
- for (const auto& [config, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+ for (const auto& [configId, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] += time;
}
totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
@@ -139,14 +138,14 @@
// Aggregate refresh rate statistics for telemetry.
TimeStats& mTimeStats;
- int mCurrentConfigMode;
+ HwcConfigIndexType mCurrentConfigMode;
int32_t mCurrentPowerMode;
- std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ std::unordered_map<HwcConfigIndexType /* configId */, int64_t /* duration in ms */>
+ mConfigModesTotalTime;
int64_t mScreenOffTime = 0;
nsecs_t mPreviousRecordedTime = systemTime();
};
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 55fd603..1d50fe1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -182,7 +182,7 @@
}
void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- int32_t configId) {
+ HwcConfigIndexType configId) {
RETURN_IF_INVALID_HANDLE(handle);
mConnections[handle].thread->onConfigChanged(displayId, configId);
}
@@ -280,8 +280,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- resyncToHardwareVsync(false,
- mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
+ resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().vsyncPeriod);
}
}
@@ -332,53 +331,49 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE;
-
- const auto lowFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
- const auto highFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
+ const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+ const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? lowFps
+ : mRefreshRateConfigs.getMaxRefreshRate().fps;
mLayerHistory->registerLayer(layer, lowFps, highFps);
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
if (mLayerHistory) {
- mLayerHistory->record(layer, presentTime, isHDR, systemTime());
+ mLayerHistory->record(layer, presentTime, systemTime());
}
}
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
- auto [refreshRate, isHDR] = mLayerHistory->summarize(systemTime());
+ auto [refreshRate] = mLayerHistory->summarize(systemTime());
const uint32_t refreshRateRound = std::round(refreshRate);
- RefreshRateType newRefreshRateType;
+ HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRefreshRate == refreshRateRound && mFeatures.isHDRContent == isHDR) {
+ if (mFeatures.contentRefreshRate == refreshRateRound) {
return;
}
mFeatures.contentRefreshRate = refreshRateRound;
ATRACE_INT("ContentFPS", refreshRateRound);
- mFeatures.isHDRContent = isHDR;
- ATRACE_INT("ContentHDR", isHDR);
-
mFeatures.contentDetection =
refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
- newRefreshRateType = calculateRefreshRateType();
- if (mFeatures.refreshRateType == newRefreshRateType) {
+ newConfigId = calculateRefreshRateType();
+ if (mFeatures.configId == newConfigId) {
return;
}
- mFeatures.refreshRateType = newRefreshRateType;
- }
- changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
+ mFeatures.configId = newConfigId;
+ };
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
-void Scheduler::setChangeRefreshRateCallback(ChangeRefreshRateCallback&& callback) {
+void Scheduler::setSchedulerCallback(android::Scheduler::ISchedulerCallback* callback) {
std::lock_guard<std::mutex> lock(mCallbackLock);
- mChangeRefreshRateCallback = std::move(callback);
+ mSchedulerCallback = callback;
}
void Scheduler::resetIdleTimer() {
@@ -423,13 +418,16 @@
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
+ // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
+ // magic number
const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
- if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) {
+ constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
+ if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// 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 /* makeAvailable */, refreshRate.second.vsyncPeriod);
- } else if (state == TimerState::Expired && refreshRate.first != RefreshRateType::PERFORMANCE) {
+ resyncToHardwareVsync(true /* makeAvailable */, refreshRate.vsyncPeriod);
+ } else if (state == TimerState::Expired && refreshRate.fps <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// 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 update the DispSync model anyway.
@@ -471,96 +469,67 @@
template <class T>
void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None;
- RefreshRateType newRefreshRateType;
+ HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
return;
}
*currentState = newState;
- newRefreshRateType = calculateRefreshRateType();
- if (mFeatures.refreshRateType == newRefreshRateType) {
+ newConfigId = calculateRefreshRateType();
+ if (mFeatures.configId == newConfigId) {
return;
}
- mFeatures.refreshRateType = newRefreshRateType;
+ mFeatures.configId = newConfigId;
if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) {
event = ConfigEvent::Changed;
}
}
- changeRefreshRate(newRefreshRateType, event);
+ const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ changeRefreshRate(newRefreshRate, event);
}
-Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+HwcConfigIndexType Scheduler::calculateRefreshRateType() {
if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
- return RefreshRateType::DEFAULT;
- }
-
- // HDR content is not supported on PERFORMANCE mode
- if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
- return RefreshRateType::DEFAULT;
+ return mRefreshRateConfigs.getCurrentRefreshRate().configId;
}
// 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 (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// As long as touch is active we want to be in performance mode
if (mFeatures.touch == TouchState::Active) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// If timer has expired as it means there is no new content on the screen
if (mFeatures.idleTimer == TimerState::Expired) {
- return RefreshRateType::DEFAULT;
+ return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
// If content detection is off we choose performance as we don't know the content fps
if (mFeatures.contentDetection == ContentDetectionState::Off) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// Content detection is on, find the appropriate refresh rate with minimal error
- // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs)
- const float rate = static_cast<float>(mFeatures.contentRefreshRate);
- auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
- mRefreshRateConfigs.getRefreshRateMap().cend(),
- [rate](const auto& lhs, const auto& rhs) -> bool {
- return std::abs(lhs.second.fps - rate) <
- std::abs(rhs.second.fps - 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
- constexpr float MARGIN = 0.05f;
- float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate;
- if (std::abs(std::round(ratio) - ratio) > MARGIN) {
- while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
- ratio = iter->second.fps / rate;
-
- if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
- currRefreshRateType = iter->first;
- break;
- }
- ++iter;
- }
- }
-
- return currRefreshRateType;
+ return mRefreshRateConfigs
+ .getRefreshRateForContent(static_cast<float>(mFeatures.contentRefreshRate))
+ .configId;
}
-Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() {
+std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- return mFeatures.refreshRateType;
+ return mFeatures.configId;
}
-void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+void Scheduler::changeRefreshRate(const RefreshRate& refreshRate, ConfigEvent configEvent) {
std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- mChangeRefreshRateCallback(refreshRateType, configEvent);
+ if (mSchedulerCallback) {
+ mSchedulerCallback->changeRefreshRate(refreshRate, configEvent);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 346896c..04a8390 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -34,6 +34,8 @@
namespace android {
+using namespace std::chrono_literals;
+
class DispSync;
class FenceTime;
class InjectVSyncSource;
@@ -41,10 +43,14 @@
class Scheduler {
public:
- using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
+ class ISchedulerCallback {
+ public:
+ virtual ~ISchedulerCallback() = default;
+ virtual void changeRefreshRate(const RefreshRate&, ConfigEvent) = 0;
+ };
// Indicates whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
@@ -67,7 +73,7 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onConfigChanged(ConnectionHandle, PhysicalDisplayId, int32_t configId);
+ void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -103,13 +109,13 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
+ void recordLayerHistory(Layer*, nsecs_t presentTime);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
- // Called by Scheduler to change refresh rate.
- void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
+ // Called by Scheduler to control SurfaceFlinger operations.
+ void setSchedulerCallback(ISchedulerCallback*);
bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
void resetIdleTimer();
@@ -122,8 +128,8 @@
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
- // Get the appropriate refresh type for current conditions.
- RefreshRateType getPreferredRefreshRateType();
+ // Get the appropriate refresh for current conditions.
+ std::optional<HwcConfigIndexType> getPreferredConfigId();
private:
friend class TestableScheduler;
@@ -158,9 +164,9 @@
void setVsyncPeriod(nsecs_t period);
- RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback with given parameters.
- void changeRefreshRate(RefreshRateType, ConfigEvent);
+ void changeRefreshRate(const RefreshRate&, ConfigEvent);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -198,7 +204,7 @@
std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
std::mutex mCallbackLock;
- ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+ ISchedulerCallback* mSchedulerCallback GUARDED_BY(mCallbackLock) = nullptr;
// 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.
@@ -210,17 +216,13 @@
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ std::optional<HwcConfigIndexType> configId;
uint32_t contentRefreshRate = 0;
- bool isHDRContent = false;
bool isDisplayPowerStateNormal = true;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
-
- // Global config to force HDR content to work on DEFAULT refreshRate
- static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index 02db022..e8ca0ba 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -51,13 +51,22 @@
inline bool operator>(T const& other) const { return !(*this < other || *this == other); }
};
+template <typename T>
+struct Hash : Ability<T, Hash> {
+ [[nodiscard]] std::size_t hash() const {
+ return std::hash<typename std::remove_const<
+ typename std::remove_reference<decltype(this->base().value())>::type>::type>{}(
+ this->base().value());
+ }
+};
+
template <typename T, typename W, template <typename> class... Ability>
struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
StrongTyping() : mValue(0) {}
explicit StrongTyping(T const& value) : mValue(value) {}
StrongTyping(StrongTyping const&) = default;
StrongTyping& operator=(StrongTyping const&) = default;
- inline operator T() const { return mValue; }
+ explicit inline operator T() const { return mValue; }
T const& value() const { return mValue; }
T& value() { return mValue; }
@@ -65,3 +74,12 @@
T mValue;
};
} // namespace android
+
+namespace std {
+template <typename T, typename W, template <typename> class... Ability>
+struct hash<android::StrongTyping<T, W, Ability...>> {
+ std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const {
+ return k.hash();
+ }
+};
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 4a4bef8..e001080 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -34,7 +34,7 @@
*/
class VSyncDispatch {
public:
- using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
+ using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
virtual ~VSyncDispatch();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 530e0a6..fc78da3 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -105,7 +105,8 @@
VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
- using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+ using CallbackMap =
+ std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
void timerCallback();
void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 27fd76c..8de35b1 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -134,18 +134,13 @@
return;
}
- const bool isDefault = mOffsets.fpsMode == RefreshRateType::DEFAULT;
- const bool isPerformance = mOffsets.fpsMode == RefreshRateType::PERFORMANCE;
const bool isEarly = &offsets == &mOffsetsConfig.early;
const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
const bool isLate = &offsets == &mOffsetsConfig.late;
- ATRACE_INT("Vsync-EarlyOffsetsOn", isDefault && isEarly);
- ATRACE_INT("Vsync-EarlyGLOffsetsOn", isDefault && isEarlyGl);
- ATRACE_INT("Vsync-LateOffsetsOn", isDefault && isLate);
- ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", isPerformance && isEarly);
- ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", isPerformance && isEarlyGl);
- ATRACE_INT("Vsync-HighFpsLateOffsetsOn", isPerformance && isLate);
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 727cef2..63c0feb 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -37,13 +37,10 @@
// switch in and out of gl composition.
static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
- using RefreshRateType = RefreshRateConfigs::RefreshRateType;
-
public:
// Wrapper for a collection of surfaceflinger/app offsets for a particular
// configuration.
struct Offsets {
- RefreshRateType fpsMode;
nsecs_t sf;
nsecs_t app;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 6588d1b..f2a7791 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -89,10 +89,29 @@
void VSyncReactor::setPeriod(nsecs_t period) {
mTracker->setPeriod(period);
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mPeriodChangeInProgress = true;
+ }
}
nsecs_t VSyncReactor::getPeriod() {
return mTracker->currentPeriod();
}
+void VSyncReactor::beginResync() {}
+
+void VSyncReactor::endResync() {}
+
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+ assert(periodFlushed);
+ mTracker->addVsyncTimestamp(timestamp);
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ *periodFlushed = mPeriodChangeInProgress;
+ mPeriodChangeInProgress = false;
+ }
+ return false;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 73a09f1..786ee98 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -43,6 +43,11 @@
void setPeriod(nsecs_t period);
nsecs_t getPeriod();
+ // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
+ void beginResync();
+ bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
+ void endResync();
+
private:
std::unique_ptr<Clock> const mClock;
std::unique_ptr<VSyncDispatch> const mDispatch;
@@ -52,6 +57,7 @@
std::mutex mMutex;
bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+ bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8cd7223..9f4dd2b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -542,14 +542,8 @@
if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
// set the refresh rate according to the policy
- const auto& performanceRefreshRate =
- mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
-
- if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
- setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
- } else {
- setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
- }
+ const auto& performanceRefreshRate = mRefreshRateConfigs->getMaxRefreshRateByPolicy();
+ changeRefreshRateLocked(performanceRefreshRate, Scheduler::ConfigEvent::None);
}
}));
}
@@ -606,6 +600,7 @@
? renderengine::RenderEngine::ContextPriority::HIGH
: renderengine::RenderEngine::ContextPriority::MEDIUM)
.build()));
+ mCompositionEngine->setTimeStats(mTimeStats);
LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
"Starting with vr flinger active is not currently supported.");
@@ -821,9 +816,8 @@
info.xdpi = xdpi;
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto refreshRateType =
- mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
- const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
+
+ const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
info.appVsyncOffset = offset.late.app;
// This is how far in advance a buffer must be queued for
@@ -873,17 +867,17 @@
if (display->isPrimary()) {
std::lock_guard<std::mutex> lock(mActiveConfigLock);
if (mDesiredActiveConfigChanged) {
- return mDesiredActiveConfig.configId;
- } else {
- return display->getActiveConfig();
+ return mDesiredActiveConfig.configId.value();
}
- } else {
- return display->getActiveConfig();
}
+
+ return display->getActiveConfig().value();
}
void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
ATRACE_CALL();
+ auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+ ALOGV("setDesiredActiveConfig(%s)", refreshRate.name.c_str());
// Don't check against the current mode yet. Worst case we set the desired
// config twice. However event generation config might have changed so we need to update it
@@ -902,13 +896,14 @@
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
- mPhaseOffsets->setRefreshRateType(info.type);
+
+ mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
}
mDesiredActiveConfigChanged = true;
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+ mRefreshRateOverlay->changeRefreshRate(refreshRate);
}
}
@@ -930,14 +925,15 @@
}
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
+ mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
-
display->setActiveConfig(mUpcomingActiveConfig.configId);
- mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
+ mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
- ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+ ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
@@ -951,12 +947,15 @@
mDesiredActiveConfigChanged = false;
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
- mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+ mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
}
bool SurfaceFlinger::performSetActiveConfig() {
ATRACE_CALL();
+ ALOGV("performSetActiveConfig");
if (mCheckPendingFence) {
if (previousFrameMissed()) {
// fence has not signaled yet. wait for the next invalidate
@@ -980,6 +979,10 @@
desiredActiveConfig = mDesiredActiveConfig;
}
+ auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig.configId);
+ ALOGV("performSetActiveConfig changing active config to %d(%s)", refreshRate.configId.value(),
+ refreshRate.name.c_str());
const auto display = getDefaultDisplayDeviceLocked();
if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
// display is not valid or we are already in the requested mode
@@ -1000,8 +1003,8 @@
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
- ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
- getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+ ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.fps);
+ getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId.value());
// we need to submit an empty frame to HWC to start the process
mCheckPendingFence = true;
@@ -1396,11 +1399,12 @@
*compositorTiming = getBE().mCompositorTiming;
}
-bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) const {
+bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
}
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
+ Scheduler::ConfigEvent event) {
const auto display = getDefaultDisplayDeviceLocked();
if (!display || mBootStage != BootStage::FINISHED) {
return;
@@ -1408,15 +1412,13 @@
ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
- 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);
+ if (!isDisplayConfigAllowed(refreshRate.configId)) {
+ ALOGV("Skipping config %d as it is not part of allowed configs",
+ refreshRate.configId.value());
return;
}
- setDesiredActiveConfig({refreshRate, desiredConfigId, event});
+ setDesiredActiveConfig({refreshRate.configId, event});
}
void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -2180,7 +2182,8 @@
Dataspace::UNKNOWN});
if (!state.isVirtual()) {
LOG_ALWAYS_FATAL_IF(!displayId);
- display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
+ auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+ display->setActiveConfig(activeConfigId);
}
display->setLayerStack(state.layerStack);
@@ -2520,6 +2523,12 @@
mCompositionEngine->updateCursorAsync(refreshArgs);
}
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
+ Scheduler::ConfigEvent event) {
+ Mutex::Autolock lock(mStateLock);
+ changeRefreshRateLocked(refreshRate, event);
+}
+
void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
if (mScheduler) {
// In practice it's not allowed to hotplug in/out the primary display once it's been
@@ -2528,7 +2537,7 @@
return;
}
- int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+ auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
mRefreshRateConfigs =
std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
getHwComposer().getConfigs(
@@ -2562,11 +2571,7 @@
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
- mScheduler->setChangeRefreshRateCallback(
- [this](RefreshRateType type, Scheduler::ConfigEvent event) {
- Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(type, event);
- });
+ mScheduler->setSchedulerCallback(this);
}
void SurfaceFlinger::commitTransaction()
@@ -3969,9 +3974,10 @@
dispSyncPresentTimeOffset, getVsyncPeriod());
StringAppendF(&result, "Allowed Display Configs: ");
- for (int32_t configId : mAllowedDisplayConfigs) {
+ for (auto configId : mAllowedDisplayConfigs) {
StringAppendF(&result, "%" PRIu32 " Hz, ",
- mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
+ static_cast<int32_t>(
+ mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps));
}
StringAppendF(&result,
"DesiredDisplayConfigSpecs: default config ID: %" PRIu32
@@ -4820,13 +4826,9 @@
n = data.readInt32();
if (n && !mRefreshRateOverlay &&
mRefreshRateConfigs->refreshRateSwitchingSupported()) {
- RefreshRateType type;
- {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- type = mDesiredActiveConfig.type;
- }
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
- mRefreshRateOverlay->changeRefreshRate(type);
+ auto current = mRefreshRateConfigs->getCurrentRefreshRate();
+ mRefreshRateOverlay->changeRefreshRate(current);
} else if (!n) {
mRefreshRateOverlay.reset();
}
@@ -4930,8 +4932,12 @@
}
// Couldn't find display by displayId. Try to get display by layerStack since virtual displays
// may not have a displayId.
+ return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == displayOrLayerStack) {
+ if (display->getLayerStack() == layerStack) {
return display;
}
}
@@ -4987,8 +4993,8 @@
public:
LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
+ bool childrenOnly, const Rect& displayViewport)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
mLayer(layer),
mCrop(crop),
mNeedsFiltering(false),
@@ -5072,7 +5078,7 @@
sp<Layer> parent;
Rect crop(sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-
+ Rect displayViewport;
{
Mutex::Autolock _l(mStateLock);
@@ -5089,14 +5095,21 @@
return PERMISSION_DENIED;
}
+ Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
if (sourceCrop.width() <= 0) {
crop.left = 0;
- crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+ crop.right = parentSourceBounds.getWidth();
}
if (sourceCrop.height() <= 0) {
crop.top = 0;
- crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+ crop.bottom = parentSourceBounds.getHeight();
+ }
+
+ if (crop.isEmpty() || frameScale <= 0.0f) {
+ // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+ // crop was not specified, or an invalid frame scale was provided.
+ return BAD_VALUE;
}
reqWidth = crop.width() * frameScale;
reqHeight = crop.height() * frameScale;
@@ -5110,6 +5123,13 @@
return NAME_NOT_FOUND;
}
}
+
+ auto display = getDisplayByLayerStack(parent->getLayerStack());
+ if (!display) {
+ return BAD_VALUE;
+ }
+
+ displayViewport = display->getViewport();
} // mStateLock
// really small crop or frameScale
@@ -5120,7 +5140,8 @@
reqHeight = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+ LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
+ displayViewport);
auto traverseLayers = [parent, childrenOnly,
&excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5246,6 +5267,7 @@
const auto rotation = renderArea.getRotationFlags();
const auto transform = renderArea.getTransform();
const auto sourceCrop = renderArea.getSourceCrop();
+ const auto& displayViewport = renderArea.getDisplayViewport();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<renderengine::LayerSettings> clientCompositionLayers;
@@ -5330,6 +5352,12 @@
};
auto result = layer->prepareClientComposition(targetSettings);
if (result) {
+ std::optional<renderengine::LayerSettings> shadowLayer =
+ layer->prepareShadowClientComposition(*result, displayViewport,
+ clientCompositionDisplay.outputDataspace);
+ if (shadowLayer) {
+ clientCompositionLayers.push_back(*shadowLayer);
+ }
clientCompositionLayers.push_back(*result);
}
});
@@ -5428,28 +5456,48 @@
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;
- }
- }
+ // Prepare the parameters needed for RefreshRateConfigs::setPolicy. This will change to just
+ // passthrough once DisplayManager provide these parameters directly.
+ const auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(HwcConfigIndexType(allowedConfigs[0]));
+ const auto defaultModeId = refreshRate.configId;
+ auto minRefreshRateFps = refreshRate.fps;
+ auto maxRefreshRateFps = minRefreshRateFps;
+
+ for (auto config : allowedConfigs) {
+ const auto configRefreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(HwcConfigIndexType(config));
+ if (configRefreshRate.fps < minRefreshRateFps) {
+ minRefreshRateFps = configRefreshRate.fps;
+ } else if (configRefreshRate.fps > maxRefreshRateFps) {
+ maxRefreshRateFps = configRefreshRate.fps;
}
- } else if (!allowedConfigs.empty()) {
- ALOGV("switching to config %d", allowedConfigs[0]);
- setDesiredActiveConfig(
- {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
+ }
+ mRefreshRateConfigs->setPolicy(defaultModeId, minRefreshRateFps, maxRefreshRateFps);
+
+ if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ auto configId = mScheduler->getPreferredConfigId();
+ auto preferredRefreshRate = configId
+ ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
+ : mRefreshRateConfigs->getMinRefreshRateByPolicy();
+ ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+ preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
+ if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
+ ALOGV("switching to Scheduler preferred config %d",
+ preferredRefreshRate.configId.value());
+ setDesiredActiveConfig(
+ {preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
+ } else {
+ // Set the highest allowed config
+ setDesiredActiveConfig({mRefreshRateConfigs->getMaxRefreshRateByPolicy().configId,
+ Scheduler::ConfigEvent::Changed});
+ }
+ } else {
+ if (!allowedConfigs.empty()) {
+ ALOGV("switching to config %d", allowedConfigs[0]);
+ auto configId = HwcConfigIndexType(allowedConfigs[0]);
+ setDesiredActiveConfig({configId, Scheduler::ConfigEvent::Changed});
+ }
}
}
@@ -5498,7 +5546,10 @@
}
if (display->isPrimary()) {
- outAllowedConfigs->assign(mAllowedDisplayConfigs.begin(), mAllowedDisplayConfigs.end());
+ outAllowedConfigs->reserve(mAllowedDisplayConfigs.size());
+ for (auto configId : mAllowedDisplayConfigs) {
+ outAllowedConfigs->push_back(configId.value());
+ }
}
return NO_ERROR;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5f1580b..ae3f2d7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -175,7 +175,8 @@
public PriorityDumper,
public ClientCache::ErasedRecipient,
private IBinder::DeathRecipient,
- private HWC2::ComposerCallback {
+ private HWC2::ComposerCallback,
+ private Scheduler::ISchedulerCallback {
public:
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -518,6 +519,10 @@
const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
/* ------------------------------------------------------------------------
+ * Scheduler::ISchedulerCallback
+ */
+ void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+ /* ------------------------------------------------------------------------
* Message handling
*/
void waitForEvent();
@@ -527,15 +532,14 @@
void signalLayerUpdate();
void signalRefresh();
- using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
struct ActiveConfigInfo {
- RefreshRateType type = RefreshRateType::DEFAULT;
- int configId = 0;
+ HwcConfigIndexType configId;
Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
bool operator!=(const ActiveConfigInfo& other) const {
- return type != other.type || configId != other.configId || event != other.event;
+ return configId != other.configId || event != other.event;
}
};
@@ -688,6 +692,7 @@
const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool& outCapturedSecureLayers);
const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
+ const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
@@ -810,9 +815,10 @@
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void setRefreshRateTo(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+ void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
+ REQUIRES(mStateLock);
- bool isDisplayConfigAllowed(int32_t configId) const REQUIRES(mStateLock);
+ bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
bool previousFrameMissed(int graceTimeMs = 0);
@@ -1136,7 +1142,7 @@
std::atomic<nsecs_t> mExpectedPresentTime = 0;
// All configs are allowed if the set is empty.
- using DisplayConfigs = std::set<int32_t>;
+ using DisplayConfigs = std::set<HwcConfigIndexType>;
DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
DesiredDisplayConfigSpecs mDesiredDisplayConfigSpecs GUARDED_BY(mStateLock);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 626efb8..1895777 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -129,6 +129,29 @@
}
}
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) {
+ if (!mEnabled.load()) return;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+ ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+ mGlobalRecord.renderEngineDurations.pop_front();
+ }
+ mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime,
+ const std::shared_ptr<FenceTime>& endTime) {
+ if (!mEnabled.load()) return;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+ ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+ mGlobalRecord.renderEngineDurations.pop_front();
+ }
+ mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
if (!timeRecord->ready) {
ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
@@ -501,6 +524,31 @@
mGlobalRecord.prevPresentTime = curPresentTime;
mGlobalRecord.presentFences.pop_front();
}
+ while (!mGlobalRecord.renderEngineDurations.empty()) {
+ const auto duration = mGlobalRecord.renderEngineDurations.front();
+ const auto& endTime = duration.endTime;
+
+ nsecs_t endNs = -1;
+
+ if (auto val = std::get_if<nsecs_t>(&endTime)) {
+ endNs = *val;
+ } else {
+ endNs = std::get<std::shared_ptr<FenceTime>>(endTime)->getSignalTime();
+ }
+
+ if (endNs == Fence::SIGNAL_TIME_PENDING) break;
+
+ if (endNs < 0) {
+ ALOGE("RenderEngineTiming is invalid!");
+ mGlobalRecord.renderEngineDurations.pop_front();
+ continue;
+ }
+
+ const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
+ mTimeStats.renderEngineTiming.insert(renderEngineMs);
+
+ mGlobalRecord.renderEngineDurations.pop_front();
+ }
}
void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 670bc8e..65e5cf4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -27,6 +27,7 @@
#include <mutex>
#include <optional>
#include <unordered_map>
+#include <variant>
using namespace android::surfaceflinger;
@@ -50,6 +51,13 @@
// The end time corresponds to when SurfaceFlinger finishes submitting the
// request to HWC to present a frame.
virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+ // Records the start time and end times for when RenderEngine begins work.
+ // The start time corresponds to the beginning of RenderEngine::drawLayers.
+ // The end time corresponds to when RenderEngine finishes rendering.
+ virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+ // Same as above, but passes in a fence representing the end time.
+ virtual void recordRenderEngineDuration(nsecs_t startTime,
+ const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) = 0;
@@ -58,6 +66,8 @@
virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) = 0;
+ // SetPresent{Time, Fence} are not expected to be called in the critical
+ // rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) = 0;
@@ -107,9 +117,15 @@
nsecs_t prevTime = 0;
};
+ struct RenderEngineDuration {
+ nsecs_t startTime;
+ std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
+ };
+
struct GlobalRecord {
nsecs_t prevPresentTime = 0;
std::deque<std::shared_ptr<FenceTime>> presentFences;
+ std::deque<RenderEngineDuration> renderEngineDurations;
};
public:
@@ -124,6 +140,9 @@
void incrementClientCompositionFrames() override;
void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
+ void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
+ void recordRenderEngineDuration(nsecs_t startTime,
+ const std::shared_ptr<FenceTime>& readyFence) override;
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 83cd45a..7e43880 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -113,6 +113,8 @@
result.append(presentToPresent.toString());
StringAppendF(&result, "frameDuration histogram is as below:\n");
result.append(frameDuration.toString());
+ StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
+ result.append(renderEngineTiming.toString());
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
result.append(ele->toString());
@@ -165,6 +167,11 @@
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
+ for (const auto& histEle : renderEngineTiming.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
+ histProto->set_time_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 6b28970..bd97ecc 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -62,6 +62,7 @@
int64_t displayOnTime = 0;
Histogram presentToPresent;
Histogram frameDuration;
+ Histogram renderEngineTiming;
std::unordered_map<std::string, TimeStatsLayer> stats;
std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 96430b3..5fd4a39 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
// changes to these messages, and keep google3 side proto messages in sync if
// the end to end pipeline needs to be updated.
-// Next tag: 11
+// Next tag: 12
message SFTimeStatsGlobalProto {
// The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
@@ -45,6 +45,8 @@
repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
// Frame CPU duration histogram.
repeated SFTimeStatsHistogramBucketProto frame_duration = 10;
+ // Frame GPU duration histogram.
+ repeated SFTimeStatsHistogramBucketProto render_engine_timing = 11;
// Stats per layer. Apps could have multiple layers.
repeated SFTimeStatsLayerProto stats = 6;
}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..b1bb7fd 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -274,7 +274,7 @@
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(), FRAME_SCALE, &outBuffer);
+ Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 73f563d..0ad122b 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -1443,6 +1443,61 @@
mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
}
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop(0, 0, 10, 10);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ Rect layerCrop(0, 0, 10, 10);
+ SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+ TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 32f997f..04991f9 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -33,13 +33,13 @@
#include "BufferQueueLayer.h"
#include "ColorLayer.h"
#include "Layer.h"
-
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockDispSync.h"
#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
+#include "mock/MockTimeStats.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -100,6 +100,7 @@
.WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
setupComposer(0);
}
@@ -181,6 +182,7 @@
Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ mock::TimeStats* mTimeStats = new mock::TimeStats();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index db7d04c..76e8171 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1427,7 +1427,7 @@
// Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
// remapped, and the test only ever sets up one config. If there were an error
// looking up the remapped index, device->getActiveConfig() would be -1 instead.
- EXPECT_EQ(0, device->getActiveConfig());
+ EXPECT_EQ(0, device->getActiveConfig().value());
EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
device->getSupportedPerFrameMetadata());
}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 2662f52..80bca02 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -26,6 +26,7 @@
#include "AsyncCallRecorder.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/HwcStrongTypes.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -34,6 +35,7 @@
using testing::Invoke;
namespace android {
+
namespace {
constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
@@ -448,17 +450,17 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7);
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7));
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7);
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
- mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5);
+ mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5));
expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
- mThread->onConfigChanged(DISPLAY_ID_64BIT, 7);
+ mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7));
expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
}
@@ -468,7 +470,7 @@
createConnection(suppressConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9);
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9));
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9);
auto args = suppressConnectionEventRecorder.waitForCall();
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 66c7f6b..da4eea0 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -25,16 +25,16 @@
struct FakePhaseOffsets : PhaseOffsets {
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
- Offsets getOffsetsForRefreshRate(RefreshRateType) const override { return getCurrentOffsets(); }
+ Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
Offsets getCurrentOffsets() const override {
- return {{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},
+ return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
FAKE_PHASE_OFFSET_NS};
}
- void setRefreshRateType(RefreshRateType) override {}
+ void setRefreshRateFps(float) override {}
void dump(std::string&) const override {}
};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index e93d31e..d95252b 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -44,9 +44,15 @@
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
RefreshRateConfigs mConfigs{true,
- {RefreshRateConfigs::InputConfig{0, LO_FPS_PERIOD},
- RefreshRateConfigs::InputConfig{1, HI_FPS_PERIOD}},
- 0};
+ {
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
+ HwcConfigGroupType(0),
+ LO_FPS_PERIOD},
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(1),
+ HwcConfigGroupType(0),
+ HI_FPS_PERIOD},
+ },
+ HwcConfigIndexType(0)};
TestableScheduler* const mScheduler{new TestableScheduler(mConfigs)};
TestableSurfaceFlinger mFlinger;
@@ -57,7 +63,6 @@
TEST_F(LayerHistoryTest, oneLayer) {
const auto layer = createLayer();
- constexpr bool isHDR = false;
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_EQ(1, layerCount());
@@ -69,14 +74,14 @@
// 0 FPS is returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, isHDR, mTime);
+ history().record(layer.get(), 0, mTime);
EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
EXPECT_EQ(1, activeLayerCount());
}
// High FPS is returned once enough history has been recorded.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, isHDR, mTime);
+ history().record(layer.get(), 0, mTime);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime).maxRefreshRate);
EXPECT_EQ(1, activeLayerCount());
}
@@ -84,29 +89,25 @@
TEST_F(LayerHistoryTest, oneHDRLayer) {
const auto layer = createLayer();
- constexpr bool isHDR = true;
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, isHDR, mTime);
+ history().record(layer.get(), 0, mTime);
auto summary = history().summarize(mTime);
EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_TRUE(summary.isHDR);
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
summary = history().summarize(mTime);
EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_FALSE(summary.isHDR);
EXPECT_EQ(0, activeLayerCount());
}
TEST_F(LayerHistoryTest, explicitTimestamp) {
const auto layer = createLayer();
- constexpr bool isHDR = false;
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_EQ(1, layerCount());
@@ -114,7 +115,7 @@
nsecs_t time = mTime;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, isHDR, time);
+ history().record(layer.get(), time, time);
time += LO_FPS_PERIOD;
}
@@ -127,7 +128,6 @@
auto layer1 = createLayer();
auto layer2 = createLayer();
auto layer3 = createLayer();
- constexpr bool isHDR = false;
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
@@ -141,7 +141,7 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, isHDR, time);
+ history().record(layer1.get(), time, time);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -151,12 +151,12 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
time += HI_FPS_PERIOD;
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, isHDR, time);
+ history().record(layer1.get(), time, time);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
EXPECT_EQ(2, activeLayerCount());
@@ -165,7 +165,7 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
time += LO_FPS_PERIOD;
}
@@ -178,10 +178,10 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
}
- history().record(layer3.get(), time, isHDR, time);
+ history().record(layer3.get(), time, time);
time += HI_FPS_PERIOD;
}
@@ -190,7 +190,7 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, isHDR, time);
+ history().record(layer3.get(), time, time);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -205,7 +205,7 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
time += LO_FPS_PERIOD;
}
@@ -222,7 +222,7 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer3.get(), time, isHDR, time);
+ history().record(layer3.get(), time, time);
time += HI_FPS_PERIOD;
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index f315a8a..546e65c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -30,27 +30,19 @@
namespace android {
namespace scheduler {
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
class RefreshRateConfigsTest : public testing::Test {
protected:
- static constexpr int CONFIG_ID_60 = 0;
- static constexpr hwc2_config_t HWC2_CONFIG_ID_60 = 0;
- static constexpr int CONFIG_ID_90 = 1;
- static constexpr hwc2_config_t HWC2_CONFIG_ID_90 = 1;
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
+ static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
+ static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
static constexpr int64_t VSYNC_60 = 16666667;
static constexpr int64_t VSYNC_90 = 11111111;
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
-
- void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
- ASSERT_EQ(left.configId, right.configId);
- ASSERT_EQ(left.name, right.name);
- ASSERT_EQ(left.fps, right.fps);
- ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
- }
};
RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -69,40 +61,173 @@
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
- std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfig=*/0);
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingNotSupported) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
- std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
- {HWC2_CONFIG_ID_90, VSYNC_90}};
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfig=*/0);
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
- const auto& rates = refreshRateConfigs->getRefreshRateMap();
- ASSERT_EQ(2, rates.size());
- const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
- const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
- ASSERT_NE(rates.end(), defaultRate);
- ASSERT_NE(rates.end(), performanceRate);
- 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);
+ const auto minRate = refreshRateConfigs->getMinRefreshRate();
+ const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
- assertRatesEqual(expectedDefaultConfig,
- refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
- assertRatesEqual(expectedPerformanceConfig,
- refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ ASSERT_EQ(expectedDefaultConfig, minRate);
+ RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps",
+ 90};
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+
+ const auto minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ ASSERT_EQ(minRateByPolicy, minRate);
+ ASSERT_EQ(performanceRateByPolicy, performanceRate);
}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentGroups) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_1, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
+ const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ ASSERT_EQ(expectedDefaultConfig, minRate);
+ ASSERT_EQ(expectedDefaultConfig, minRate60);
+ ASSERT_EQ(expectedDefaultConfig, performanceRate60);
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90);
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+ RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_1, "90fps",
+ 90};
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+ ASSERT_EQ(expectedPerformanceConfig, minRate90);
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate90);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+ auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ ASSERT_EQ(expectedDefaultConfig, minRate);
+ RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps",
+ 90};
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ ASSERT_EQ(expectedDefaultConfig, minRate60);
+ ASSERT_EQ(expectedDefaultConfig, performanceRate60);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ {
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
+ }
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ {
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
+ }
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+ {
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120);
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index cec0b32..ef4699f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -33,8 +33,9 @@
class RefreshRateStatsTest : public testing::Test {
protected:
- static constexpr int CONFIG_ID_90 = 0;
- static constexpr int CONFIG_ID_60 = 1;
+ static inline const auto CONFIG_ID_0 = HwcConfigIndexType(0);
+ static inline const auto CONFIG_ID_1 = HwcConfigIndexType(1);
+ static inline const auto CONFIG_GROUP_0 = HwcConfigGroupType(0);
static constexpr int64_t VSYNC_90 = 11111111;
static constexpr int64_t VSYNC_60 = 16666667;
@@ -43,10 +44,10 @@
void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
- /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
+ /*refreshRateSwitching=*/true, configs, /*currentConfig=*/CONFIG_ID_0);
mRefreshRateStats =
std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
- /*currentConfig=*/0,
+ /*currentConfigId=*/CONFIG_ID_0,
/*currentPowerMode=*/HWC_POWER_MODE_OFF);
}
@@ -72,7 +73,7 @@
* Test cases
*/
TEST_F(RefreshRateStatsTest, oneConfigTest) {
- init({{CONFIG_ID_90, VSYNC_90}});
+ init({{{CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90}}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
@@ -91,7 +92,7 @@
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(0u, times.count("90fps"));
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -107,7 +108,7 @@
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -118,7 +119,7 @@
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
- init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
+ init({{{CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90}, {CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60}}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
@@ -137,7 +138,7 @@
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -147,7 +148,7 @@
EXPECT_LT(0, times["90fps"]);
// When power mode is normal, time for configs updates.
- mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_1);
int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -156,7 +157,7 @@
ASSERT_EQ(1u, times.count("60fps"));
EXPECT_LT(0, times["60fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -164,7 +165,7 @@
EXPECT_LT(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_1);
ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -175,7 +176,7 @@
// 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);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -183,7 +184,7 @@
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_1);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index b4cc1e1..40536ab 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -50,10 +50,11 @@
::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::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
+ {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
+ mRefreshRateConfigs = std::make_unique<
+ scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/HwcConfigIndexType(0));
mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs);
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
index b9ddcd7..5406879 100644
--- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
@@ -56,18 +56,18 @@
EXPECT_THAT(f1 + f2, Eq(FunkyType(32)));
EXPECT_THAT(f2 + f1, Eq(FunkyType(32)));
- EXPECT_THAT(++f1, Eq(11));
- EXPECT_THAT(f1, Eq(11));
- EXPECT_THAT(f1++, Eq(11));
- EXPECT_THAT(f1++, Eq(12));
- EXPECT_THAT(f1, Eq(13));
+ EXPECT_THAT(++f1.value(), Eq(11));
+ EXPECT_THAT(f1.value(), Eq(11));
+ EXPECT_THAT(f1++.value(), Eq(11));
+ EXPECT_THAT(f1++.value(), Eq(12));
+ EXPECT_THAT(f1.value(), Eq(13));
auto f3 = f1;
EXPECT_THAT(f1, Eq(f3));
EXPECT_THAT(f1, Lt(f2));
f3 += f1;
- EXPECT_THAT(f1, Eq(13));
- EXPECT_THAT(f3, Eq(26));
+ EXPECT_THAT(f1.value(), Eq(13));
+ EXPECT_THAT(f3.value(), Eq(26));
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 94fc5f7..b2210ec 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -189,19 +189,23 @@
std::make_unique<impl::HWComposer>(std::move(composer)));
}
+ void setupTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+ mFlinger->mCompositionEngine->setTimeStats(timeStats);
+ }
+
void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread) {
- std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
- mFlinger->mRefreshRateConfigs =
- std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
- configs, /*currentConfig=*/0);
- mFlinger->mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs,
- *mFlinger->mTimeStats,
- /*currentConfig=*/0,
- /*powerMode=*/HWC_POWER_MODE_OFF);
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
+ {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
+ mFlinger->mRefreshRateConfigs = std::make_unique<
+ scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/HwcConfigIndexType(0));
+ mFlinger->mRefreshRateStats = std::make_unique<
+ scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
+ /*currentConfig=*/HwcConfigIndexType(0),
+ /*powerMode=*/HWC_POWER_MODE_OFF);
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -429,6 +433,7 @@
static constexpr int32_t DEFAULT_WIDTH = 1920;
static constexpr int32_t DEFAULT_HEIGHT = 1280;
static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+ static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
static constexpr int32_t DEFAULT_POWER_MODE = 2;
@@ -452,7 +457,7 @@
return *this;
}
- auto& setRefreshRate(int32_t refreshRate) {
+ auto& setRefreshRate(uint32_t refreshRate) {
mRefreshRate = refreshRate;
return *this;
}
@@ -499,6 +504,7 @@
config.setVsyncPeriod(mRefreshRate);
config.setDpiX(mDpiX);
config.setDpiY(mDpiY);
+ config.setConfigGroup(mConfigGroup);
display->mutableConfigs().emplace(mActiveConfig, config.build());
display->mutableIsConnected() = true;
display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
@@ -522,8 +528,9 @@
hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
int32_t mWidth = DEFAULT_WIDTH;
int32_t mHeight = DEFAULT_HEIGHT;
- int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
+ uint32_t mRefreshRate = DEFAULT_REFRESH_RATE;
int32_t mDpiX = DEFAULT_DPI;
+ int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
int32_t mDpiY = DEFAULT_DPI;
int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
int32_t mPowerMode = DEFAULT_POWER_MODE;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 069344a..68e4c58 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -303,6 +303,44 @@
EXPECT_EQ(3, histogramProto.time_millis());
}
+TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ using namespace std::chrono_literals;
+
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+ .count(),
+ std::make_shared<FenceTime>(
+ std::chrono::duration_cast<
+ std::chrono::nanoseconds>(3ms)
+ .count()));
+
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+ .count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+
+ // First verify that flushing RenderEngine durations did not occur yet.
+ SFTimeStatsGlobalProto preFlushProto;
+ ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+ ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
+
+ // Push a dummy present fence to trigger flushing the RenderEngine timings.
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+ // Now we can verify that RenderEngine durations were flushed now.
+ SFTimeStatsGlobalProto postFlushProto;
+ ASSERT_TRUE(postFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, postFlushProto.render_engine_timing_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto =
+ postFlushProto.render_engine_timing().Get(0);
+ EXPECT_EQ(2, histogramProto.frame_count());
+ EXPECT_EQ(2, histogramProto.time_millis());
+}
+
TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 2e01d5c..84df019 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -126,7 +126,7 @@
protected:
VSyncReactorTest()
: mMockDispatch(std::make_shared<MockVSyncDispatch>()),
- mMockTracker(std::make_shared<MockVSyncTracker>()),
+ mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
mReactor(std::make_unique<ClockWrapper>(mMockClock),
std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
@@ -248,4 +248,23 @@
mReactor.setPeriod(fakePeriod);
}
+TEST_F(VSyncReactorTest, addResyncSampleTypical) {
+ nsecs_t const fakeTimestamp = 3032;
+ bool periodFlushed = false;
+
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+ EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
+ bool periodFlushed = false;
+ nsecs_t const fakeTimestamp = 4398;
+ nsecs_t const newPeriod = 3490;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+ mReactor.setPeriod(newPeriod);
+ EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ed35ebf..f7c3804 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,7 +33,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t));
+ MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
MOCK_METHOD1(registerDisplayEventConnection,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index e94af49..ec74a42 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -35,6 +35,8 @@
MOCK_METHOD0(incrementMissedFrames, void());
MOCK_METHOD0(incrementClientCompositionFrames, void());
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
+ MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
+ MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 22df255..2c63da0 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -136,6 +136,11 @@
}
}
+ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+ bool unlock = true) {
+ fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+ }
+
// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
bool unlock = true) {