Merge "SurfaceFlinger: mExpectedPresentTime should not be updated outside the main thread" into sc-dev
diff --git a/Android.bp b/Android.bp
index 298c01a..1393f61 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,4 +85,4 @@
cc_library_headers{
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
-}
\ No newline at end of file
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 990aa53..cfc405a 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1657,8 +1657,6 @@
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
- /* Dump Bluetooth HCI logs */
- ds.AddDir("/data/misc/bluetooth/logs", true);
/* Dump Nfc NCI logs */
ds.AddDir("/data/misc/nfc/logs", true);
@@ -1744,6 +1742,9 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
+ /* Dump Bluetooth HCI logs after getting bluetooth_manager dumpsys */
+ ds.AddDir("/data/misc/bluetooth/logs", true);
+
if (ds.dump_pool_) {
WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
} else {
@@ -2060,7 +2061,7 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
+ const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
@@ -3067,6 +3068,9 @@
android::os::UnlinkAndLogOnError(tmp_path_);
android::os::UnlinkAndLogOnError(screenshot_path_);
android::os::UnlinkAndLogOnError(path_);
+ if (dump_traces_path != nullptr) {
+ android::os::UnlinkAndLogOnError(dump_traces_path);
+ }
}
void Dumpstate::EnableParallelRunIfNeeded() {
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 4dfd1d0..0cf50a3 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -2112,8 +2112,9 @@
{
struct stat s;
if (stat(b_path.c_str(), &s) != 0) {
- // Silently ignore for now. The service calling this isn't smart enough to understand
- // lack of artifacts at the moment.
+ // Ignore for now. The service calling this isn't smart enough to
+ // understand lack of artifacts at the moment.
+ LOG(VERBOSE) << "A/B artifact " << b_path << " does not exist!";
return false;
}
if (!S_ISREG(s.st_mode)) {
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 443821c..ef052bd 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -473,24 +473,29 @@
// Run dexopt with the parameters of parameters_.
// TODO(calin): embed the profile name in the parameters.
int Dexopt() {
- std::string dummy;
- return dexopt(parameters_.apk_path,
- parameters_.uid,
- parameters_.pkgName,
- parameters_.instruction_set,
- parameters_.dexopt_needed,
- parameters_.oat_dir,
- parameters_.dexopt_flags,
- parameters_.compiler_filter,
- parameters_.volume_uuid,
- parameters_.shared_libraries,
- parameters_.se_info,
- parameters_.downgrade,
- parameters_.target_sdk_version,
- parameters_.profile_name,
- parameters_.dex_metadata_path,
- parameters_.compilation_reason,
- &dummy);
+ std::string error;
+ int res = dexopt(parameters_.apk_path,
+ parameters_.uid,
+ parameters_.pkgName,
+ parameters_.instruction_set,
+ parameters_.dexopt_needed,
+ parameters_.oat_dir,
+ parameters_.dexopt_flags,
+ parameters_.compiler_filter,
+ parameters_.volume_uuid,
+ parameters_.shared_libraries,
+ parameters_.se_info,
+ parameters_.downgrade,
+ parameters_.target_sdk_version,
+ parameters_.profile_name,
+ parameters_.dex_metadata_path,
+ parameters_.compilation_reason,
+ &error);
+ if (res != 0) {
+ LOG(ERROR) << "During preopt of " << parameters_.apk_path << " got result " << res
+ << " error: " << error;
+ }
+ return res;
}
int RunPreopt() {
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
index dae976e..23b54ee 100644
--- a/cmds/surfacereplayer/proto/Android.bp
+++ b/cmds/surfacereplayer/proto/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 83b6aa0..9d88ca6 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -1,7 +1,16 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
prebuilt_etc {
name: "android.hardware.biometrics.face.xml",
product_specific: true,
sub_dir: "permissions",
src: "android.hardware.biometrics.face.xml",
filename_from_src: true,
-}
\ No newline at end of file
+}
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index cac67d4..ee1eee2 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -367,6 +367,10 @@
* the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
* after calling {@link AImageDecoder_rewind}).
*
+ * It is strongly recommended to use setTargetSize only for downscaling, as it
+ * is often more efficient to scale-up when rendering than up-front due to
+ * reduced overall memory.
+ *
* Available since API level 30.
*
* @param width Width of the output (prior to cropping).
diff --git a/include/input/Flags.h b/include/input/Flags.h
index 072dd18..b12a9ed 100644
--- a/include/input/Flags.h
+++ b/include/input/Flags.h
@@ -84,7 +84,7 @@
template <typename F>
constexpr std::optional<std::string_view> flag_name(F flag) {
using U = std::underlying_type_t<F>;
- auto idx = __builtin_ctzl(static_cast<U>(flag));
+ auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
return details::flag_names<F>[idx];
}
@@ -280,4 +280,4 @@
} // namespace flag_operators
} // namespace android
-#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file
+#endif // __UI_INPUT_FLAGS_H
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index b85aecd..ea3c341 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_library_static {
name: "libattestation",
cflags: [
@@ -28,4 +37,4 @@
"liblog",
"libcrypto",
],
-}
\ No newline at end of file
+}
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
index 6ce5ea1..8dac0ce 100644
--- a/libs/attestation/tests/Android.bp
+++ b/libs/attestation/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_test {
name: "libattestation_tests",
test_suites: ["device-tests"],
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2bf3d03..f1b068e 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -40,6 +40,41 @@
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
+macro_rules! assert_eq {
+ ($left:expr, $right:expr $(,)?) => {
+ match (&$left, &$right) {
+ (left, right) => {
+ if *left != *right {
+ eprintln!(
+ "assertion failed: `{:?}` == `{:?}`, {}:{}:{}",
+ &*left,
+ &*right,
+ file!(),
+ line!(),
+ column!()
+ );
+ return Err(StatusCode::FAILED_TRANSACTION);
+ }
+ }
+ }
+ };
+}
+
+macro_rules! assert {
+ ($expr:expr) => {
+ if !$expr {
+ eprintln!(
+ "assertion failed: `{:?}`, {}:{}:{}",
+ $expr,
+ file!(),
+ line!(),
+ column!()
+ );
+ return Err(StatusCode::FAILED_TRANSACTION);
+ }
+ };
+}
+
static SERVICE_ONCE: Once = Once::new();
static mut SERVICE: Option<SpIBinder> = None;
@@ -282,7 +317,7 @@
))?;
}
bindings::Transaction_TEST_FAIL => {
- return Err(StatusCode::FAILED_TRANSACTION)
+ assert!(false);
}
_ => return Err(StatusCode::UNKNOWN_TRANSACTION),
}
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 5bccaca..97626be 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_test {
name: "ftl_test",
test_suites: ["device-tests"],
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index eba7b3c..05e1935 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,15 +17,10 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
-#include <stdint.h>
-#include <sys/types.h>
-
#include <android/gui/ITransactionTraceListener.h>
-
-#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-
+#include <binder/Parcel.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IRegionSamplingListener.h>
@@ -33,16 +28,15 @@
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <system/graphics.h>
-
#include <ui/DisplayMode.h>
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/HdrCapabilities.h>
#include <ui/StaticDisplayInfo.h>
-
#include <utils/Log.h>
// ---------------------------------------------------------------------------
@@ -761,6 +755,33 @@
return error;
}
+ virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, layerHandle);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("addFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::REMOVE_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("removeFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
ui::DisplayModeId defaultMode, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1646,6 +1667,32 @@
}
return removeRegionSamplingListener(listener);
}
+ case ADD_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> layerHandle;
+ status_t result = data.readNullableStrongBinder(&layerHandle);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read layer handle");
+ return result;
+ }
+ sp<gui::IFpsListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read listener");
+ return result;
+ }
+ return addFpsListener(layerHandle, listener);
+ }
+ case REMOVE_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<gui::IFpsListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeFpsListener: Failed to read listener");
+ return result;
+ }
+ return removeFpsListener(listener);
+ }
case SET_DESIRED_DISPLAY_MODE_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0317b2b..f5cde59 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1982,6 +1982,15 @@
return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
}
+status_t SurfaceComposerClient::addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->addFpsListener(layerHandle, listener);
+}
+
+status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->removeFpsListener(listener);
+}
+
bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
bool support = false;
ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
diff --git a/libs/gui/aidl/android/gui/IFpsListener.aidl b/libs/gui/aidl/android/gui/IFpsListener.aidl
new file mode 100644
index 0000000..63d333c
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IFpsListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IFpsListener {
+
+ // Reports the most recent recorded fps for the tree rooted at this layer
+ void onFpsReported(float fps);
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 6289e5a..8b64bcf 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,21 +16,17 @@
#pragma once
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-
+#include <android/gui/IFpsListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
#include <gui/FrameTimelineInfo.h>
#include <gui/ITransactionCompletedListener.h>
-
#include <input/Flags.h>
-
#include <math/vec4.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -40,7 +36,6 @@
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
-
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -354,6 +349,23 @@
*/
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
+ /* Registers a listener that streams fps updates from SurfaceFlinger.
+ *
+ * The listener will stream fps updates for the layer tree rooted at layerHandle. Usually, this
+ * should be tied to a task. Layers that are not descendants of that task are out of scope for
+ * FPS computations.
+ *
+ * Multiple listeners may be supported.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) = 0;
+ /*
+ * Removes a listener that was streaming fps updates from SurfaceFlinger.
+ */
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0;
+
/* Sets the refresh rate boundaries for the display.
*
* The primary refresh rate range represents display manager's general guidance on the display
@@ -559,6 +571,8 @@
GET_GPU_CONTEXT_PRIORITY,
GET_EXTRA_BUFFER_COUNT,
GET_DYNAMIC_DISPLAY_INFO,
+ ADD_FPS_LISTENER,
+ REMOVE_FPS_LISTENER,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 64c8af5..ab0347c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -589,6 +589,9 @@
const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener);
static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+ static status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener);
+ static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
private:
virtual void onFirstRef();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 69bb866..91b2aff 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -824,6 +824,11 @@
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
+ status_t addFpsListener(const sp<IBinder>& /*layerHandle*/,
+ const sp<gui::IFpsListener>& /*listener*/) {
+ return NO_ERROR;
+ }
+ status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
ui::DisplayModeId /*defaultMode*/,
bool /*allowGroupSwitching*/,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 9f40011..6c6db46 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -404,10 +404,6 @@
return true;
}
-static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) {
- return !!(desc.usage & usage);
-}
-
static float toDegrees(uint32_t transform) {
switch (transform) {
case ui::Transform::ROT_90:
@@ -606,8 +602,6 @@
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
- LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
- "missing usage");
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
if (useFramebufferCache) {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index de2b9a4..74d17ce 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -94,6 +94,7 @@
"libarect",
"libmath",
],
+
}
cc_library_shared {
diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp
index 2eb2f9f..d4538f1 100644
--- a/libs/vr/libbroadcastring/Android.bp
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 45bdd35..583ad1d 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index f372bd7..0bda798 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
index fc1f376..e33e03b 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp
+++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
@@ -5,8 +5,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index e883916..33a0d75 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -5,8 +5,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index 365a676..b0ed950 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 83c30d7..96023dd 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -19,8 +19,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index 4ed80a4..fe7feb8 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
index 9e1e516..fe4dfc7 100644
--- a/libs/vr/libdvrcommon/Android.bp
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index ea73d7a..8046857 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 532d1a7..216ca9f 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
index 5beee35..38bf4ea 100644
--- a/libs/vr/libperformance/Android.bp
+++ b/libs/vr/libperformance/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 8aca9a5..bf848af 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
index dafd354..095f556 100644
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
index 8f566a0..40a5099 100644
--- a/libs/vr/libvrsensor/Android.bp
+++ b/libs/vr/libvrsensor/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/configdump/Android.bp b/opengl/tests/configdump/Android.bp
index ffb0c1f..1bb5983 100644
--- a/opengl/tests/configdump/Android.bp
+++ b/opengl/tests/configdump/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/filter/Android.bp b/opengl/tests/filter/Android.bp
index 3b92b37..b93576f 100644
--- a/opengl/tests/filter/Android.bp
+++ b/opengl/tests/filter/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/gl_basic/Android.bp b/opengl/tests/gl_basic/Android.bp
index f777401..f55cd0d 100644
--- a/opengl/tests/gl_basic/Android.bp
+++ b/opengl/tests/gl_basic/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/tritex/Android.bp b/opengl/tests/tritex/Android.bp
index 759582c..87da93f 100644
--- a/opengl/tests/tritex/Android.bp
+++ b/opengl/tests/tritex/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 182736f..b2ddb42 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -105,7 +105,7 @@
void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
- void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 45a007b..ed17e68 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -273,6 +273,7 @@
bool enabled;
int32_t pid;
nsecs_t consumeTime; // time when the event was consumed by InputConsumer
+ int32_t displayId;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index ee6b38b..2db8c13 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -40,110 +40,102 @@
return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
}
-std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
- auto it = mPendingFocusRequests.find(displayId);
- return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
+std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
+ auto it = mFocusRequestByDisplay.find(displayId);
+ return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
}
+/**
+ * 'setInputWindows' is called when the window properties change. Here we will check whether the
+ * currently focused window can remain focused. If the currently focused window remains eligible
+ * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
+ * we will check if the previous focus request is eligible to receive focus.
+ */
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
- // If the current focused window becomes unfocusable, remove focus.
- sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ std::string removeFocusReason;
+
+ // Check if the currently focused window is still focusable.
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
if (currentFocus) {
- FocusResult result = isTokenFocusable(currentFocus, windows);
- if (result != FocusResult::OK) {
- return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
+ Focusability result = isTokenFocusable(currentFocus, windows);
+ if (result == Focusability::OK) {
+ return std::nullopt;
+ }
+ removeFocusReason = NamedEnum::string(result);
+ }
+
+ // We don't have a focused window or the currently focused window is no longer focusable. Check
+ // to see if we can grant focus to the window that previously requested focus.
+ const std::optional<FocusRequest> request = getFocusRequest(displayId);
+ if (request) {
+ sp<IBinder> requestedFocus = request->token;
+ const Focusability result = isTokenFocusable(requestedFocus, windows);
+ const Focusability previousResult = mLastFocusResultByDisplay[displayId];
+ mLastFocusResultByDisplay[displayId] = result;
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId,
+ "Window became focusable. Previous reason: " +
+ NamedEnum::string(previousResult),
+ requestedFocus, request->windowName);
}
}
- // Check if any pending focus requests can be resolved.
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (!pendingRequest) {
- return std::nullopt;
- }
-
- sp<IBinder> requestedFocus = pendingRequest->token;
- std::string windowName = pendingRequest->windowName;
- if (currentFocus == requestedFocus) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
- windowName.c_str(), displayId);
- mPendingFocusRequests.erase(displayId);
- return std::nullopt;
- }
-
- FocusResult result = isTokenFocusable(requestedFocus, windows);
- // If the window from the pending request is now visible, provide it focus.
- if (result == FocusResult::OK) {
- mPendingFocusRequests.erase(displayId);
- return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
- }
-
- if (result != FocusResult::NOT_VISIBLE) {
- // Drop the request if we are unable to change the focus for a reason other than visibility.
- ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
- displayId, NamedEnum::string(result).c_str());
- mPendingFocusRequests.erase(displayId);
- }
- return std::nullopt;
+ // Focused window is no longer focusable and we don't have a suitable focus request to grant.
+ // Remove focus if needed.
+ return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}
std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
const int32_t displayId = request.displayId;
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
- if (request.focusedToken && currentFocus != request.focusedToken) {
- ALOGW("setFocusedWindow %s on display %" PRId32
- " ignored, reason: focusedToken %s is not focused",
- request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
- return std::nullopt;
- }
-
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (pendingRequest) {
- ALOGW("Pending focus request %s on display %" PRId32
- " ignored, reason:replaced by new request",
- pendingRequest->windowName.c_str(), displayId);
-
- // clear any pending focus requests
- mPendingFocusRequests.erase(displayId);
- }
-
if (currentFocus == request.token) {
ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
request.windowName.c_str(), displayId);
return std::nullopt;
}
- FocusResult result = isTokenFocusable(request.token, windows);
- if (result == FocusResult::OK) {
- std::string reason =
- (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
- return updateFocusedWindow(displayId, reason, request.token, request.windowName);
- }
-
- if (result == FocusResult::NOT_VISIBLE) {
- // The requested window is not currently visible. Wait for the window to become visible
- // and then provide it focus. This is to handle situations where a user action triggers
- // a new window to appear. We want to be able to queue any key events after the user
- // action and deliver it to the newly focused window. In order for this to happen, we
- // take focus from the currently focused window so key events can be queued.
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32
- " pending, reason: window is not visible",
- request.windowName.c_str(), displayId);
- mPendingFocusRequests[displayId] = request;
- return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
- } else {
- ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
+ // Handle conditional focus requests, i.e. requests that have a focused token. These requests
+ // are not persistent. If the window is no longer focusable, we expect focus to go back to the
+ // previously focused window.
+ if (request.focusedToken) {
+ if (currentFocus != request.focusedToken) {
+ ALOGW("setFocusedWindow %s on display %" PRId32
+ " ignored, reason: focusedToken %s is not focused",
+ request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
+ return std::nullopt;
+ }
+ Focusability result = isTokenFocusable(request.token, windows);
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId, "setFocusedWindow with focus check",
+ request.token, request.windowName);
+ }
+ ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ return std::nullopt;
}
- return std::nullopt;
+ Focusability result = isTokenFocusable(request.token, windows);
+ // Update focus request. The focus resolver will always try to handle this request if there is
+ // no focused window on the display.
+ mFocusRequestByDisplay[displayId] = request;
+ mLastFocusResultByDisplay[displayId] = result;
+
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId, "setFocusedWindow", request.token,
+ request.windowName);
+ }
+
+ // The requested window is not currently focusable. Wait for the window to become focusable
+ // but remove focus from the current window so that input events can go into a pending queue
+ // and be sent to the window when it becomes focused.
+ return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+ nullptr);
}
-FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
bool allWindowsAreFocusable = true;
bool visibleWindowFound = false;
@@ -165,16 +157,16 @@
}
if (!windowFound) {
- return FocusResult::NO_WINDOW;
+ return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
- return FocusResult::NOT_FOCUSABLE;
+ return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowFound) {
- return FocusResult::NOT_VISIBLE;
+ return Focusability::NOT_VISIBLE;
}
- return FocusResult::OK;
+ return Focusability::OK;
}
std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
@@ -209,15 +201,17 @@
std::string FocusResolver::dump() const {
std::string dump = dumpFocusedWindows();
-
- if (mPendingFocusRequests.empty()) {
- return dump + INDENT "PendingFocusRequests: <none>\n";
+ if (mFocusRequestByDisplay.empty()) {
+ return dump + INDENT "FocusRequests: <none>\n";
}
- dump += INDENT "PendingFocusRequests:\n";
- for (const auto& [displayId, request] : mPendingFocusRequests) {
- dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- request.windowName.c_str());
+ dump += INDENT "FocusRequests:\n";
+ for (const auto& [displayId, request] : mFocusRequestByDisplay) {
+ auto it = mLastFocusResultByDisplay.find(displayId);
+ std::string result =
+ it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
+ displayId, request.windowName.c_str(), result.c_str());
}
return dump;
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index e067ad9..dc5eeeb 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -34,11 +34,18 @@
// is visible with the same token and all window handles with the same token are focusable.
// See FocusResolver::isTokenFocusable
//
-// Focus request - Request will be granted if the window is focusable. If the window is not
-// visible, then the request is kept in a pending state and granted when it becomes visible.
-// If window becomes not focusable, or another request comes in, the pending request is dropped.
+// Focus request - Request will be granted if the window is focusable. If it's not
+// focusable, then the request is persisted and granted when it becomes focusable. The currently
+// focused window will lose focus and any pending keys will be added to a queue so it can be sent
+// to the window when it gets focus.
+//
+// Condition focus request - Request with a focus token specified. Request will be granted if the
+// window is focusable and the focus token is the currently focused. Otherwise, the request is
+// dropped. Conditional focus requests are not persisted. The window will lose focus and go back
+// to the focus token if it becomes not focusable.
//
// Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+// If the previous focus request is focusable, then we will try to grant that window focus.
class FocusResolver {
public:
// Returns the focused window token on the specified display.
@@ -61,7 +68,7 @@
std::string dump() const;
private:
- enum class FocusResult {
+ enum class Focusability {
OK,
NO_WINDOW,
NOT_FOCUSABLE,
@@ -77,8 +84,8 @@
// we expect the focusability of the windows to match since its hard to reason why one window
// can receive focus events and the other cannot when both are backed by the same input channel.
//
- static FocusResult isTokenFocusable(const sp<IBinder>& token,
- const std::vector<sp<InputWindowHandle>>& windows);
+ static Focusability isTokenFocusable(const sp<IBinder>& token,
+ const std::vector<sp<InputWindowHandle>>& windows);
// Focus tracking for keys, trackball, etc. A window token can be associated with one or
// more InputWindowHandles. If a window is mirrored, the window and its mirror will share
@@ -87,15 +94,18 @@
typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
- // This map will store a single pending focus request per display that cannot be currently
- // processed. This can happen if the window requested to be focused is not currently visible.
- // Such a window might become visible later, and these requests would be processed at that time.
- std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
+ // This map will store the focus request per display. When the input window handles are updated,
+ // the current request will be checked to see if it can be processed at that time.
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay;
+
+ // Last reason for not granting a focus request. This is used to add more debug information
+ // in the event logs.
+ std::unordered_map<int32_t /* displayId */, Focusability> mLastFocusResultByDisplay;
std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
int32_t displayId, const std::string& reason, const sp<IBinder>& token,
const std::string& tokenName = "");
- std::optional<FocusRequest> getPendingRequest(int32_t displayId);
+ std::optional<FocusRequest> getFocusRequest(int32_t displayId);
};
} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9898e9d..d0a5c09 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2591,6 +2591,7 @@
std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventEntry.eventTime;
commandEntry->userActivityEventType = eventType;
+ commandEntry->displayId = displayId;
postCommandLocked(std::move(commandEntry));
}
@@ -5715,7 +5716,8 @@
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
+ commandEntry->displayId);
mLock.lock();
}
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 909ce54..439d85e 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -134,7 +134,7 @@
uint32_t policyFlags) = 0;
/* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
/* Checks whether a given application pid/uid has permission to inject input events
* into other applications.
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a0d3b6a..d0a9ca7 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,6 +37,7 @@
// #define LOG_NDEBUG 0
#include <android-base/file.h>
+#include <android-base/strings.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <input/KeyCharacterMap.h>
@@ -1361,13 +1362,14 @@
// Some devices report battery capacity as an integer through the "capacity" file
if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
- return std::stoi(buffer);
+ return std::stoi(base::Trim(buffer));
}
// Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
// These values are taken from kernel source code include/linux/power_supply.h
if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
- const auto it = BATTERY_LEVEL.find(buffer);
+ // Remove any white space such as trailing new line
+ const auto it = BATTERY_LEVEL.find(base::Trim(buffer));
if (it != BATTERY_LEVEL.end()) {
return it->second;
}
@@ -1389,9 +1391,8 @@
return std::nullopt;
}
- // Remove trailing new line
- buffer.erase(std::remove(buffer.begin(), buffer.end(), '\n'), buffer.end());
- const auto it = BATTERY_STATUS.find(buffer);
+ // Remove white space like trailing new line
+ const auto it = BATTERY_STATUS.find(base::Trim(buffer));
if (it != BATTERY_STATUS.end()) {
return it->second;
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ef3dd65..17efb5b 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -18,6 +18,12 @@
#include "../FocusResolver.h"
+#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
+ { \
+ ASSERT_EQ(_oldFocus, _changes->oldFocus); \
+ ASSERT_EQ(_newFocus, _changes->newFocus); \
+ }
+
// atest inputflinger_tests:FocusResolverTest
namespace android::inputdispatcher {
@@ -56,8 +62,7 @@
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
ASSERT_EQ(request.displayId, changes->displayId);
// invisible window cannot get focused
@@ -65,6 +70,7 @@
changes = focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->oldFocus);
ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
// unfocusableWindowToken window cannot get focused
request.token = unfocusableWindowToken;
@@ -99,19 +105,17 @@
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
// mirrored window with one visible window can get focused
request.token = invisibleWindowToken;
changes = focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);
// mirrored window with one or more unfocusable window cannot get focused
request.token = unfocusableWindowToken;
changes = focusResolver.setFocusedWindow(request, windows);
- ASSERT_FALSE(changes);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, SetInputWindows) {
@@ -130,11 +134,10 @@
focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->newFocus);
- // Window visibility changes and the window loses focused
+ // Window visibility changes and the window loses focus
window->setVisible(false);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->newFocus);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, FocusRequestsCanBePending) {
@@ -158,8 +161,100 @@
// Window visibility changes and the window gets focused
invisibleWindow->setVisible(true);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
+}
+
+TEST(FocusResolverTest, FocusRequestsArePersistent) {
+ sp<IBinder> windowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
+ false /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // non-focusable window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = windowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Focusability changes and the window gets focused
+ window->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Visibility changes and the window loses focus
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Visibility changes and the window gets focused
+ window->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Window is gone and the window loses focus
+ changes = focusResolver.setInputWindows(request.displayId, {});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Window returns and the window gains focus
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+}
+
+TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
+ sp<IBinder> hostWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> hostWindow =
+ new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(hostWindow);
+ sp<IBinder> embeddedWindowToken = new BBinder();
+ sp<FakeWindowHandle> embeddedWindow =
+ new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(embeddedWindow);
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = hostWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
+
+ request.focusedToken = hostWindow->getToken();
+ request.token = embeddedWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
+
+ embeddedWindow->setFocusable(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // The embedded window is no longer focusable, provide focus back to the original focused
+ // window.
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
+
+ embeddedWindow->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // The embedded window is focusable again, but we it cannot gain focus unless there is another
+ // focus request.
+ ASSERT_FALSE(changes);
+
+ embeddedWindow->setVisible(false);
+ changes = focusResolver.setFocusedWindow(request, windows);
+ // If the embedded window is not visible/focusable, then we do not grant it focus and the
+ // request is dropped.
+ ASSERT_FALSE(changes);
+
+ embeddedWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // If the embedded window becomes visble/focusable, nothing changes since the request has been
+ // dropped.
+ ASSERT_FALSE(changes);
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7ad5152..e027c5e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -377,7 +377,7 @@
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
@@ -908,6 +908,8 @@
mInfo.addTouchableRegion(frame);
}
+ void addFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags |= flags; }
+
void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; }
@@ -4318,6 +4320,17 @@
mTouchWindow->assertNoEvents();
}
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedOcclusionMode_DoesNotReceiveTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ w->assertNoEvents();
+}
+
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
@@ -4358,6 +4371,48 @@
mTouchWindow->consumeAnyMotionDown();
}
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_DoesNotReceiveTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ w->assertNoEvents();
+}
+
+/**
+ * This is important to make sure apps can't indirectly learn the position of touches (outside vs
+ * inside) while letting them pass-through. Note that even though touch passes through the occluding
+ * window, the occluding window will still receive ACTION_OUTSIDE event.
+ */
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithZeroOpacityAndWatchOutside_ReceivesOutsideEvent) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ w->consumeMotionOutside();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, OutsideEvent_HasZeroCoordinates) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
+ w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ InputEvent* event = w->consume();
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ EXPECT_EQ(0.0f, motionEvent.getRawPointerCoords(0)->getX());
+ EXPECT_EQ(0.0f, motionEvent.getRawPointerCoords(0)->getY());
+}
+
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index ad93a65..a489253 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_benchmark {
name: "libpowermanager_benchmarks",
srcs: [
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 0f5037e..69e4041 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_test {
name: "libpowermanager_test",
test_suites: ["device-tests"],
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 625f315..470059a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -154,6 +154,7 @@
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"Layer.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 50bc5ed..f9e5b9a 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -29,6 +29,7 @@
"liblog",
"libnativewindow",
"libprotobuf-cpp-lite",
+ "libSurfaceFlingerProp",
"libtimestats",
"libui",
"libutils",
@@ -51,6 +52,11 @@
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
srcs: [
+ "src/planner/CachedSet.cpp",
+ "src/planner/Flattener.cpp",
+ "src/planner/LayerState.cpp",
+ "src/planner/Planner.cpp",
+ "src/planner/Predictor.cpp",
"src/ClientCompositionRequestCache.cpp",
"src/CompositionEngine.cpp",
"src/Display.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 018a687..1fd07b0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -135,6 +135,9 @@
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
+
+ // Gets the sequence number: a serial number that uniquely identifies a Layer
+ virtual int32_t getSequence() const = 0;
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 3be1cc4..4976213 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -31,6 +31,7 @@
#include <ui/Region.h>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
#include "DisplayHardware/DisplayIdentification.h"
@@ -183,6 +184,9 @@
// Outputs a string with a state dump
virtual void dump(std::string&) const = 0;
+ // Outputs planner information
+ virtual void dumpPlannerInfo(const Vector<String16>& args, std::string&) const = 0;
+
// Gets the debug name for the output
virtual const std::string& getName() const = 0;
@@ -264,7 +268,9 @@
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
- virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
+ virtual void updateCompositionState(const CompositionRefreshArgs&) = 0;
+ virtual void planComposition() = 0;
+ virtual void writeCompositionState(const CompositionRefreshArgs&) = 0;
virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
virtual void beginFrame() = 0;
@@ -274,6 +280,7 @@
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
virtual void postFramebuffer() = 0;
+ virtual void renderCachedSets() = 0;
virtual void chooseCompositionStrategy() = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index fb19216..3a84327 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -30,6 +30,8 @@
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
+#include "LayerFE.h"
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -43,7 +45,6 @@
class CompositionEngine;
class Output;
-class LayerFE;
namespace impl {
struct OutputLayerCompositionState;
@@ -88,8 +89,9 @@
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
- // skipped.
- virtual void writeStateToHWC(bool includeGeometry) = 0;
+ // skipped. If skipLayer is true, then the alpha of the layer is forced to
+ // 0 so that HWC will ignore it.
+ virtual void writeStateToHWC(bool includeGeometry, bool skipLayer) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
@@ -115,6 +117,10 @@
// Returns true if the composition settings scale pixels
virtual bool needsFiltering() const = 0;
+ // Returns a composition list to be used by RenderEngine if the layer has been overridden
+ // during the composition process
+ virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0;
+
// Debugging
virtual void dump(std::string& result) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 651230c..eeb20fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -28,10 +28,15 @@
namespace android::compositionengine::impl {
+namespace planner {
+class Planner;
+} // namespace planner
+
// The implementation class contains the common implementation, but does not
// actually contain the final output state.
class Output : public virtual compositionengine::Output {
public:
+ Output();
~Output() override;
// compositionengine::Output overrides
@@ -48,6 +53,7 @@
void setColorProfile(const ColorProfile&) override;
void dump(std::string&) const override;
+ void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override;
const std::string& getName() const override;
void setName(const std::string&) override;
@@ -77,7 +83,9 @@
void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
- void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+ void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+ void planComposition() override;
+ void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override;
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
@@ -86,12 +94,14 @@
std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
void postFramebuffer() override;
+ void renderCachedSets() override;
void cacheClientCompositionRequests(uint32_t) override;
// Testing
const ReleasedLayers& getReleasedLayersForTest() const;
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+ bool plannerEnabled() const { return mPlanner != nullptr; }
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -130,6 +140,7 @@
ReleasedLayers mReleasedLayers;
OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
+ std::unique_ptr<planner::Planner> mPlanner;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 8cb5ae8..f113c34 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
@@ -41,7 +42,7 @@
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
- void writeStateToHWC(bool) override;
+ void writeStateToHWC(bool includeGeometry, bool skipLayer) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -51,6 +52,7 @@
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
+ std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
void dump(std::string&) const override;
@@ -66,7 +68,8 @@
private:
Rect calculateInitialCrop() const;
void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
- void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
+ bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 9a118d3..5f834be 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -84,6 +84,13 @@
// The Z order index of this layer on this output
uint32_t z{0};
+ // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
+ struct {
+ sp<GraphicBuffer> buffer = nullptr;
+ sp<Fence> acquireFence = nullptr;
+ Rect displayFrame = {};
+ } overrideInfo;
+
/*
* HWC state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
new file mode 100644
index 0000000..00424b2
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+#include <chrono>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+
+std::string durationString(std::chrono::milliseconds duration);
+
+class LayerState;
+
+class CachedSet {
+public:
+ class Layer {
+ public:
+ Layer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ const LayerState* getState() const { return mState; }
+ const std::string& getName() const { return mState->getName(); }
+ Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
+ const sp<GraphicBuffer>& getBuffer() const { return mState->getBuffer(); }
+ int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
+ NonBufferHash getHash() const { return mHash; }
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+
+ private:
+ const LayerState* mState;
+ NonBufferHash mHash;
+ std::chrono::steady_clock::time_point mLastUpdate;
+ };
+
+ CachedSet(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+ CachedSet(Layer layer);
+
+ void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+ NonBufferHash getFingerprint() const { return mFingerprint; }
+ size_t getLayerCount() const { return mLayers.size(); }
+ const Layer& getFirstLayer() const { return mLayers[0]; }
+ const Rect& getBounds() const { return mBounds; }
+ size_t getAge() const { return mAge; }
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<Fence>& getDrawFence() const { return mDrawFence; }
+
+ NonBufferHash getNonBufferHash() const;
+
+ size_t getComponentDisplayCost() const;
+ size_t getCreationCost() const;
+ size_t getDisplayCost() const;
+
+ bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const;
+ bool hasReadyBuffer() const;
+
+ // Decomposes this CachedSet into a vector of its layers as individual CachedSets
+ std::vector<CachedSet> decompose() const;
+
+ void updateAge(std::chrono::steady_clock::time_point now);
+
+ void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
+ void append(const CachedSet& other) {
+ mBuffer = nullptr;
+ mDrawFence = nullptr;
+
+ mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(other.mBounds);
+ mBounds = boundingRegion.getBounds();
+ }
+ void incrementAge() { ++mAge; }
+
+ void render(renderengine::RenderEngine&);
+
+ void dump(std::string& result) const;
+
+private:
+ CachedSet() = default;
+
+ NonBufferHash mFingerprint = 0;
+ std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
+ std::vector<Layer> mLayers;
+ Rect mBounds = Rect::EMPTY_RECT;
+ size_t mAge = 0;
+ sp<GraphicBuffer> mBuffer;
+ sp<Fence> mDrawFence;
+
+ static const bool sDebugHighlighLayers;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
new file mode 100644
index 0000000..6c86408
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+
+#include <vector>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+using namespace std::chrono_literals;
+
+class LayerState;
+class Predictor;
+
+class Flattener {
+public:
+ Flattener(Predictor& predictor) : mPredictor(predictor) {}
+
+ void setDisplaySize(ui::Size size) { mDisplaySize = size; }
+
+ NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash);
+
+ void renderCachedSets(renderengine::RenderEngine&);
+
+ void reset();
+
+ void dump(std::string& result) const;
+
+private:
+ size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
+
+ void resetActivities(NonBufferHash, std::chrono::steady_clock::time_point now);
+
+ void updateLayersHash();
+
+ bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
+ std::chrono::steady_clock::time_point now);
+
+ void buildCachedSets(std::chrono::steady_clock::time_point now);
+
+ Predictor& mPredictor;
+
+ ui::Size mDisplaySize;
+
+ NonBufferHash mCurrentGeometry;
+ std::chrono::steady_clock::time_point mLastGeometryUpdate;
+
+ std::vector<CachedSet> mLayers;
+ NonBufferHash mLayersHash = 0;
+ std::optional<CachedSet> mNewCachedSet;
+
+ // Statistics
+ size_t mUnflattenedDisplayCost = 0;
+ size_t mFlattenedDisplayCost = 0;
+ std::unordered_map<size_t, size_t> mInitialLayerCounts;
+ std::unordered_map<size_t, size_t> mFinalLayerCounts;
+ size_t mCachedSetCreationCount = 0;
+ size_t mCachedSetCreationCost = 0;
+ std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
+
+ static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
new file mode 100644
index 0000000..d19ac62
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2021 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 <android-base/strings.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <input/Flags.h>
+
+#include <string>
+
+#include "DisplayHardware/Hal.h"
+
+namespace std {
+template <typename T>
+struct hash<android::sp<T>> {
+ size_t operator()(const android::sp<T>& p) { return std::hash<void*>()(p.get()); }
+};
+} // namespace std
+
+namespace android::compositionengine::impl::planner {
+
+using LayerId = int32_t;
+
+// clang-format off
+enum class LayerStateField : uint32_t {
+ Id = 1u << 0,
+ Name = 1u << 1,
+ DisplayFrame = 1u << 2,
+ SourceCrop = 1u << 3,
+ ZOrder = 1u << 4,
+ BufferTransform = 1u << 5,
+ BlendMode = 1u << 6,
+ Alpha = 1u << 7,
+ VisibleRegion = 1u << 8,
+ Dataspace = 1u << 9,
+ ColorTransform = 1u << 10,
+ CompositionType = 1u << 11,
+ SidebandStream = 1u << 12,
+ Buffer = 1u << 13,
+ SolidColor = 1u << 14,
+};
+// clang-format on
+
+std::string to_string(LayerStateField field);
+
+// An abstract interface allows us to iterate over all of the OutputLayerState fields
+// without having to worry about their templated types.
+// See `LayerState::getNonUniqueFields` below.
+class StateInterface {
+public:
+ virtual ~StateInterface() = default;
+
+ virtual Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) = 0;
+
+ virtual size_t getHash(Flags<LayerStateField> skipFields) const = 0;
+
+ virtual LayerStateField getField() const = 0;
+
+ virtual Flags<LayerStateField> getFieldIfDifferent(const StateInterface* other) const = 0;
+
+ virtual bool equals(const StateInterface* other) const = 0;
+
+ virtual std::vector<std::string> toStrings() const = 0;
+};
+
+template <typename T, LayerStateField FIELD>
+class OutputLayerState : public StateInterface {
+public:
+ using ReadFromLayerState = std::function<T(const compositionengine::OutputLayer* layer)>;
+ using ToStrings = std::function<std::vector<std::string>(const T&)>;
+ using Equals = std::function<bool(const T&, const T&)>;
+
+ static ToStrings getDefaultToStrings() {
+ return [](const T& value) {
+ using std::to_string;
+ return std::vector<std::string>{to_string(value)};
+ };
+ }
+
+ static ToStrings getHalToStrings() {
+ return [](const T& value) { return std::vector<std::string>{toString(value)}; };
+ }
+
+ static Equals getDefaultEquals() {
+ return [](const T& lhs, const T& rhs) { return lhs == rhs; };
+ }
+
+ OutputLayerState(ReadFromLayerState reader,
+ ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
+ Equals equals = OutputLayerState::getDefaultEquals())
+ : mReader(reader), mToStrings(toStrings), mEquals(equals) {}
+
+ ~OutputLayerState() override = default;
+
+ // Returns this member's field flag if it was changed
+ Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) override {
+ T newValue = mReader(layer);
+ if (!mEquals(mValue, newValue)) {
+ mValue = newValue;
+ mHash = {};
+ return FIELD;
+ }
+ return {};
+ }
+
+ LayerStateField getField() const override { return FIELD; }
+ const T& get() const { return mValue; }
+
+ size_t getHash(Flags<LayerStateField> skipFields) const override {
+ if (skipFields.test(FIELD)) {
+ return 0;
+ }
+ if (!mHash) {
+ mHash = std::hash<T>{}(mValue);
+ }
+ return *mHash;
+ }
+
+ Flags<LayerStateField> getFieldIfDifferent(const StateInterface* other) const override {
+ if (other->getField() != FIELD) {
+ return {};
+ }
+
+ // The early return ensures that this downcast is sound
+ const OutputLayerState* otherState = static_cast<const OutputLayerState*>(other);
+ return *this != *otherState ? FIELD : Flags<LayerStateField>{};
+ }
+
+ bool equals(const StateInterface* other) const override {
+ if (other->getField() != FIELD) {
+ return false;
+ }
+
+ // The early return ensures that this downcast is sound
+ const OutputLayerState* otherState = static_cast<const OutputLayerState*>(other);
+ return *this == *otherState;
+ }
+
+ std::vector<std::string> toStrings() const override { return mToStrings(mValue); }
+
+ bool operator==(const OutputLayerState& other) const { return mEquals(mValue, other.mValue); }
+ bool operator!=(const OutputLayerState& other) const { return !(*this == other); }
+
+private:
+ const ReadFromLayerState mReader;
+ const ToStrings mToStrings;
+ const Equals mEquals;
+ T mValue = {};
+ mutable std::optional<size_t> mHash = {};
+};
+
+class LayerState {
+public:
+ LayerState(compositionengine::OutputLayer* layer);
+
+ // Returns which fields were updated
+ Flags<LayerStateField> update(compositionengine::OutputLayer*);
+
+ size_t getHash(Flags<LayerStateField> skipFields) const;
+
+ Flags<LayerStateField> getDifferingFields(const LayerState& other,
+ Flags<LayerStateField> skipFields) const;
+
+ compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
+ int32_t getId() const { return mId.get(); }
+ const std::string& getName() const { return mName.get(); }
+ Rect getDisplayFrame() const { return mDisplayFrame.get(); }
+ hardware::graphics::composer::hal::Composition getCompositionType() const {
+ return mCompositionType.get();
+ }
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer.get(); }
+
+ void incrementFramesSinceBufferUpdate() { ++mFramesSinceBufferUpdate; }
+ void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
+ int64_t getFramesSinceBufferUpdate() const { return mFramesSinceBufferUpdate; }
+
+ void dump(std::string& result) const;
+ std::optional<std::string> compare(const LayerState& other) const;
+
+ // This makes LayerState's private members accessible to the operator
+ friend bool operator==(const LayerState& lhs, const LayerState& rhs);
+ friend bool operator!=(const LayerState& lhs, const LayerState& rhs) { return !(lhs == rhs); }
+
+private:
+ compositionengine::OutputLayer* mOutputLayer = nullptr;
+
+ OutputLayerState<LayerId, LayerStateField::Id> mId{
+ [](const compositionengine::OutputLayer* layer) {
+ return layer->getLayerFE().getSequence();
+ }};
+
+ OutputLayerState<std::string, LayerStateField::Name>
+ mName{[](auto layer) { return layer->getLayerFE().getDebugName(); },
+ [](const std::string& name) { return std::vector<std::string>{name}; }};
+
+ // Output-dependent geometry state
+
+ OutputLayerState<Rect, LayerStateField::DisplayFrame>
+ mDisplayFrame{[](auto layer) { return layer->getState().displayFrame; },
+ [](const Rect& rect) {
+ return std::vector<std::string>{
+ base::StringPrintf("[%d, %d, %d, %d]", rect.left, rect.top,
+ rect.right, rect.bottom)};
+ }};
+
+ OutputLayerState<FloatRect, LayerStateField::SourceCrop>
+ mSourceCrop{[](auto layer) { return layer->getState().sourceCrop; },
+ [](const FloatRect& rect) {
+ return std::vector<std::string>{
+ base::StringPrintf("[%.2f, %.2f, %.2f, %.2f]", rect.left,
+ rect.top, rect.right, rect.bottom)};
+ }};
+
+ OutputLayerState<uint32_t, LayerStateField::ZOrder> mZOrder{
+ [](auto layer) { return layer->getState().z; }};
+
+ using BufferTransformState = OutputLayerState<hardware::graphics::composer::hal::Transform,
+ LayerStateField::BufferTransform>;
+ BufferTransformState mBufferTransform{[](auto layer) {
+ return layer->getState().bufferTransform;
+ },
+ BufferTransformState::getHalToStrings()};
+
+ // Output-independent geometry state
+
+ using BlendModeState = OutputLayerState<hardware::graphics::composer::hal::BlendMode,
+ LayerStateField::BlendMode>;
+ BlendModeState mBlendMode{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->blendMode;
+ },
+ BlendModeState::getHalToStrings()};
+
+ OutputLayerState<float, LayerStateField::Alpha> mAlpha{
+ [](auto layer) { return layer->getLayerFE().getCompositionState()->alpha; }};
+
+ // TODO(b/180638831): Generic layer metadata
+
+ // Output-dependent per-frame state
+
+ OutputLayerState<Region, LayerStateField::VisibleRegion>
+ mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+ [](const Region& region) {
+ using namespace std::string_literals;
+ std::string dump;
+ region.dump(dump, "");
+ std::vector<std::string> split = base::Split(dump, "\n"s);
+ split.erase(split.begin()); // Strip the header
+ split.pop_back(); // Strip the last (empty) line
+ for (std::string& line : split) {
+ line.erase(0, 4); // Strip leading padding before each rect
+ }
+ return split;
+ },
+ [](const Region& lhs, const Region& rhs) {
+ return lhs.hasSameRects(rhs);
+ }};
+
+ using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
+ DataspaceState mDataspace{[](auto layer) { return layer->getState().dataspace; },
+ DataspaceState::getHalToStrings()};
+
+ // TODO(b/180638831): Buffer format
+
+ // Output-independent per-frame state
+
+ OutputLayerState<mat4, LayerStateField::ColorTransform>
+ mColorTransform{[](auto layer) {
+ const auto state = layer->getLayerFE().getCompositionState();
+ return state->colorTransformIsIdentity ? mat4{}
+ : state->colorTransform;
+ },
+ [](const mat4& mat) {
+ using namespace std::string_literals;
+ std::vector<std::string> split =
+ base::Split(std::string(mat.asString().string()), "\n"s);
+ split.pop_back(); // Strip the last (empty) line
+ return split;
+ }};
+
+ // TODO(b/180638831): Surface damage
+
+ using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
+ LayerStateField::CompositionType>;
+ CompositionTypeState
+ mCompositionType{[](auto layer) {
+ return layer->getState().forceClientComposition
+ ? hardware::graphics::composer::hal::Composition::CLIENT
+ : layer->getLayerFE()
+ .getCompositionState()
+ ->compositionType;
+ },
+ CompositionTypeState::getHalToStrings()};
+
+ OutputLayerState<void*, LayerStateField::SidebandStream>
+ mSidebandStream{[](auto layer) {
+ return layer->getLayerFE()
+ .getCompositionState()
+ ->sidebandStream.get();
+ },
+ [](void* p) {
+ return std::vector<std::string>{base::StringPrintf("%p", p)};
+ }};
+
+ OutputLayerState<sp<GraphicBuffer>, LayerStateField::Buffer>
+ mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
+ [](const sp<GraphicBuffer>& buffer) {
+ return std::vector<std::string>{base::StringPrintf("%p", buffer.get())};
+ }};
+
+ int64_t mFramesSinceBufferUpdate = 0;
+
+ OutputLayerState<half4, LayerStateField::SolidColor>
+ mSolidColor{[](auto layer) { return layer->getLayerFE().getCompositionState()->color; },
+ [](const half4& vec) {
+ std::stringstream stream;
+ stream << vec;
+ return std::vector<std::string>{stream.str()};
+ }};
+
+ std::array<StateInterface*, 13> getNonUniqueFields() {
+ std::array<const StateInterface*, 13> constFields =
+ const_cast<const LayerState*>(this)->getNonUniqueFields();
+ std::array<StateInterface*, 13> fields;
+ std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
+ [](const StateInterface* constField) {
+ return const_cast<StateInterface*>(constField);
+ });
+ return fields;
+ }
+
+ std::array<const StateInterface*, 13> getNonUniqueFields() const {
+ return {
+ &mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
+ &mBlendMode, &mAlpha, &mVisibleRegion, &mDataspace,
+ &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
+ &mSolidColor,
+ };
+ }
+};
+
+using NonBufferHash = size_t;
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>&);
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
new file mode 100644
index 0000000..e96abb7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2021 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 <compositionengine/Output.h>
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+
+// This is the top level class for layer caching. It is responsible for
+// heuristically determining the composition strategy of the current layer stack,
+// and flattens inactive layers into an override buffer so it can be used
+// as a more efficient representation of parts of the layer stack.
+class Planner {
+public:
+ Planner() : mFlattener(mPredictor) {}
+
+ void setDisplaySize(ui::Size);
+
+ // Updates the Planner with the current set of layers before a composition strategy is
+ // determined.
+ // The Planner will call to the Flattener to determine to:
+ // 1. Replace any cached sets with a newly available flattened cached set
+ // 2. Create a new cached set if possible
+ void plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+
+ // Updates the Planner with the current set of layers after a composition strategy is
+ // determined.
+ void reportFinalPlan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+
+ // The planner will call to the Flattener to render any pending cached set
+ void renderCachedSets(renderengine::RenderEngine&);
+
+ void dump(const Vector<String16>& args, std::string&);
+
+private:
+ void dumpUsage(std::string&) const;
+
+ std::unordered_map<LayerId, LayerState> mPreviousLayers;
+
+ std::vector<const LayerState*> mCurrentLayers;
+
+ Predictor mPredictor;
+ Flattener mFlattener;
+
+ std::optional<Predictor::PredictedPlan> mPredictedPlan;
+ NonBufferHash mFlattenedHash = 0;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
new file mode 100644
index 0000000..422af77
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+namespace android::compositionengine::impl::planner {
+
+class LayerStack {
+public:
+ LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {}
+
+ struct ApproximateMatch {
+ bool operator==(const ApproximateMatch& other) const {
+ return differingIndex == other.differingIndex &&
+ differingFields == other.differingFields;
+ }
+
+ size_t differingIndex;
+ Flags<LayerStateField> differingFields;
+ };
+
+ std::optional<ApproximateMatch> getApproximateMatch(
+ const std::vector<const LayerState*>& other) const;
+
+ void compare(const LayerStack& other, std::string& result) const {
+ if (mLayers.size() != other.mLayers.size()) {
+ base::StringAppendF(&result, "Cannot compare stacks of different sizes (%zd vs. %zd)\n",
+ mLayers.size(), other.mLayers.size());
+ return;
+ }
+
+ for (size_t l = 0; l < mLayers.size(); ++l) {
+ const auto& thisLayer = mLayers[l];
+ const auto& otherLayer = other.mLayers[l];
+ base::StringAppendF(&result, "\n+ - - - - - - - - - Layer %d [%s]\n", thisLayer.getId(),
+ thisLayer.getName().c_str());
+ auto comparisonOpt = thisLayer.compare(otherLayer);
+ base::StringAppendF(&result,
+ " %s + - - - - - - - - - - - - - - - - - - - - - - - "
+ "- Layer %d [%s]\n",
+ comparisonOpt ? " " : "Identical", otherLayer.getId(),
+ otherLayer.getName().c_str());
+ if (comparisonOpt) {
+ result.append(*comparisonOpt);
+ }
+ }
+ }
+
+ void dump(std::string& result) const {
+ for (const LayerState& layer : mLayers) {
+ base::StringAppendF(&result, "+ - - - - - - - - - Layer %d [%s]\n", layer.getId(),
+ layer.getName().c_str());
+ layer.dump(result);
+ }
+ }
+
+ void dumpLayerNames(std::string& result, const std::string& prefix = " ") const {
+ for (const LayerState& layer : mLayers) {
+ result.append(prefix);
+ result.append(layer.getName());
+ result.append("\n");
+ }
+ }
+
+private:
+ std::vector<const LayerState> copyLayers(const std::vector<const LayerState*>& layers) {
+ std::vector<const LayerState> copiedLayers;
+ copiedLayers.reserve(layers.size());
+ std::transform(layers.cbegin(), layers.cend(), std::back_inserter(copiedLayers),
+ [](const LayerState* layerState) { return *layerState; });
+ return copiedLayers;
+ }
+
+ std::vector<const LayerState> mLayers;
+
+ // TODO(b/180976743): Tune kMaxDifferingFields
+ constexpr static int kMaxDifferingFields = 6;
+};
+
+class Plan {
+public:
+ static std::optional<Plan> fromString(const std::string&);
+
+ void reset() { mLayerTypes.clear(); }
+ void addLayerType(hardware::graphics::composer::hal::Composition type) {
+ mLayerTypes.emplace_back(type);
+ }
+
+ friend std::string to_string(const Plan& plan);
+
+ friend bool operator==(const Plan& lhs, const Plan& rhs) {
+ return lhs.mLayerTypes == rhs.mLayerTypes;
+ }
+ friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); }
+
+private:
+ std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
+};
+
+} // namespace android::compositionengine::impl::planner
+
+namespace std {
+template <>
+struct hash<android::compositionengine::impl::planner::Plan> {
+ size_t operator()(const android::compositionengine::impl::planner::Plan& plan) const {
+ return std::hash<std::string>{}(to_string(plan));
+ }
+};
+} // namespace std
+
+namespace android::compositionengine::impl::planner {
+
+class Prediction {
+public:
+ enum class Type {
+ Exact,
+ Approximate,
+ Total,
+ };
+
+ friend std::string to_string(Type type) {
+ using namespace std::string_literals;
+
+ switch (type) {
+ case Type::Exact:
+ return "Exact";
+ case Type::Approximate:
+ return "Approximate";
+ case Type::Total:
+ return "Total";
+ }
+ }
+
+ Prediction(const std::vector<const LayerState*>& layers, Plan plan)
+ : mExampleLayerStack(layers), mPlan(std::move(plan)) {}
+
+ const LayerStack& getExampleLayerStack() const { return mExampleLayerStack; }
+ const Plan& getPlan() const { return mPlan; }
+
+ size_t getHitCount(Type type) const {
+ if (type == Type::Total) {
+ return getHitCount(Type::Exact) + getHitCount(Type::Approximate);
+ }
+ return getStatsForType(type).hitCount;
+ }
+
+ size_t getMissCount(Type type) const {
+ if (type == Type::Total) {
+ return getMissCount(Type::Exact) + getMissCount(Type::Approximate);
+ }
+ return getStatsForType(type).missCount;
+ }
+
+ void recordHit(Type type) { ++getStatsForType(type).hitCount; }
+
+ void recordMiss(Type type) { ++getStatsForType(type).missCount; }
+
+ void dump(std::string&) const;
+
+private:
+ struct Stats {
+ void dump(std::string& result) const {
+ const size_t totalAttempts = hitCount + missCount;
+ base::StringAppendF(&result, "%.2f%% (%zd/%zd)", 100.0f * hitCount / totalAttempts,
+ hitCount, totalAttempts);
+ }
+
+ size_t hitCount = 0;
+ size_t missCount = 0;
+ };
+
+ const Stats& getStatsForType(Type type) const {
+ return (type == Type::Exact) ? mExactStats : mApproximateStats;
+ }
+
+ Stats& getStatsForType(Type type) {
+ return const_cast<Stats&>(const_cast<const Prediction*>(this)->getStatsForType(type));
+ }
+
+ LayerStack mExampleLayerStack;
+ Plan mPlan;
+
+ Stats mExactStats;
+ Stats mApproximateStats;
+};
+
+class Predictor {
+public:
+ struct PredictedPlan {
+ NonBufferHash hash;
+ Plan plan;
+ Prediction::Type type;
+ };
+
+ std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&,
+ NonBufferHash) const;
+
+ void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
+
+ void dump(std::string&) const;
+
+ void compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash, std::string&) const;
+ void describeLayerStack(NonBufferHash, std::string&) const;
+ void listSimilarStacks(Plan, std::string&) const;
+
+private:
+ // Retrieves a prediction from either the main prediction list or from the candidate list
+ const Prediction& getPrediction(NonBufferHash) const;
+ Prediction& getPrediction(NonBufferHash);
+
+ std::optional<Plan> getExactMatch(NonBufferHash) const;
+ std::optional<NonBufferHash> getApproximateMatch(
+ const std::vector<const LayerState*>& layers) const;
+
+ void promoteIfCandidate(NonBufferHash);
+ void recordPredictedResult(PredictedPlan, const std::vector<const LayerState*>& layers,
+ Plan result);
+ bool findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result);
+
+ void dumpPredictionsByFrequency(std::string&) const;
+
+ struct PromotionCandidate {
+ PromotionCandidate(NonBufferHash hash, Prediction&& prediction)
+ : hash(hash), prediction(std::move(prediction)) {}
+
+ NonBufferHash hash;
+ Prediction prediction;
+ };
+
+ static constexpr const size_t MAX_CANDIDATES = 4;
+ std::deque<PromotionCandidate> mCandidates;
+ decltype(mCandidates)::const_iterator getCandidateEntryByHash(NonBufferHash hash) const {
+ const auto candidateMatches = [&](const PromotionCandidate& candidate) {
+ return candidate.hash == hash;
+ };
+
+ return std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches);
+ }
+
+ std::unordered_map<NonBufferHash, Prediction> mPredictions;
+ std::unordered_map<Plan, std::vector<NonBufferHash>> mSimilarStacks;
+
+ struct ApproximateStack {
+ ApproximateStack(NonBufferHash hash, LayerStack::ApproximateMatch match)
+ : hash(hash), match(match) {}
+
+ bool operator==(const ApproximateStack& other) const {
+ return hash == other.hash && match == other.match;
+ }
+
+ NonBufferHash hash;
+ LayerStack::ApproximateMatch match;
+ };
+
+ std::vector<ApproximateStack> mApproximateStacks;
+
+ mutable size_t mExactHitCount = 0;
+ mutable size_t mApproximateHitCount = 0;
+ mutable size_t mMissCount = 0;
+};
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 45891a7..dde8999 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -42,6 +42,7 @@
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
+ MOCK_CONST_METHOD0(getSequence, int32_t());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 95db4da..5aa53e5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -45,6 +45,7 @@
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&));
MOCK_CONST_METHOD0(getName, const std::string&());
MOCK_METHOD1(setName, void(const std::string&));
@@ -85,7 +86,9 @@
MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
- MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
+ MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&));
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
@@ -104,6 +107,7 @@
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 81e1fc7..2454ff7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -39,7 +39,7 @@
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
- MOCK_METHOD1(writeStateToHWC, void(bool));
+ MOCK_METHOD2(writeStateToHWC, void(bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
@@ -49,6 +49,7 @@
MOCK_METHOD0(prepareForDeviceLayerRequests, void());
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
+ MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3907ac5..dc1aacc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -27,6 +27,9 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+#include <SurfaceFlingerProperties.sysprop.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -38,6 +41,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include <android-base/properties.h>
#include <ui/DebugUtils.h>
#include <ui/HdrCapabilities.h>
#include <utils/Trace.h>
@@ -50,6 +54,18 @@
namespace impl {
+Output::Output() {
+ const bool enableLayerCaching = [] {
+ const bool enable =
+ android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+ return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+ }();
+
+ if (enableLayerCaching) {
+ mPlanner = std::make_unique<planner::Planner>();
+ }
+}
+
namespace {
template <typename T>
@@ -182,6 +198,10 @@
const Rect newOrientedBounds(orientedSize);
state.orientedDisplaySpace.bounds = newOrientedBounds;
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
+
dirtyEntireOutput();
}
@@ -269,6 +289,15 @@
}
}
+void Output::dumpPlannerInfo(const Vector<String16>& args, std::string& out) const {
+ if (!mPlanner) {
+ base::StringAppendF(&out, "Planner is disabled\n");
+ return;
+ }
+ base::StringAppendF(&out, "Planner info for display [%s]\n", mName.c_str());
+ mPlanner->dump(args, out);
+}
+
compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
return mDisplayColorProfile.get();
}
@@ -292,7 +321,11 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
+ const auto size = mRenderSurface->getSize();
+ editState().framebufferSpace.bounds = Rect(size);
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
dirtyEntireOutput();
}
@@ -368,13 +401,16 @@
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
- updateAndWriteCompositionState(refreshArgs);
+ updateCompositionState(refreshArgs);
+ planComposition();
+ writeCompositionState(refreshArgs);
setColorTransform(refreshArgs);
beginFrame();
prepareFrame();
devOptRepaintFlash(refreshArgs);
finishFrame(refreshArgs);
postFramebuffer();
+ renderCachedSets();
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -632,8 +668,7 @@
}
}
-void Output::updateAndWriteCompositionState(
- const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -653,9 +688,45 @@
if (mLayerRequestingBackgroundBlur == layer) {
forceClientComposition = false;
}
+ }
+}
- // Send the updated state to the HWC, if appropriate.
- layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+void Output::planComposition() {
+ if (!mPlanner || !getState().isEnabled) {
+ return;
+ }
+
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ mPlanner->plan(getOutputLayersOrderedByZ());
+}
+
+void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ if (!getState().isEnabled) {
+ return;
+ }
+
+ sp<GraphicBuffer> previousOverride = nullptr;
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ bool skipLayer = false;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ if (previousOverride != nullptr &&
+ layer->getState().overrideInfo.buffer == previousOverride) {
+ ALOGV("Skipping redundant buffer");
+ skipLayer = true;
+ }
+ previousOverride = layer->getState().overrideInfo.buffer;
+ }
+
+ // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+ // only when the geometry actually changes
+ const bool includeGeometry = refreshArgs.updatingGeometryThisFrame ||
+ layer->getState().overrideInfo.buffer != nullptr || skipLayer;
+ layer->writeStateToHWC(includeGeometry, skipLayer);
}
}
@@ -826,6 +897,10 @@
chooseCompositionStrategy();
+ if (mPlanner) {
+ mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+ }
+
mRenderSurface->prepareFrame(outputState.usesClientComposition,
outputState.usesDeviceComposition);
}
@@ -1075,10 +1150,16 @@
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
.disableBlurs = disableBlurs};
- std::vector<LayerFE::LayerSettings> results =
- layerFE.prepareClientCompositionList(targetSettings);
- if (realContentIsVisible && !results.empty()) {
- layer->editState().clientCompositionTimestamp = systemTime();
+
+ std::vector<LayerFE::LayerSettings> results;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ results = layer->getOverrideCompositionList();
+ ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ } else {
+ results = layerFE.prepareClientCompositionList(targetSettings);
+ if (realContentIsVisible && !results.empty()) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ }
}
clientCompositionLayers.insert(clientCompositionLayers.end(),
@@ -1169,6 +1250,12 @@
mReleasedLayers.clear();
}
+void Output::renderCachedSets() {
+ if (mPlanner) {
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine());
+ }
+}
+
void Output::dirtyEntireOutput() {
auto& outputState = editState();
outputState.dirtyRegion.set(outputState.displaySpace.bounds);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0faab6f..54784a2 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,7 +16,6 @@
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -313,7 +312,7 @@
}
}
-void OutputLayer::writeStateToHWC(bool includeGeometry) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -336,7 +335,8 @@
if (includeGeometry) {
writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
- writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+ writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
+ skipLayer);
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
@@ -352,23 +352,27 @@
HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
const auto& outputDependentState = getState();
- if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
- getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
- outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
- outputDependentState.displayFrame.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ Rect displayFrame = outputDependentState.displayFrame;
+ FloatRect sourceCrop = outputDependentState.sourceCrop;
+ if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
+ displayFrame = outputDependentState.overrideInfo.displayFrame;
+ sourceCrop = displayFrame.toFloatRect();
}
- if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
- error != hal::Error::NONE) {
+ ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
+ displayFrame.right, displayFrame.bottom);
+
+ if (auto error = hwcLayer->setDisplayFrame(displayFrame); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+ getLayerFE().getDebugName(), displayFrame.left, displayFrame.top, displayFrame.right,
+ displayFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setSourceCrop(sourceCrop); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
"%s (%d)",
- getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
- outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
- outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ getLayerFE().getDebugName(), sourceCrop.left, sourceCrop.top, sourceCrop.right,
+ sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
@@ -389,7 +393,8 @@
}
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+ HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
error != hal::Error::NONE) {
ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
@@ -397,10 +402,12 @@
static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+ const float alpha = skipLayer ? 0.0f : outputIndependentState.alpha;
+ ALOGV("Writing alpha %f", alpha);
+
+ if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(), alpha,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
for (const auto& [name, entry] : outputIndependentState.metadata) {
@@ -509,19 +516,26 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
+ sp<GraphicBuffer> buffer = outputIndependentState.buffer;
+ sp<Fence> acquireFence = outputIndependentState.acquireFence;
+ if (getState().overrideInfo.buffer != nullptr) {
+ buffer = getState().overrideInfo.buffer;
+ acquireFence = getState().overrideInfo.acquireFence;
+ }
+
+ ALOGV("Writing buffer %p", buffer.get());
+
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
// We need access to the output-dependent state for the buffer cache there,
// though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot,
- outputIndependentState.buffer, &hwcSlot,
- &hwcBuffer);
+ editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
+ &hwcSlot, &hwcBuffer);
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.buffer->handle, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -652,6 +666,26 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
+std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+ if (getState().overrideInfo.buffer == nullptr) {
+ return {};
+ }
+
+ LayerFE::LayerSettings settings;
+ settings.geometry = renderengine::Geometry{
+ .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+ };
+ settings.bufferId = getState().overrideInfo.buffer->getId();
+ settings.source =
+ renderengine::PixelSource{.buffer = renderengine::Buffer{
+ .buffer = getState().overrideInfo.buffer,
+ .fence = getState().overrideInfo.acquireFence,
+ }};
+ settings.alpha = 1.0f;
+
+ return {static_cast<LayerFE::LayerSettings>(settings)};
+}
+
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
new file mode 100644
index 0000000..ab3fe9e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+// #define LOG_NDEBUG 0
+
+#include <android-base/properties.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <math/HashCombine.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/RenderEngine.h>
+
+namespace android::compositionengine::impl::planner {
+
+const bool CachedSet::sDebugHighlighLayers =
+ base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
+
+std::string durationString(std::chrono::milliseconds duration) {
+ using namespace std::chrono_literals;
+
+ std::string result;
+
+ if (duration >= 1h) {
+ const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
+ base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
+ duration -= hours;
+ }
+ if (duration >= 1min) {
+ const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
+ base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
+ duration -= minutes;
+ }
+ base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
+
+ return result;
+}
+
+CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
+ : mState(state), mHash(state->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+
+CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
+ : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+ addLayer(layer, lastUpdate);
+}
+
+CachedSet::CachedSet(Layer layer)
+ : mFingerprint(layer.getHash()),
+ mLastUpdate(layer.getLastUpdate()),
+ mBounds(layer.getDisplayFrame()) {
+ mLayers.emplace_back(std::move(layer));
+}
+
+void CachedSet::addLayer(const LayerState* layer,
+ std::chrono::steady_clock::time_point lastUpdate) {
+ mLayers.emplace_back(layer, lastUpdate);
+
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(layer->getDisplayFrame());
+ mBounds = boundingRegion.getBounds();
+}
+
+NonBufferHash CachedSet::getNonBufferHash() const {
+ if (mLayers.size() == 1) {
+ return mFingerprint;
+ }
+
+ // TODO(b/181192080): Add all fields which contribute to geometry of override layer (e.g.,
+ // dataspace)
+ size_t hash = 0;
+ android::hashCombineSingle(hash, mBounds);
+ return hash;
+}
+
+size_t CachedSet::getComponentDisplayCost() const {
+ size_t displayCost = 0;
+
+ for (const Layer& layer : mLayers) {
+ displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
+ layer.getDisplayFrame().height());
+ }
+
+ return displayCost;
+}
+
+size_t CachedSet::getCreationCost() const {
+ if (mLayers.size() == 1) {
+ return 0;
+ }
+
+ // Reads
+ size_t creationCost = getComponentDisplayCost();
+
+ // Write - assumes that the output buffer only gets written once per pixel
+ creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
+
+ return creationCost;
+}
+
+size_t CachedSet::getDisplayCost() const {
+ return static_cast<size_t>(mBounds.width() * mBounds.height());
+}
+
+bool CachedSet::hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const {
+ for (const Layer& layer : mLayers) {
+ if (layer.getFramesSinceBufferUpdate() == 0) {
+ return true;
+ }
+ ++layers;
+ }
+ return false;
+}
+
+bool CachedSet::hasReadyBuffer() const {
+ return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+}
+
+std::vector<CachedSet> CachedSet::decompose() const {
+ std::vector<CachedSet> layers;
+
+ std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
+ [](Layer layer) { return CachedSet(std::move(layer)); });
+
+ return layers;
+}
+
+void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
+ LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
+ __func__);
+
+ if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
+ mLastUpdate = now;
+ mAge = 0;
+ }
+}
+
+void CachedSet::render(renderengine::RenderEngine& renderEngine) {
+ renderengine::DisplaySettings displaySettings{
+ .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+ .clip = mBounds,
+ };
+
+ Region clearRegion = Region::INVALID_REGION;
+ Rect viewport = mBounds;
+ LayerFE::ClientCompositionTargetSettings targetSettings{
+ .clip = Region(mBounds),
+ .needsFiltering = false,
+ .isSecure = true,
+ .supportsProtectedContent = false,
+ .clearRegion = clearRegion,
+ .viewport = viewport,
+ // TODO(181192086): Propagate the Output's dataspace instead of using UNKNOWN
+ .dataspace = ui::Dataspace::UNKNOWN,
+ .realContentIsVisible = true,
+ .clearContent = false,
+ .disableBlurs = false,
+ };
+
+ std::vector<renderengine::LayerSettings> layerSettings;
+ for (const auto& layer : mLayers) {
+ const auto clientCompositionList =
+ layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
+ clientCompositionList.cend());
+ }
+
+ std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
+ std::transform(layerSettings.cbegin(), layerSettings.cend(),
+ std::back_inserter(layerSettingsPointers),
+ [](const renderengine::LayerSettings& settings) { return &settings; });
+
+ if (sDebugHighlighLayers) {
+ renderengine::LayerSettings highlight{
+ .geometry =
+ renderengine::Geometry{
+ .boundaries = FloatRect(0.0f, 0.0f,
+ static_cast<float>(mBounds.getWidth()),
+ static_cast<float>(mBounds.getHeight())),
+ },
+ .source =
+ renderengine::PixelSource{
+ .solidColor = half3(0.25f, 0.0f, 0.5f),
+ },
+ .alpha = half(0.05f),
+ };
+
+ layerSettingsPointers.emplace_back(&highlight);
+ }
+
+ const uint64_t usageFlags = GraphicBuffer::USAGE_HW_RENDER | GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()),
+ static_cast<uint32_t>(mBounds.getHeight()),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
+ LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
+ base::unique_fd drawFence;
+ status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
+ base::unique_fd(), &drawFence);
+
+ if (result == NO_ERROR) {
+ mBuffer = buffer;
+ mDrawFence = new Fence(drawFence.release());
+ }
+}
+
+void CachedSet::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
+ base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
+ mFingerprint, durationString(lastUpdate).c_str(), mAge);
+
+ if (mLayers.size() == 1) {
+ base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
+ base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
+ } else {
+ result.append(" Cached set of:");
+ for (const Layer& layer : mLayers) {
+ base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
+ }
+ }
+
+ base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost());
+ base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
new file mode 100644
index 0000000..0c09714
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+// #define LOG_NDEBUG 0
+
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+
+using time_point = std::chrono::steady_clock::time_point;
+using namespace std::chrono_literals;
+
+namespace android::compositionengine::impl::planner {
+
+NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash) {
+ const auto now = std::chrono::steady_clock::now();
+
+ const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
+ mUnflattenedDisplayCost += unflattenedDisplayCost;
+
+ if (mCurrentGeometry != hash) {
+ resetActivities(hash, now);
+ mFlattenedDisplayCost += unflattenedDisplayCost;
+ return hash;
+ }
+
+ ++mInitialLayerCounts[layers.size()];
+
+ if (mergeWithCachedSets(layers, now)) {
+ hash = mLayersHash;
+ }
+
+ ++mFinalLayerCounts[mLayers.size()];
+
+ buildCachedSets(now);
+
+ return hash;
+}
+
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+ if (!mNewCachedSet) {
+ return;
+ }
+
+ mNewCachedSet->render(renderEngine);
+}
+
+void Flattener::reset() {
+ resetActivities(0, std::chrono::steady_clock::now());
+
+ mUnflattenedDisplayCost = 0;
+ mFlattenedDisplayCost = 0;
+ mInitialLayerCounts.clear();
+ mFinalLayerCounts.clear();
+ mCachedSetCreationCount = 0;
+ mCachedSetCreationCost = 0;
+ mInvalidatedCachedSetAges.clear();
+}
+
+void Flattener::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ base::StringAppendF(&result, "Flattener state:\n");
+
+ result.append("\n Statistics:\n");
+
+ result.append(" Display cost (in screen-size buffers):\n");
+ const size_t displayArea = static_cast<size_t>(mDisplaySize.width * mDisplaySize.height);
+ base::StringAppendF(&result, " Unflattened: %.2f\n",
+ static_cast<float>(mUnflattenedDisplayCost) / displayArea);
+ base::StringAppendF(&result, " Flattened: %.2f\n",
+ static_cast<float>(mFlattenedDisplayCost) / displayArea);
+
+ const auto compareLayerCounts = [](const std::pair<size_t, size_t>& left,
+ const std::pair<size_t, size_t>& right) {
+ return left.first < right.first;
+ };
+
+ const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
+ mInitialLayerCounts.cend(), compareLayerCounts)
+ ->first;
+
+ result.append("\n Initial counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t initial = mInitialLayerCounts.count(count) > 0 ? mInitialLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, initial);
+ }
+
+ result.append("\n Final counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t final = mFinalLayerCounts.count(count) > 0 ? mFinalLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, final);
+ }
+
+ base::StringAppendF(&result, "\n Cached sets created: %zd\n", mCachedSetCreationCount);
+ base::StringAppendF(&result, " Cost: %.2f\n",
+ static_cast<float>(mCachedSetCreationCost) / displayArea);
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastGeometryUpdate);
+ base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
+ durationString(lastUpdate).c_str());
+
+ result.append(" Current layers:");
+ for (const CachedSet& layer : mLayers) {
+ result.append("\n");
+ layer.dump(result);
+ }
+}
+
+size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
+ Region coveredRegion;
+ size_t displayCost = 0;
+ bool hasClientComposition = false;
+
+ for (const LayerState* layer : layers) {
+ coveredRegion.orSelf(layer->getDisplayFrame());
+
+ // Regardless of composition type, we always have to read each input once
+ displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
+ layer->getDisplayFrame().height());
+
+ hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+ }
+
+ if (hasClientComposition) {
+ // If there is client composition, the client target buffer has to be both written by the
+ // GPU and read by the DPU, so we pay its cost twice
+ displayCost += 2 *
+ static_cast<size_t>(coveredRegion.bounds().width() *
+ coveredRegion.bounds().height());
+ }
+
+ return displayCost;
+}
+
+void Flattener::resetActivities(NonBufferHash hash, time_point now) {
+ ALOGV("[%s]", __func__);
+
+ mCurrentGeometry = hash;
+ mLastGeometryUpdate = now;
+
+ for (const CachedSet& cachedSet : mLayers) {
+ if (cachedSet.getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[cachedSet.getAge()];
+ }
+ }
+
+ mLayers.clear();
+
+ if (mNewCachedSet) {
+ ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
+ mNewCachedSet = std::nullopt;
+ }
+}
+
+void Flattener::updateLayersHash() {
+ size_t hash = 0;
+ for (const auto& layer : mLayers) {
+ android::hashCombineSingleHashed(hash, layer.getNonBufferHash());
+ }
+ mLayersHash = hash;
+}
+
+bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
+ std::vector<CachedSet> merged;
+
+ if (mLayers.empty()) {
+ merged.reserve(layers.size());
+ for (const LayerState* layer : layers) {
+ merged.emplace_back(layer, now);
+ mFlattenedDisplayCost += merged.back().getDisplayCost();
+ }
+ mLayers = std::move(merged);
+ return false;
+ }
+
+ ALOGV("[%s] Incoming layers:", __func__);
+ for (const LayerState* layer : layers) {
+ ALOGV("%s", layer->getName().c_str());
+ }
+
+ ALOGV("[%s] Current layers:", __func__);
+ for (const CachedSet& layer : mLayers) {
+ std::string dump;
+ layer.dump(dump);
+ ALOGV("%s", dump.c_str());
+ }
+
+ auto currentLayerIter = mLayers.begin();
+ auto incomingLayerIter = layers.begin();
+ while (incomingLayerIter != layers.end()) {
+ if (mNewCachedSet &&
+ mNewCachedSet->getFingerprint() ==
+ (*incomingLayerIter)->getHash(LayerStateField::Buffer)) {
+ if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) {
+ ALOGV("[%s] Dropping new cached set", __func__);
+ ++mInvalidatedCachedSetAges[0];
+ mNewCachedSet = std::nullopt;
+ } else if (mNewCachedSet->hasReadyBuffer()) {
+ ALOGV("[%s] Found ready buffer", __func__);
+ size_t skipCount = mNewCachedSet->getLayerCount();
+ while (skipCount != 0) {
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = mNewCachedSet->getBuffer(),
+ .acquireFence = mNewCachedSet->getDrawFence(),
+ .displayFrame = mNewCachedSet->getBounds(),
+ };
+ ++incomingLayerIter;
+ }
+
+ if (currentLayerIter->getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ }
+ ++currentLayerIter;
+
+ skipCount -= layerCount;
+ }
+ merged.emplace_back(std::move(*mNewCachedSet));
+ mNewCachedSet = std::nullopt;
+ continue;
+ }
+ }
+
+ if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) {
+ currentLayerIter->incrementAge();
+ merged.emplace_back(*currentLayerIter);
+
+ // Skip the incoming layers corresponding to this valid current layer
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = currentLayerIter->getBuffer(),
+ .acquireFence = currentLayerIter->getDrawFence(),
+ .displayFrame = currentLayerIter->getBounds(),
+ };
+ ++incomingLayerIter;
+ }
+ } else if (currentLayerIter->getLayerCount() > 1) {
+ // Break the current layer into its constituent layers
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ for (CachedSet& layer : currentLayerIter->decompose()) {
+ layer.updateAge(now);
+ merged.emplace_back(layer);
+ ++incomingLayerIter;
+ }
+ } else {
+ currentLayerIter->updateAge(now);
+ merged.emplace_back(*currentLayerIter);
+ ++incomingLayerIter;
+ }
+ ++currentLayerIter;
+ }
+
+ for (const CachedSet& layer : merged) {
+ mFlattenedDisplayCost += layer.getDisplayCost();
+ }
+
+ mLayers = std::move(merged);
+ updateLayersHash();
+ return true;
+}
+
+void Flattener::buildCachedSets(time_point now) {
+ struct Run {
+ Run(std::vector<CachedSet>::const_iterator start, size_t length)
+ : start(start), length(length) {}
+
+ std::vector<CachedSet>::const_iterator start;
+ size_t length;
+ };
+
+ if (mLayers.empty()) {
+ ALOGV("[%s] No layers found, returning", __func__);
+ return;
+ }
+
+ std::vector<Run> runs;
+ bool isPartOfRun = false;
+ for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
+ if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
+ // Layer is inactive
+ if (isPartOfRun) {
+ runs.back().length += currentSet->getLayerCount();
+ } else {
+ // Runs can't start with a non-buffer layer
+ if (currentSet->getFirstLayer().getBuffer() == nullptr) {
+ ALOGV("[%s] Skipping initial non-buffer layer", __func__);
+ } else {
+ runs.emplace_back(currentSet, currentSet->getLayerCount());
+ isPartOfRun = true;
+ }
+ }
+ } else {
+ // Runs must be at least 2 sets long or there's nothing to combine
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ isPartOfRun = false;
+ }
+ }
+
+ // Check for at least 2 sets one more time in case the set includes the last layer
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
+
+ if (runs.empty()) {
+ return;
+ }
+
+ mNewCachedSet.emplace(*runs[0].start);
+ mNewCachedSet->setLastUpdate(now);
+ auto currentSet = runs[0].start;
+ while (mNewCachedSet->getLayerCount() < runs[0].length) {
+ ++currentSet;
+ mNewCachedSet->append(*currentSet);
+ }
+
+ // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
+ mPredictor.getPredictedPlan({}, 0);
+
+ ++mCachedSetCreationCount;
+ mCachedSetCreationCost += mNewCachedSet->getCreationCost();
+ std::string setDump;
+ mNewCachedSet->dump(setDump);
+ ALOGV("[%s] Added new cached set:\n%s", __func__, setDump.c_str());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
new file mode 100644
index 0000000..7cf4819
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+namespace android::compositionengine::impl::planner {
+
+LayerState::LayerState(compositionengine::OutputLayer* layer) : mOutputLayer(layer) {
+ update(layer);
+}
+
+Flags<LayerStateField> LayerState::update(compositionengine::OutputLayer* layer) {
+ ALOGE_IF(layer != mOutputLayer, "[%s] Expected mOutputLayer to never change", __func__);
+
+ Flags<LayerStateField> differences;
+
+ // Update the unique fields as well, since we have to set them at least
+ // once from the OutputLayer
+ differences |= mId.update(layer);
+ differences |= mName.update(layer);
+
+ for (StateInterface* field : getNonUniqueFields()) {
+ differences |= field->update(layer);
+ }
+
+ return differences;
+}
+
+size_t LayerState::getHash(
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ size_t hash = 0;
+ for (const StateInterface* field : getNonUniqueFields()) {
+ android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+ }
+
+ return hash;
+}
+
+Flags<LayerStateField> LayerState::getDifferingFields(
+ const LayerState& other,
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ Flags<LayerStateField> differences;
+ auto myFields = getNonUniqueFields();
+ auto otherFields = other.getNonUniqueFields();
+ for (size_t i = 0; i < myFields.size(); ++i) {
+ if (skipFields.test(myFields[i]->getField())) {
+ continue;
+ }
+
+ differences |= myFields[i]->getFieldIfDifferent(otherFields[i]);
+ }
+
+ return differences;
+}
+
+void LayerState::dump(std::string& result) const {
+ for (const StateInterface* field : getNonUniqueFields()) {
+ if (auto viewOpt = flag_name(field->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ bool first = true;
+ for (const std::string& line : field->toStrings()) {
+ base::StringAppendF(&result, "%s%s\n", first ? "" : " ",
+ line.c_str());
+ first = false;
+ }
+ }
+ result.append("\n");
+}
+
+std::optional<std::string> LayerState::compare(const LayerState& other) const {
+ std::string result;
+
+ const auto& thisFields = getNonUniqueFields();
+ const auto& otherFields = other.getNonUniqueFields();
+ for (size_t f = 0; f < thisFields.size(); ++f) {
+ const auto& thisField = thisFields[f];
+ const auto& otherField = otherFields[f];
+ // Skip comparing buffers
+ if (thisField->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+
+ if (thisField->equals(otherField)) {
+ continue;
+ }
+
+ if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ const auto& thisStrings = thisField->toStrings();
+ const auto& otherStrings = otherField->toStrings();
+ bool first = true;
+ for (size_t line = 0; line < std::max(thisStrings.size(), otherStrings.size()); ++line) {
+ if (!first) {
+ result.append(" ");
+ }
+ first = false;
+
+ if (line < thisStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", thisStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+
+ if (line < otherStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", otherStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+ result.append("\n");
+ }
+ }
+
+ return result.empty() ? std::nullopt : std::make_optional(result);
+}
+
+bool operator==(const LayerState& lhs, const LayerState& rhs) {
+ return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
+ lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
+ lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
+ lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mDataspace == rhs.mDataspace && lhs.mColorTransform == rhs.mColorTransform &&
+ lhs.mCompositionType == rhs.mCompositionType &&
+ lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
+ (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+ lhs.mSolidColor == rhs.mSolidColor);
+}
+
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
+ size_t hash = 0;
+ for (const auto layer : layers) {
+ android::hashCombineSingleHashed(hash, layer->getHash(LayerStateField::Buffer));
+ }
+
+ return hash;
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
new file mode 100644
index 0000000..52efff5
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2021 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_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+namespace android::compositionengine::impl::planner {
+
+void Planner::setDisplaySize(ui::Size size) {
+ mFlattener.setDisplaySize(size);
+}
+
+void Planner::plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ std::unordered_set<LayerId> removedLayers;
+ removedLayers.reserve(mPreviousLayers.size());
+
+ std::transform(mPreviousLayers.begin(), mPreviousLayers.end(),
+ std::inserter(removedLayers, removedLayers.begin()),
+ [](const auto& layer) { return layer.first; });
+
+ std::vector<LayerId> currentLayerIds;
+ for (auto layer : layers) {
+ LayerId id = layer->getLayerFE().getSequence();
+ if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) {
+ // Track changes from previous info
+ LayerState& state = layerEntry->second;
+ Flags<LayerStateField> differences = state.update(layer);
+ if (differences.get() == 0) {
+ state.incrementFramesSinceBufferUpdate();
+ } else {
+ ALOGV("Layer %s changed: %s", state.getName().c_str(),
+ differences.string().c_str());
+
+ if (differences.test(LayerStateField::Buffer)) {
+ state.resetFramesSinceBufferUpdate();
+ } else {
+ state.incrementFramesSinceBufferUpdate();
+ }
+ }
+ } else {
+ LayerState state(layer);
+ ALOGV("Added layer %s", state.getName().c_str());
+ mPreviousLayers.emplace(std::make_pair(id, std::move(state)));
+ }
+
+ currentLayerIds.emplace_back(id);
+
+ if (const auto found = removedLayers.find(id); found != removedLayers.end()) {
+ removedLayers.erase(found);
+ }
+ }
+
+ for (LayerId removedLayer : removedLayers) {
+ if (const auto layerEntry = mPreviousLayers.find(removedLayer);
+ layerEntry != mPreviousLayers.end()) {
+ const auto& [id, state] = *layerEntry;
+ ALOGV("Removed layer %s", state.getName().c_str());
+ mPreviousLayers.erase(removedLayer);
+ }
+ }
+
+ mCurrentLayers.clear();
+ mCurrentLayers.reserve(currentLayerIds.size());
+ std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
+ std::back_inserter(mCurrentLayers), [this](LayerId id) {
+ LayerState* state = &mPreviousLayers.at(id);
+ state->getOutputLayer()->editState().overrideInfo = {};
+ return state;
+ });
+
+ const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
+ mFlattenedHash = mFlattener.flattenLayers(mCurrentLayers, hash);
+ const bool layersWereFlattened = hash != mFlattenedHash;
+ ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
+
+ mPredictedPlan =
+ mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+ : mCurrentLayers,
+ mFlattenedHash);
+ if (mPredictedPlan) {
+ ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+ } else {
+ ALOGV("[%s] No prediction found\n", __func__);
+ }
+}
+
+void Planner::reportFinalPlan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ Plan finalPlan;
+ const GraphicBuffer* currentOverrideBuffer = nullptr;
+ bool hasSkippedLayers = false;
+ for (auto layer : layers) {
+ const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.get();
+ if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
+ // Skip this layer since it is part of a previous cached set
+ hasSkippedLayers = true;
+ continue;
+ }
+
+ currentOverrideBuffer = overrideBuffer;
+
+ const bool forcedOrRequestedClient =
+ layer->getState().forceClientComposition || layer->requiresClientComposition();
+
+ finalPlan.addLayerType(
+ forcedOrRequestedClient
+ ? hardware::graphics::composer::hal::Composition::CLIENT
+ : layer->getLayerFE().getCompositionState()->compositionType);
+ }
+
+ mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
+ finalPlan);
+}
+
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+ mFlattener.renderCachedSets(renderEngine);
+}
+
+void Planner::dump(const Vector<String16>& args, std::string& result) {
+ if (args.size() > 1) {
+ const String8 command(args[1]);
+ if (command == "--compare" || command == "-c") {
+ if (args.size() < 4) {
+ base::StringAppendF(&result,
+ "Expected two layer stack hashes, e.g. '--planner %s "
+ "<left_hash> <right_hash>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 4) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <left_hash> "
+ "<right_hash>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 leftHashString(args[2]);
+ size_t leftHash = 0;
+ int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ leftHashString.string());
+ return;
+ }
+
+ const String8 rightHashString(args[3]);
+ size_t rightHash = 0;
+ fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ rightHashString.string());
+ return;
+ }
+
+ mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ } else if (command == "--describe" || command == "-d") {
+ if (args.size() < 3) {
+ base::StringAppendF(&result,
+ "Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 3) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <hash>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 hashString(args[2]);
+ size_t hash = 0;
+ const int fieldsRead = sscanf(hashString.string(), "%zx", &hash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ hashString.string());
+ return;
+ }
+
+ mPredictor.describeLayerStack(hash, result);
+ } else if (command == "--help" || command == "-h") {
+ dumpUsage(result);
+ } else if (command == "--similar" || command == "-s") {
+ if (args.size() < 3) {
+ base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 3) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <plan>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 planString(args[2]);
+ std::optional<Plan> plan = Plan::fromString(std::string(planString.string()));
+ if (!plan) {
+ base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string());
+ return;
+ }
+
+ mPredictor.listSimilarStacks(*plan, result);
+ } else {
+ base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
+ dumpUsage(result);
+ }
+ return;
+ }
+
+ // If there are no specific commands, dump the usual state
+
+ mFlattener.dump(result);
+ result.append("\n");
+
+ mPredictor.dump(result);
+}
+
+void Planner::dumpUsage(std::string& result) const {
+ result.append("Planner command line interface usage\n");
+ result.append(" dumpsys SurfaceFlinger --planner <command> [arguments]\n\n");
+
+ result.append("If run without a command, dumps current Planner state\n\n");
+
+ result.append("Commands:\n");
+
+ result.append("[--compare|-c] <left_hash> <right_hash>\n");
+ result.append(" Compares the predictions <left_hash> and <right_hash> by showing differences"
+ " in their example layer stacks\n");
+
+ result.append("[--describe|-d] <hash>\n");
+ result.append(" Prints the example layer stack and prediction statistics for <hash>\n");
+
+ result.append("[--help|-h]\n");
+ result.append(" Shows this message\n");
+
+ result.append("[--similar|-s] <plan>\n");
+ result.append(" Prints the example layer names for similar stacks matching <plan>\n");
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
new file mode 100644
index 0000000..ba5e64d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2021 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_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/impl/planner/Predictor.h>
+
+namespace android::compositionengine::impl::planner {
+
+std::optional<LayerStack::ApproximateMatch> LayerStack::getApproximateMatch(
+ const std::vector<const LayerState*>& other) const {
+ // Differing numbers of layers are never an approximate match
+ if (mLayers.size() != other.size()) {
+ return std::nullopt;
+ }
+
+ std::optional<ApproximateMatch> approximateMatch = {};
+ for (size_t i = 0; i < mLayers.size(); ++i) {
+ // Skip identical layers
+ if (mLayers[i].getHash(LayerStateField::Buffer) ==
+ other[i]->getHash(LayerStateField::Buffer)) {
+ continue;
+ }
+
+ // Skip layers where both are client-composited, since that doesn't change the
+ // composition plan
+ if (mLayers[i].getCompositionType() == hal::Composition::CLIENT &&
+ other[i]->getCompositionType() == hal::Composition::CLIENT) {
+ continue;
+ }
+
+ // If layers differ in composition type, their stacks are too different
+ if (mLayers[i].getCompositionType() != other[i]->getCompositionType()) {
+ return std::nullopt;
+ }
+
+ // If layers are not identical, but we already have a prior approximate match,
+ // the LayerStacks differ by too much, so return nothing
+ if (approximateMatch) {
+ return std::nullopt;
+ }
+
+ Flags<LayerStateField> differingFields =
+ mLayers[i].getDifferingFields(*other[i], LayerStateField::Buffer);
+
+ // If we don't find an approximate match on this layer, then the LayerStacks differ
+ // by too much, so return nothing
+ const int differingFieldCount = __builtin_popcount(differingFields.get());
+ if (differingFieldCount <= kMaxDifferingFields) {
+ approximateMatch = ApproximateMatch{
+ .differingIndex = i,
+ .differingFields = differingFields,
+ };
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ // If we make it through the layer-by-layer comparison without an approximate match,
+ // it means that all layers were either identical or had client-composited layers in common,
+ // which don't affect the composition strategy, so return a successful result with
+ // no differences.
+ return ApproximateMatch{
+ .differingIndex = 0,
+ .differingFields = {},
+ };
+}
+
+std::optional<Plan> Plan::fromString(const std::string& string) {
+ Plan plan;
+ for (char c : string) {
+ switch (c) {
+ case 'C':
+ plan.addLayerType(hal::Composition::CLIENT);
+ continue;
+ case 'U':
+ plan.addLayerType(hal::Composition::CURSOR);
+ continue;
+ case 'D':
+ plan.addLayerType(hal::Composition::DEVICE);
+ continue;
+ case 'I':
+ plan.addLayerType(hal::Composition::INVALID);
+ continue;
+ case 'B':
+ plan.addLayerType(hal::Composition::SIDEBAND);
+ continue;
+ case 'S':
+ plan.addLayerType(hal::Composition::SOLID_COLOR);
+ continue;
+ default:
+ return std::nullopt;
+ }
+ }
+ return plan;
+}
+
+std::string to_string(const Plan& plan) {
+ std::string result;
+ for (auto type : plan.mLayerTypes) {
+ switch (type) {
+ case hal::Composition::CLIENT:
+ result.append("C");
+ break;
+ case hal::Composition::CURSOR:
+ result.append("U");
+ break;
+ case hal::Composition::DEVICE:
+ result.append("D");
+ break;
+ case hal::Composition::INVALID:
+ result.append("I");
+ break;
+ case hal::Composition::SIDEBAND:
+ result.append("B");
+ break;
+ case hal::Composition::SOLID_COLOR:
+ result.append("S");
+ break;
+ }
+ }
+ return result;
+}
+
+void Prediction::dump(std::string& result) const {
+ result.append(to_string(mPlan));
+ result.append(" [Exact ");
+ mExactStats.dump(result);
+ result.append("] [Approximate ");
+ mApproximateStats.dump(result);
+ result.append("]");
+}
+
+std::optional<Predictor::PredictedPlan> Predictor::getPredictedPlan(
+ const std::vector<const LayerState*>& layers, NonBufferHash hash) const {
+ // First check for an exact match
+ if (std::optional<Plan> exactMatch = getExactMatch(hash); exactMatch) {
+ ALOGV("[%s] Found an exact match for %zx", __func__, hash);
+ return PredictedPlan{.hash = hash, .plan = *exactMatch, .type = Prediction::Type::Exact};
+ }
+
+ // If only a hash was passed in for a layer stack with a cached set, don't perform
+ // approximate matches and return early
+ if (layers.empty()) {
+ ALOGV("[%s] Only hash was passed, but no exact match was found", __func__);
+ return std::nullopt;
+ }
+
+ // Then check for approximate matches
+ if (std::optional<NonBufferHash> approximateMatch = getApproximateMatch(layers);
+ approximateMatch) {
+ ALOGV("[%s] Found an approximate match for %zx", __func__, *approximateMatch);
+ const Prediction& prediction = getPrediction(*approximateMatch);
+ return PredictedPlan{.hash = *approximateMatch,
+ .plan = prediction.getPlan(),
+ .type = Prediction::Type::Approximate};
+ }
+
+ return std::nullopt;
+}
+
+void Predictor::recordResult(std::optional<PredictedPlan> predictedPlan,
+ NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>& layers, bool hasSkippedLayers,
+ Plan result) {
+ if (predictedPlan) {
+ recordPredictedResult(*predictedPlan, layers, std::move(result));
+ return;
+ }
+
+ ++mMissCount;
+
+ if (!hasSkippedLayers && findSimilarPrediction(layers, result)) {
+ return;
+ }
+
+ ALOGV("[%s] Adding novel candidate %zx", __func__, flattenedHash);
+ mCandidates.emplace_front(flattenedHash, Prediction(layers, result));
+ if (mCandidates.size() > MAX_CANDIDATES) {
+ mCandidates.pop_back();
+ }
+}
+
+void Predictor::dump(std::string& result) const {
+ result.append("Predictor state:\n");
+
+ const size_t hitCount = mExactHitCount + mApproximateHitCount;
+ const size_t totalAttempts = hitCount + mMissCount;
+ base::StringAppendF(&result, "Global non-skipped hit rate: %.2f%% (%zd/%zd)\n",
+ 100.0f * hitCount / totalAttempts, hitCount, totalAttempts);
+ base::StringAppendF(&result, " Exact hits: %zd\n", mExactHitCount);
+ base::StringAppendF(&result, " Approximate hits: %zd\n", mApproximateHitCount);
+ base::StringAppendF(&result, " Misses: %zd\n\n", mMissCount);
+
+ dumpPredictionsByFrequency(result);
+}
+
+void Predictor::compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash,
+ std::string& result) const {
+ const auto& [leftPredictionEntry, rightPredictionEntry] =
+ std::make_tuple(mPredictions.find(leftHash), mPredictions.find(rightHash));
+ if (leftPredictionEntry == mPredictions.end()) {
+ base::StringAppendF(&result, "No prediction found for %zx\n", leftHash);
+ return;
+ }
+ if (rightPredictionEntry == mPredictions.end()) {
+ base::StringAppendF(&result, "No prediction found for %zx\n", rightHash);
+ return;
+ }
+
+ base::StringAppendF(&result,
+ "Comparing %-16zx %-16zx\n",
+ leftHash, rightHash);
+
+ const auto& [leftPrediction, rightPrediction] =
+ std::make_tuple(leftPredictionEntry->second, rightPredictionEntry->second);
+ const auto& [leftStack, rightStack] = std::make_tuple(leftPrediction.getExampleLayerStack(),
+ rightPrediction.getExampleLayerStack());
+ leftStack.compare(rightStack, result);
+}
+
+void Predictor::describeLayerStack(NonBufferHash hash, std::string& result) const {
+ base::StringAppendF(&result, "Describing %zx:\n\n", hash);
+
+ if (const auto predictionsEntry = mPredictions.find(hash);
+ predictionsEntry != mPredictions.cend()) {
+ const auto& [hash, prediction] = *predictionsEntry;
+
+ prediction.getExampleLayerStack().dump(result);
+
+ result.append("Prediction: ");
+ prediction.dump(result);
+ result.append("\n");
+ } else {
+ result.append("No predictions found\n");
+ }
+}
+
+void Predictor::listSimilarStacks(Plan plan, std::string& result) const {
+ base::StringAppendF(&result, "Similar stacks for plan %s:\n", to_string(plan).c_str());
+
+ if (const auto similarStacksEntry = mSimilarStacks.find(plan);
+ similarStacksEntry != mSimilarStacks.end()) {
+ const auto& [_, similarStacks] = *similarStacksEntry;
+ for (NonBufferHash hash : similarStacks) {
+ base::StringAppendF(&result, "\nPrediction hash %zx:\n", hash);
+ const Prediction& prediction = mPredictions.at(hash);
+ prediction.getExampleLayerStack().dumpLayerNames(result);
+ }
+ } else {
+ result.append("No similar stacks found\n");
+ }
+}
+
+const Prediction& Predictor::getPrediction(NonBufferHash hash) const {
+ if (const auto predictionEntry = mPredictions.find(hash);
+ predictionEntry != mPredictions.end()) {
+ const auto& [_, prediction] = *predictionEntry;
+ return prediction;
+ } else {
+ const auto candidateEntry = getCandidateEntryByHash(hash);
+ ALOGE_IF(candidateEntry == mCandidates.cend(),
+ "Hash should have been found in either predictions or candidates");
+ const auto& [_, prediction] = *candidateEntry;
+ return prediction;
+ }
+}
+
+Prediction& Predictor::getPrediction(NonBufferHash hash) {
+ return const_cast<Prediction&>(const_cast<const Predictor*>(this)->getPrediction(hash));
+}
+
+std::optional<Plan> Predictor::getExactMatch(NonBufferHash hash) const {
+ const Prediction* match = nullptr;
+ if (const auto predictionEntry = mPredictions.find(hash);
+ predictionEntry != mPredictions.end()) {
+ const auto& [hash, prediction] = *predictionEntry;
+ match = &prediction;
+ } else if (const auto candidateEntry = getCandidateEntryByHash(hash);
+ candidateEntry != mCandidates.cend()) {
+ match = &(candidateEntry->prediction);
+ }
+
+ if (match == nullptr) {
+ return std::nullopt;
+ }
+
+ if (match->getMissCount(Prediction::Type::Exact) != 0) {
+ ALOGV("[%s] Skipping exact match for %zx because of prior miss", __func__, hash);
+ return std::nullopt;
+ }
+
+ return match->getPlan();
+}
+
+std::optional<NonBufferHash> Predictor::getApproximateMatch(
+ const std::vector<const LayerState*>& layers) const {
+ const auto approximateStackMatches = [&](const ApproximateStack& approximateStack) {
+ const auto& exampleStack = mPredictions.at(approximateStack.hash).getExampleLayerStack();
+ if (const auto approximateMatchOpt = exampleStack.getApproximateMatch(layers);
+ approximateMatchOpt) {
+ return *approximateMatchOpt == approximateStack.match;
+ }
+ return false;
+ };
+
+ const auto candidateMatches = [&](const PromotionCandidate& candidate) {
+ ALOGV("[getApproximateMatch] checking against %zx", candidate.hash);
+ return candidate.prediction.getExampleLayerStack().getApproximateMatch(layers) !=
+ std::nullopt;
+ };
+
+ const Prediction* match = nullptr;
+ NonBufferHash hash;
+ if (const auto approximateStackIter =
+ std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(),
+ approximateStackMatches);
+ approximateStackIter != mApproximateStacks.cend()) {
+ match = &mPredictions.at(approximateStackIter->hash);
+ hash = approximateStackIter->hash;
+ } else if (const auto candidateEntry =
+ std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches);
+ candidateEntry != mCandidates.cend()) {
+ match = &(candidateEntry->prediction);
+ hash = candidateEntry->hash;
+ }
+
+ if (match == nullptr) {
+ return std::nullopt;
+ }
+
+ if (match->getMissCount(Prediction::Type::Approximate) != 0) {
+ ALOGV("[%s] Skipping approximate match for %zx because of prior miss", __func__, hash);
+ return std::nullopt;
+ }
+
+ return hash;
+}
+
+void Predictor::promoteIfCandidate(NonBufferHash predictionHash) {
+ // Return if the candidate has already been promoted
+ if (mPredictions.count(predictionHash) != 0) {
+ return;
+ }
+
+ ALOGV("[%s] Promoting %zx from candidate to prediction", __func__, predictionHash);
+
+ auto candidateEntry = getCandidateEntryByHash(predictionHash);
+ ALOGE_IF(candidateEntry == mCandidates.end(), "Expected to find candidate");
+
+ mSimilarStacks[candidateEntry->prediction.getPlan()].push_back(predictionHash);
+ mPredictions.emplace(predictionHash, std::move(candidateEntry->prediction));
+ mCandidates.erase(candidateEntry);
+}
+
+void Predictor::recordPredictedResult(PredictedPlan predictedPlan,
+ const std::vector<const LayerState*>& layers, Plan result) {
+ Prediction& prediction = getPrediction(predictedPlan.hash);
+ if (prediction.getPlan() != result) {
+ ALOGV("[%s] %s prediction missed, expected %s, found %s", __func__,
+ to_string(predictedPlan.type).c_str(), to_string(prediction.getPlan()).c_str(),
+ to_string(result).c_str());
+ prediction.recordMiss(predictedPlan.type);
+ ++mMissCount;
+ return;
+ }
+
+ switch (predictedPlan.type) {
+ case Prediction::Type::Approximate:
+ ++mApproximateHitCount;
+ break;
+ case Prediction::Type::Exact:
+ ++mExactHitCount;
+ break;
+ default:
+ break;
+ }
+
+ ALOGV("[%s] %s prediction hit", __func__, to_string(predictedPlan.type).c_str());
+ ALOGV("[%s] Plan: %s", __func__, to_string(result).c_str());
+ prediction.recordHit(predictedPlan.type);
+
+ const auto stackMatchesHash = [hash = predictedPlan.hash](const ApproximateStack& stack) {
+ return stack.hash == hash;
+ };
+
+ if (predictedPlan.type == Prediction::Type::Approximate) {
+ // If this approximate match is not already in the list of approximate stacks, add it
+ if (std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(),
+ stackMatchesHash) == mApproximateStacks.cend()) {
+ ALOGV("[%s] Adding approximate match to list", __func__);
+ const auto approximateMatchOpt =
+ prediction.getExampleLayerStack().getApproximateMatch(layers);
+ ALOGE_IF(!approximateMatchOpt, "Expected an approximate match");
+ mApproximateStacks.emplace_back(predictedPlan.hash, *approximateMatchOpt);
+ }
+ }
+
+ promoteIfCandidate(predictedPlan.hash);
+}
+
+bool Predictor::findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result) {
+ const auto stacksEntry = mSimilarStacks.find(result);
+ if (stacksEntry == mSimilarStacks.end()) {
+ return false;
+ }
+
+ std::optional<ApproximateStack> bestMatch;
+ const auto& [plan, similarStacks] = *stacksEntry;
+ for (NonBufferHash hash : similarStacks) {
+ const Prediction& prediction = mPredictions.at(hash);
+ auto approximateMatch = prediction.getExampleLayerStack().getApproximateMatch(layers);
+ if (!approximateMatch) {
+ continue;
+ }
+
+ const int differingFieldCount = __builtin_popcount(approximateMatch->differingFields.get());
+ if (!bestMatch ||
+ differingFieldCount < __builtin_popcount(bestMatch->match.differingFields.get())) {
+ bestMatch = {hash, *approximateMatch};
+ }
+ }
+
+ if (!bestMatch) {
+ return false;
+ }
+
+ ALOGV("[%s] Adding %zx to approximate stacks", __func__, bestMatch->hash);
+
+ mApproximateStacks.emplace_back(*bestMatch);
+ return true;
+}
+
+void Predictor::dumpPredictionsByFrequency(std::string& result) const {
+ struct HashFrequency {
+ HashFrequency(NonBufferHash hash, size_t totalAttempts)
+ : hash(hash), totalAttempts(totalAttempts) {}
+
+ NonBufferHash hash;
+ size_t totalAttempts;
+ };
+
+ std::vector<HashFrequency> hashFrequencies;
+ for (const auto& [hash, prediction] : mPredictions) {
+ hashFrequencies.emplace_back(hash,
+ prediction.getHitCount(Prediction::Type::Total) +
+ prediction.getMissCount(Prediction::Type::Total));
+ }
+
+ std::sort(hashFrequencies.begin(), hashFrequencies.end(),
+ [](const HashFrequency& lhs, const HashFrequency& rhs) {
+ return lhs.totalAttempts > rhs.totalAttempts;
+ });
+
+ result.append("Predictions:\n");
+ for (const auto& [hash, totalAttempts] : hashFrequencies) {
+ base::StringAppendF(&result, " %016zx ", hash);
+ mPredictions.at(hash).dump(result);
+ result.append("\n");
+ }
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dcfc162..9dd199d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -840,19 +840,19 @@
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -861,7 +861,7 @@
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -891,7 +891,7 @@
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
expectSetColorCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -901,7 +901,7 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -911,7 +911,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -921,7 +921,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -934,7 +934,7 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -944,7 +944,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -956,7 +956,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -969,7 +969,7 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -980,7 +980,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c4ae3a7..3059beb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -96,6 +96,8 @@
EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("InjectedLayer"));
}
mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>};
@@ -111,6 +113,8 @@
EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("NonInjectedLayer"));
}
mock::OutputLayer outputLayer;
@@ -751,7 +755,9 @@
mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
@@ -766,7 +772,9 @@
injectOutputLayer(layer3);
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
@@ -775,11 +783,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -791,7 +802,9 @@
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
args.internalDisplayRotationFlags = ui::Transform::ROT_180;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
@@ -800,11 +813,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -815,7 +831,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = true;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
@@ -824,11 +842,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -839,7 +860,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = true;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
/*
@@ -877,6 +900,9 @@
mOutput.editState().usesDeviceComposition = true;
EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
+ if (mOutput.plannerEnabled()) {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ }
EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
mOutput.prepareFrame();
@@ -1621,14 +1647,17 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(updateAndWriteCompositionState,
+ MOCK_METHOD1(updateCompositionState,
void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
};
StrictMock<OutputPartialMock> mOutput;
@@ -1639,13 +1668,16 @@
InSequence seq;
EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
- EXPECT_CALL(mOutput, updateAndWriteCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, updateCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, planComposition());
+ EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(Ref(args)));
EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, renderCachedSets());
mOutput.present(args);
}
@@ -3461,7 +3493,8 @@
mOutput.editState().isEnabled = true;
EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(mLayer.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
@@ -3476,7 +3509,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
mRefreshArgs.blursAreExpensive = true;
- mOutput.updateAndWriteCompositionState(mRefreshArgs);
+ mOutput.updateCompositionState(mRefreshArgs);
+ mOutput.planComposition();
+ mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
@@ -3484,7 +3519,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
mRefreshArgs.blursAreExpensive = false;
- mOutput.updateAndWriteCompositionState(mRefreshArgs);
+ mOutput.updateCompositionState(mRefreshArgs);
+ mOutput.planComposition();
+ mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
@@ -4038,11 +4075,14 @@
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4055,7 +4095,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
@@ -4065,11 +4107,14 @@
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);
@@ -4083,7 +4128,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
new file mode 100644
index 0000000..c7dbf88
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FpsReporter.h"
+
+#include "Layer.h"
+
+namespace android {
+
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline)
+ : mFrameTimeline(frameTimeline) {}
+
+void FpsReporter::dispatchLayerFps() const {
+ std::vector<TrackedListener> localListeners;
+ {
+ std::scoped_lock lock(mMutex);
+ if (mListeners.empty()) {
+ return;
+ }
+
+ std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners),
+ [](const std::pair<wp<IBinder>, TrackedListener>& entry) {
+ return entry.second;
+ });
+ }
+
+ for (const auto& listener : localListeners) {
+ sp<Layer> promotedLayer = listener.layer.promote();
+ if (promotedLayer != nullptr) {
+ std::unordered_set<int32_t> layerIds;
+
+ promotedLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+
+ listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+ }
+ }
+}
+
+void FpsReporter::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mMutex);
+ mListeners.erase(who);
+}
+
+void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, layer});
+}
+
+void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
new file mode 100644
index 0000000..d64b3dc
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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 <android-base/thread_annotations.h>
+#include <android/gui/IFpsListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+#include "FrameTimeline/FrameTimeline.h"
+
+namespace android {
+
+class Layer;
+
+class FpsReporter : public IBinder::DeathRecipient {
+public:
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline);
+
+ // Dispatches updated layer fps values for the registered listeners
+ // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+ // must be held when calling this method.
+ void dispatchLayerFps() const EXCLUDES(mMutex);
+
+ // Override for IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>&) override;
+
+ // Registers an Fps listener that listens to fps updates for the provided layer
+ void addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer);
+ // Deregisters an Fps listener
+ void removeListener(const sp<gui::IFpsListener>& listener);
+
+private:
+ mutable std::mutex mMutex;
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+
+ struct TrackedListener {
+ sp<gui::IFpsListener> listener;
+ wp<Layer> layer;
+ };
+
+ frametimeline::FrameTimeline& mFrameTimeline;
+ std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 1e6d21e..10a5833 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_library_static {
name: "libframetimeline",
defaults: ["surfaceflinger_defaults"],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index da04202..ff000c9 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -19,12 +19,15 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "FrameTimeline.h"
+
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/Trace.h>
+
#include <chrono>
#include <cinttypes>
#include <numeric>
+#include <unordered_set>
namespace android::frametimeline {
@@ -277,8 +280,8 @@
}
SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid,
- uid_t ownerUid, std::string layerName, std::string debugName,
- PredictionState predictionState,
+ uid_t ownerUid, int32_t layerId, std::string layerName,
+ std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
@@ -289,6 +292,7 @@
mOwnerUid(ownerUid),
mLayerName(std::move(layerName)),
mDebugName(std::move(debugName)),
+ mLayerId(layerId),
mPresentState(PresentState::Unknown),
mPredictionState(predictionState),
mPredictions(predictions),
@@ -397,6 +401,8 @@
StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
mRenderRate ? mRenderRate->getIntValue() : 0);
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer ID : %d\n", mLayerId);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
if (mPresentState == PresentState::Dropped) {
@@ -458,7 +464,9 @@
const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
- const nsecs_t deltaToVsync = std::abs(presentDelta) % refreshRate.getPeriodNsecs();
+ const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
+ : 0;
if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
@@ -698,11 +706,11 @@
}
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+ const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
std::string layerName, std::string debugName) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter);
@@ -710,13 +718,13 @@
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
if (predictions) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
&mTraceCookieCounter);
}
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter);
@@ -804,7 +812,9 @@
// How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
// was a prediction error or not.
- nsecs_t deltaToVsync = std::abs(presentDelta) % mRefreshRate.getPeriodNsecs();
+ nsecs_t deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
+ : 0;
if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
@@ -968,6 +978,65 @@
}
}
+float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
+ if (layerIds.empty()) {
+ return 0.0f;
+ }
+
+ std::vector<nsecs_t> presentTimes;
+ {
+ std::scoped_lock lock(mMutex);
+ presentTimes.reserve(mDisplayFrames.size());
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ const auto& displayFrame = mDisplayFrames[i];
+ if (displayFrame->getActuals().presentTime <= 0) {
+ continue;
+ }
+ for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
+ layerIds.count(surfaceFrame->getLayerId()) > 0) {
+ // We're looking for DisplayFrames that presents at least one layer from
+ // layerIds, so push the present time and skip looking through the rest of the
+ // SurfaceFrames.
+ presentTimes.push_back(displayFrame->getActuals().presentTime);
+ break;
+ }
+ }
+ }
+ }
+
+ // FPS can't be computed when there's fewer than 2 presented frames.
+ if (presentTimes.size() <= 1) {
+ return 0.0f;
+ }
+
+ nsecs_t priorPresentTime = -1;
+ nsecs_t totalPresentToPresentWalls = 0;
+
+ for (const nsecs_t presentTime : presentTimes) {
+ if (priorPresentTime == -1) {
+ priorPresentTime = presentTime;
+ continue;
+ }
+
+ totalPresentToPresentWalls += (presentTime - priorPresentTime);
+ priorPresentTime = presentTime;
+ }
+
+ if (CC_UNLIKELY(totalPresentToPresentWalls <= 0)) {
+ ALOGW("Invalid total present-to-present duration when computing fps: %" PRId64,
+ totalPresentToPresentWalls);
+ return 0.0f;
+ }
+
+ const constexpr nsecs_t kOneSecond =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ // (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
+ // M frames / second
+ return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
+ static_cast<float>(totalPresentToPresentWalls);
+}
+
void FrameTimeline::flushPendingPresentFences() {
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 8f3157d..d65769b 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -156,9 +156,10 @@
// Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
// TokenManager), Thresholds and TimeStats pointer.
SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName, PredictionState predictionState,
- TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds, TraceCookieCounter* traceCookieCounter);
+ int32_t layerId, std::string layerName, std::string debugName,
+ PredictionState predictionState, TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -199,6 +200,7 @@
// Getter functions used only by FrameTimelineTests and SurfaceFrame internally
TimelineItem getActuals() const;
pid_t getOwnerPid() const { return mOwnerPid; };
+ int32_t getLayerId() const { return mLayerId; };
PredictionState getPredictionState() const;
PresentState getPresentState() const;
FrameReadyMetadata getFrameReadyMetadata() const;
@@ -221,6 +223,7 @@
const uid_t mOwnerUid;
const std::string mLayerName;
const std::string mDebugName;
+ const int32_t mLayerId;
PresentState mPresentState GUARDED_BY(mMutex);
const PredictionState mPredictionState;
const TimelineItem mPredictions;
@@ -267,7 +270,7 @@
// Debug name is the human-readable debugging string for dumpsys.
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName) = 0;
+ int32_t layerId, std::string layerName, std::string debugName) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -292,6 +295,11 @@
// Sets the max number of display frames that can be stored. Called by SF backdoor.
virtual void setMaxDisplayFrames(uint32_t size);
+ // Computes the historical fps for the provided set of layer IDs
+ // The fps is compted from the linear timeline of present timestamps for DisplayFrames
+ // containing at least one layer ID.
+ virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
+
// Restores the max number of display frames to default. Called by SF backdoor.
virtual void reset() = 0;
};
@@ -417,13 +425,14 @@
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName) override;
+ int32_t layerId, std::string layerName, std::string debugName) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
+ float computeFps(const std::unordered_set<int32_t>& layerIds) override;
void reset() override;
// Sets up the perfetto tracing backend and data source.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 141a112..937868a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1595,7 +1595,8 @@
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
const FrameTimelineInfo& info, nsecs_t postTime) {
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName,
mTransactionName);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1611,8 +1612,8 @@
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
- debugName);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName, debugName);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 14bb5b7..23758c9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -501,6 +501,7 @@
// one empty rect.
virtual void useSurfaceDamage() {}
virtual void useEmptyDamage() {}
+ Region getVisibleRegion(const DisplayDevice*) const;
virtual void incrementPendingBufferCount() {}
@@ -723,7 +724,7 @@
// Returns the bounds of the layer without any buffer scaling.
FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
- int32_t getSequence() const { return sequence; }
+ int32_t getSequence() const override { return sequence; }
// For tracing.
// TODO: Replace with raw buffer id from buffer metadata when that becomes available.
@@ -1009,6 +1010,7 @@
// For unit tests
friend class TestableSurfaceFlinger;
+ friend class FpsReporterTest;
friend class RefreshRateSelectionTest;
friend class SetFrameRateTest;
friend class TransactionFrameTracerTest;
@@ -1165,7 +1167,6 @@
virtual bool canDrawShadows() const { return true; }
Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
- Region getVisibleRegion(const DisplayDevice*) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 487988b..5364adf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -108,6 +108,7 @@
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
+#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
@@ -1427,6 +1428,25 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+
+ const wp<Layer> layer = fromHandle(layerHandle);
+ mFpsReporter->addListener(listener, layer);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+ mFpsReporter->removeListener(listener);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
if (!displayToken || !outSupport) {
@@ -2113,6 +2133,13 @@
}
});
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (mFpsReporter) {
+ mFpsReporter->dispatchLayerFps();
+ }
+ }
+
mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
mTransactionCallbackInvoker.sendCallbacks();
@@ -2929,6 +2956,7 @@
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
+ mFpsReporter = new FpsReporter(*mFrameTimeline);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
// prior configuration.
@@ -4344,6 +4372,7 @@
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
{"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
{"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
@@ -4480,6 +4509,13 @@
mScheduler->dumpVsync(result);
}
+void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
+ for (const auto& [token, display] : mDisplays) {
+ const auto compositionDisplay = display->getCompositionDisplay();
+ compositionDisplay->dumpPlannerInfo(args, result);
+ }
+}
+
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
result.append("Static screen stats:\n");
for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
@@ -4909,6 +4945,8 @@
case GET_DISPLAYED_CONTENT_SAMPLE:
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
+ case ADD_FPS_LISTENER:
+ case REMOVE_FPS_LISTENER:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
// necessary permission dynamically. Don't use the permission cache for this check.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f73a13d..1615ab6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -89,6 +89,7 @@
class Client;
class EventThread;
+class FpsReporter;
class HWComposer;
struct SetInputWindowsListener;
class IGraphicBufferProducer;
@@ -589,6 +590,9 @@
status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
+ status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) override;
+ status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
ui::DisplayModeId displayModeId, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1013,6 +1017,7 @@
LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
EXCLUDES(mStateLock);
void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
+ void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
bool isLayerTripleBufferingDisabled() const {
return this->mLayerTripleBufferingDisabled;
@@ -1269,6 +1274,7 @@
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
+ sp<FpsReporter> mFpsReporter;
ui::DisplayPrimaries mInternalDisplayPrimaries;
const float mInternalDisplayDensity;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index c043866..b3dca78 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -380,5 +380,9 @@
return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
}
+bool enable_layer_caching(bool defaultValue) {
+ return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 816cb09..b19d216 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -100,6 +100,8 @@
bool enable_frame_rate_override(bool defaultValue);
+bool enable_layer_caching(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 4d25a7a..ee5542d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -454,3 +454,12 @@
access: Readonly
prop_name: "ro.surface_flinger.enable_frame_rate_override"
}
+
+# Enables Layer Caching
+prop {
+ api_name: "enable_layer_caching"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.enable_layer_caching"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0e0be09..47e14f6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -45,6 +45,10 @@
prop_name: "ro.surface_flinger.enable_frame_rate_override"
}
prop {
+ api_name: "enable_layer_caching"
+ prop_name: "ro.surface_flinger.enable_layer_caching"
+ }
+ prop {
api_name: "enable_protected_contents"
prop_name: "ro.surface_flinger.protected_contents"
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 2ac6b09..3c1b9d8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -53,6 +53,7 @@
"DisplayDevice_GetBestColorModeTest.cpp",
"DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
+ "FpsReporterTest.cpp",
"FpsTest.cpp",
"FrameTimelineTest.cpp",
"HWComposerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
new file mode 100644
index 0000000..a9e5df3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporterTest"
+
+#include <android/gui/BnFpsListener.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "FpsReporter.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockFrameTimeline.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::UnorderedElementsAre;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+struct TestableFpsListener : public gui::BnFpsListener {
+ TestableFpsListener() {}
+
+ float lastReportedFps = 0;
+
+ binder::Status onFpsReported(float fps) override {
+ lastReportedFps = fps;
+ return binder::Status::ok();
+ }
+};
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class FpsReporterTest : public testing::Test {
+public:
+ FpsReporterTest();
+ ~FpsReporterTest() override;
+
+protected:
+ static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+ static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+ static constexpr int32_t PRIORITY_UNSET = -1;
+
+ void setupScheduler();
+ void setupComposer(uint32_t virtualDisplayCount);
+ sp<BufferStateLayer> createBufferStateLayer();
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::FrameTimeline mFrameTimeline =
+ mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
+
+ sp<Client> mClient;
+ sp<Layer> mParent;
+ sp<Layer> mTarget;
+ sp<Layer> mChild;
+ sp<Layer> mGrandChild;
+ sp<Layer> mUnrelated;
+
+ sp<TestableFpsListener> mFpsListener;
+ sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline);
+};
+
+FpsReporterTest::FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ setupScheduler();
+ setupComposer(0);
+ mFpsListener = new TestableFpsListener();
+}
+
+FpsReporterTest::~FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferStateLayer(args);
+}
+
+void FpsReporterTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
+}
+
+void FpsReporterTest::setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+
+TEST_F(FpsReporterTest, callsListeners) {
+ mParent = createBufferStateLayer();
+ mTarget = createBufferStateLayer();
+ mChild = createBufferStateLayer();
+ mGrandChild = createBufferStateLayer();
+ mUnrelated = createBufferStateLayer();
+ mParent->addChild(mTarget);
+ mTarget->addChild(mChild);
+ mChild->addChild(mGrandChild);
+ mParent->commitChildList();
+
+ float expectedFps = 44.0;
+
+ EXPECT_CALL(mFrameTimeline,
+ computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(),
+ mGrandChild->getSequence())))
+ .WillOnce(Return(expectedFps));
+
+ mFpsReporter->addListener(mFpsListener, mTarget);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
+ mFpsReporter->removeListener(mFpsListener);
+ Mock::VerifyAndClearExpectations(&mFrameTimeline);
+
+ EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0);
+ mFpsReporter->dispatchLayerFps();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index b8c1607..9a4e020 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -170,6 +170,8 @@
static constexpr pid_t sPidOne = 10;
static constexpr pid_t sPidTwo = 20;
static constexpr int32_t sInputEventId = 5;
+static constexpr int32_t sLayerIdOne = 1;
+static constexpr int32_t sLayerIdTwo = 2;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -187,17 +189,20 @@
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -206,7 +211,7 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -215,7 +220,7 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -226,7 +231,7 @@
constexpr int32_t inputEventId = 1;
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -236,7 +241,7 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -263,10 +268,12 @@
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameTwo, sLayerNameTwo);
+ sUidOne, sLayerIdTwo, sLayerNameTwo,
+ sLayerNameTwo);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -307,8 +314,8 @@
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
- sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -329,7 +336,8 @@
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -343,18 +351,18 @@
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -367,8 +375,8 @@
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -383,8 +391,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -399,8 +407,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -434,7 +442,8 @@
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -460,7 +469,8 @@
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
@@ -488,7 +498,8 @@
std::chrono::nanoseconds(90ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -519,7 +530,8 @@
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(50ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -550,7 +562,8 @@
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -580,7 +593,8 @@
std::chrono::nanoseconds(90ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(82ms).count(), refreshRate);
@@ -614,7 +628,8 @@
std::chrono::nanoseconds(90ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -641,7 +656,7 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -667,7 +682,7 @@
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -710,8 +725,9 @@
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1015,10 +1031,12 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1174,7 +1192,7 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
- sPidOne, sUidOne, sLayerNameOne,
+ sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
sLayerNameOne);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
@@ -1246,7 +1264,8 @@
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1401,7 +1420,8 @@
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1420,7 +1440,8 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1479,7 +1500,8 @@
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1498,7 +1520,8 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1556,7 +1579,8 @@
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1599,7 +1623,8 @@
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1618,7 +1643,8 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1671,7 +1697,8 @@
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1690,7 +1717,8 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1735,4 +1763,151 @@
EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
JankType::AppDeadlineMissed | JankType::BufferStuffing);
}
+
+TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
+ EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_singleDisplayFrame_returnsZero) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 10.0);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne, sLayerIdTwo}), 10.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ const auto threeHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(300ms).count();
+ const auto fiveHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
+ const auto sixHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(600ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ auto surfaceFrame3 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3);
+ presentFence3->signalForTest(threeHundredMs);
+ mFrameTimeline->setSfPresent(threeHundredMs, presentFence3);
+
+ auto surfaceFrame4 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame4);
+ presentFence4->signalForTest(fiveHundredMs);
+ mFrameTimeline->setSfPresent(fiveHundredMs, presentFence4);
+
+ auto surfaceFrame5 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ // Dropped frames will be excluded from fps computation
+ surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame5);
+ presentFence5->signalForTest(sixHundredMs);
+ mFrameTimeline->setSfPresent(sixHundredMs, presentFence5);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
+}
+
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 44b9b73..b9d1794 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -33,6 +33,7 @@
MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
};
} // namespace android::mock
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4f89353..2002bdf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_library_shared {
name: "libvibratorservice",
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 7b4cc19..a468146 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_benchmark {
name: "libvibratorservice_benchmarks",
srcs: [
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index ad85990..3294724 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
cc_test {
name: "libvibratorservice_test",
test_suites: ["device-tests"],
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 8523bb2..f5491cf 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
index 98afdec..fa71ed7 100644
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ b/services/vr/hardware_composer/aidl/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}