Merge "Ensure reportFrameMetrics not being called on deleted instance" into sc-dev
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index a1d8ce5..d6ca3db 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -26,4 +26,5 @@
METADATA_ACCESSIBILITY_ID = 5,
METADATA_OWNER_PID = 6,
METADATA_DEQUEUE_TIME = 7,
+ METADATA_GAME_MODE = 8,
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 818804a..0595322 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -342,7 +342,8 @@
// If the initial top-level restorecon above changed the label, then go
// back and restorecon everything recursively
- if (strcmp(before, after)) {
+ // TODO(b/190567190, b/188141923) Remove recursive fixup of com.google.android.gsf.
+ if (strcmp(before, after) || (path.find("com.google.android.gsf") != std::string::npos)) {
if (existing) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
<< path << "; running recursive restorecon";
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 204953c..cc0434d 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -292,8 +292,8 @@
}
}
-static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600)));
+static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags, mode_t mode) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, mode)));
if (fd.get() < 0) {
if (errno != EEXIST) {
PLOG(ERROR) << "Failed to create profile " << profile;
@@ -310,7 +310,7 @@
return fd;
}
-static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) {
+static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags, mode_t mode) {
// Do not follow symlinks when opening a profile:
// - primary profiles should not contain symlinks in their paths
// - secondary dex paths should have been already resolved and validated
@@ -320,7 +320,7 @@
// Reference profiles and snapshots are created on the fly; so they might not exist beforehand.
unique_fd fd;
if ((flags & O_CREAT) != 0) {
- fd = create_profile(uid, profile, flags);
+ fd = create_profile(uid, profile, flags, mode);
} else {
fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
}
@@ -336,6 +336,16 @@
PLOG(ERROR) << "Failed to open profile " << profile;
}
return invalid_unique_fd();
+ } else {
+ // If we just create the file we need to set its mode because on Android
+ // open has a mask that only allows owner access.
+ if ((flags & O_CREAT) != 0) {
+ if (fchmod(fd.get(), mode) != 0) {
+ PLOG(ERROR) << "Could not set mode " << std::hex << mode << std::dec
+ << " on profile" << profile;
+ // Not a terminal failure.
+ }
+ }
}
return fd;
@@ -345,20 +355,29 @@
const std::string& location, bool is_secondary_dex) {
std::string profile = create_current_profile_path(user, package_name, location,
is_secondary_dex);
- return open_profile(uid, profile, O_RDONLY);
+ return open_profile(uid, profile, O_RDONLY, /*mode=*/ 0);
}
static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
- return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+ return open_profile(
+ uid,
+ profile,
+ read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
}
static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile_path = create_reference_profile_path(package_name, location,
is_secondary_dex);
- unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+ unique_fd ufd = open_profile(
+ uid,
+ profile_path,
+ read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
+
return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
clear_profile(path);
});
@@ -367,7 +386,7 @@
static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
const std::string& location) {
std::string profile = create_snapshot_profile_path(package_name, location);
- return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);
+ return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
}
static void open_profile_files(uid_t uid, const std::string& package_name,
@@ -2484,7 +2503,7 @@
for (size_t i = 0; i < profiles.size(); ) {
std::vector<unique_fd> profiles_fd;
for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
- unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
+ unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY, /*mode=*/ 0);
if (fd.get() >= 0) {
profiles_fd.push_back(std::move(fd));
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index e272025..216347e 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -919,7 +919,7 @@
return;
}
- // Check that the snapshot was created witht he expected acess flags.
+ // Check that the snapshot was created with the expected access flags.
CheckFileAccess(snap_profile_, kSystemUid, kSystemGid, 0600 | S_IFREG);
// The snapshot should be equivalent to the merge of profiles.
@@ -962,8 +962,8 @@
return;
}
- // Check that the snapshot was created witht he expected acess flags.
- CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+ // Check that the snapshot was created with the expected access flags.
+ CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0640 | S_IFREG);
// The snapshot should be equivalent to the merge of profiles.
std::string ref_profile_content = ref_profile_ + ".expected";
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index adfd6e2..cc0ee82 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -47,12 +47,6 @@
<feature name="android.software.secure_lock_screen" />
<feature name="android.software.input_methods" />
-
- <!-- Feature to support device admins -->
- <!-- TODO(b/178412797): not fully supported yet, CTS tests are still
- failing. -->
- <feature name="android.software.device_admin" />
-
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
android.hardware.camera.autofocus.xml or
diff --git a/include/input/Input.h b/include/input/Input.h
index d4defa8..e8678d2 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -24,6 +24,9 @@
*/
#include <android/input.h>
+#ifdef __linux__
+#include <android/os/IInputConstants.h>
+#endif
#include <math.h>
#include <stdint.h>
#include <ui/Transform.h>
@@ -219,7 +222,16 @@
POLICY_FLAG_GESTURE = 0x00000008,
POLICY_FLAG_RAW_MASK = 0x0000ffff,
+#ifdef __linux__
+ POLICY_FLAG_INPUTFILTER_TRUSTED = android::os::IInputConstants::POLICY_FLAG_INPUTFILTER_TRUSTED,
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
+ android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+#else
+ POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000,
+
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000,
+#endif
/* These flags are set by the input dispatcher. */
// Indicates that the input event was injected.
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 1fec080..1955104 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -257,13 +257,9 @@
return mMotionRanges;
}
- const InputDeviceSensorInfo* getSensorInfo(InputDeviceSensorType type);
+ std::vector<InputDeviceSensorInfo> getSensors();
- const std::vector<InputDeviceSensorType> getSensorTypes();
-
- const std::vector<int32_t> getLightIds();
-
- const InputDeviceLightInfo* getLightInfo(int32_t id);
+ std::vector<InputDeviceLightInfo> getLights();
private:
int32_t mId;
@@ -322,6 +318,8 @@
const std::string& name, InputDeviceConfigurationFileType type);
enum ReservedInputDeviceId : int32_t {
+ // Device id assigned to input events generated inside accessibility service
+ ACCESSIBILITY_DEVICE_ID = -2,
// Device id of a special "virtual" keyboard that is always present.
VIRTUAL_KEYBOARD_ID = -1,
// Device id of the "built-in" keyboard if there is one.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index be260e8..91cd90d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -68,6 +68,8 @@
cc_library {
name: "libbinder",
+ version_script: "libbinder.map",
+
// for vndbinder
vendor_available: true,
vndk: {
diff --git a/libs/binder/libbinder.map b/libs/binder/libbinder.map
new file mode 100644
index 0000000..9ca14bc
--- /dev/null
+++ b/libs/binder/libbinder.map
@@ -0,0 +1,5 @@
+# b/190148312: Populate with correct list of ABI symbols
+LIBBINDER {
+ global:
+ *;
+};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 3b3fd08..3920129 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -24,7 +24,6 @@
use std::ffi::{c_void, CString};
use std::mem::ManuallyDrop;
use std::ops::Deref;
-use std::ptr;
/// Rust wrapper around Binder remotable objects.
///
@@ -273,7 +272,7 @@
/// Must be called with a valid pointer to a `T` object. After this call,
/// the pointer will be invalid and should not be dereferenced.
unsafe extern "C" fn on_destroy(object: *mut c_void) {
- ptr::drop_in_place(object as *mut T)
+ Box::from_raw(object as *mut T);
}
/// Called whenever a new, local `AIBinder` object is needed of a specific
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index ec231b2..fb84f04 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -156,6 +156,10 @@
],
test_suites: ["general-tests"],
require_root: true,
+ // Prevent the unit test target from running on sc-dev as it's not ready.
+ test_options: {
+ unit_test: false,
+ },
}
cc_benchmark {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index a2868c6..b9a293f 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -302,23 +302,25 @@
// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
// Otherwise, this is a no-op.
static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
- const sp<Fence>& releaseFence) {
+ const sp<Fence>& releaseFence, uint32_t transformHint) {
sp<BLASTBufferQueue> blastBufferQueue = context.promote();
ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
graphicBufferId, blastBufferQueue ? "alive" : "dead");
if (blastBufferQueue) {
- blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence);
+ blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence, transformHint);
}
}
void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
- const sp<Fence>& releaseFence) {
+ const sp<Fence>& releaseFence,
+ uint32_t transformHint) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
if (mSurfaceControl != nullptr) {
- mTransformHint = mSurfaceControl->getTransformHint();
+ mTransformHint = transformHint;
+ mSurfaceControl->setTransformHint(transformHint);
mBufferItemConsumer->setTransformHint(mTransformHint);
}
@@ -412,7 +414,7 @@
auto releaseBufferCallback =
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
- std::placeholders::_1, std::placeholders::_2);
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index f74f91e..63d07ba 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -251,10 +251,11 @@
stats);
}
- void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) override {
+ void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence,
+ uint32_t transformHint) override {
callRemoteAsync<decltype(
&ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
- graphicBufferId, releaseFence);
+ graphicBufferId, releaseFence, transformHint);
}
};
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 634d8b7..189d51a 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -136,6 +136,8 @@
return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
case view::LayerMetadataKey::METADATA_DEQUEUE_TIME:
return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key));
+ case view::LayerMetadataKey::METADATA_GAME_MODE:
+ return StringPrintf("gameMode%s%d", separator, getInt32(key, 0));
default:
return StringPrintf("%d%s%dbytes", key, separator,
static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1057a51..660c5bd 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -327,7 +327,8 @@
callback(surfaceStats.previousBufferId,
surfaceStats.previousReleaseFence
? surfaceStats.previousReleaseFence
- : Fence::NO_FENCE);
+ : Fence::NO_FENCE,
+ surfaceStats.transformHint);
}
}
}
@@ -364,7 +365,8 @@
}
void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId,
- sp<Fence> releaseFence) {
+ sp<Fence> releaseFence,
+ uint32_t transformHint) {
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -374,7 +376,7 @@
ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
return;
}
- callback(graphicBufferId, releaseFence);
+ callback(graphicBufferId, releaseFence, transformHint);
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index c4ca399..3ab1ee1 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -89,7 +89,8 @@
void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
- void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence);
+ void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence,
+ uint32_t transformHint);
void setNextTransaction(SurfaceComposerClient::Transaction *t);
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
void setTransactionCompleteCallback(uint64_t frameNumber,
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 2d71194..3bfeef1 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -158,7 +158,8 @@
virtual void onTransactionCompleted(ListenerStats stats) = 0;
- virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) = 0;
+ virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence,
+ uint32_t transformHint) = 0;
};
class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 41982c2..de14b3d 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -29,7 +29,8 @@
METADATA_MOUSE_CURSOR = 4,
METADATA_ACCESSIBILITY_ID = 5,
METADATA_OWNER_PID = 6,
- METADATA_DEQUEUE_TIME = 7
+ METADATA_DEQUEUE_TIME = 7,
+ METADATA_GAME_MODE = 8
};
struct LayerMetadata : public Parcelable {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8a4c5a5..5aa132c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -82,7 +82,8 @@
std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& /*stats*/)>;
using ReleaseBufferCallback =
- std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/)>;
+ std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/,
+ uint32_t transformHint)>;
using SurfaceStatsCallback =
std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
@@ -727,7 +728,8 @@
// BnTransactionCompletedListener overrides
void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence) override;
+ void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence,
+ uint32_t transformHint) override;
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(uint64_t /* graphicsBufferId */);
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 61d72ad..30c42a3 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -247,36 +247,22 @@
mLights.insert_or_assign(info.id, info);
}
-const std::vector<InputDeviceSensorType> InputDeviceInfo::getSensorTypes() {
- std::vector<InputDeviceSensorType> types;
+std::vector<InputDeviceSensorInfo> InputDeviceInfo::getSensors() {
+ std::vector<InputDeviceSensorInfo> infos;
+ infos.reserve(mSensors.size());
for (const auto& [type, info] : mSensors) {
- types.push_back(type);
+ infos.push_back(info);
}
- return types;
+ return infos;
}
-const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorType type) {
- auto it = mSensors.find(type);
- if (it == mSensors.end()) {
- return nullptr;
- }
- return &it->second;
-}
-
-const std::vector<int32_t> InputDeviceInfo::getLightIds() {
- std::vector<int32_t> ids;
+std::vector<InputDeviceLightInfo> InputDeviceInfo::getLights() {
+ std::vector<InputDeviceLightInfo> infos;
+ infos.reserve(mLights.size());
for (const auto& [id, info] : mLights) {
- ids.push_back(id);
+ infos.push_back(info);
}
- return ids;
-}
-
-const InputDeviceLightInfo* InputDeviceInfo::getLightInfo(int32_t id) {
- auto it = mLights.find(id);
- if (it == mLights.end()) {
- return nullptr;
- }
- return &it->second;
+ return infos;
}
} // namespace android
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 4b90844..3038d9d 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -40,4 +40,18 @@
* available.
*/
const int INVALID_INPUT_EVENT_ID = 0;
+
+ /**
+ * The injected event was originally sent from InputDispatcher. Most likely, the journey of the
+ * event looked as follows:
+ * InputDispatcherPolicyInterface::filterInputEvent -> InputFilter.java::onInputEvent ->
+ * InputFilter.java::sendInputEvent -> InputDispatcher::injectInputEvent, without being modified
+ * along the way.
+ */
+ const int POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000;
+
+ /**
+ * The input event was injected from accessibility
+ */
+ const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
}
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index ada689a..75f2385 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -213,6 +213,7 @@
case ANATIVEWINDOW_QUERY_DEFAULT_WIDTH:
case ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT:
case ANATIVEWINDOW_QUERY_TRANSFORM_HINT:
+ case ANATIVEWINDOW_QUERY_BUFFER_AGE:
// these are part of the VNDK API
break;
case ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL:
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index 3243a6b..0eeca54 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -11,19 +11,24 @@
name: "framework-permission-aidl",
unstable: true,
local_include_dir: "aidl",
- backend: {
- ndk: {
- enabled: false
- }
- },
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
srcs: [
"aidl/android/content/AttributionSourceState.aidl",
"aidl/android/permission/IPermissionChecker.aidl",
],
}
-cc_library_shared {
+cc_library {
name: "libpermission",
+ host_supported: true,
+ double_loadable: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
cflags: [
"-Wall",
"-Wextra",
@@ -45,5 +50,7 @@
static_libs: [
"framework-permission-aidl-cpp",
],
- export_static_lib_headers: ["framework-permission-aidl-cpp"],
+ export_static_lib_headers: [
+ "framework-permission-aidl-cpp"
+ ],
}
diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl
index b6e54bf..ed1b37d 100644
--- a/libs/permission/aidl/android/content/AttributionSourceState.aidl
+++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl
@@ -23,8 +23,10 @@
* {@hide}
*/
parcelable AttributionSourceState {
+ /** The PID that is accessing the permission protected data. */
+ int pid = -1;
/** The UID that is accessing the permission protected data. */
- int uid;
+ int uid = -1;
/** The package that is accessing the permission protected data. */
@nullable @utf8InCpp String packageName;
/** The attribution tag of the app accessing the permission protected data. */
@@ -36,5 +38,5 @@
/** The next app to receive the permission protected data. */
// TODO: We use an array as a workaround - the C++ backend doesn't
// support referring to the parcelable as it expects ctor/dtor
- @nullable AttributionSourceState[] next;
+ AttributionSourceState[] next;
}
diff --git a/libs/permission/aidl/android/permission/IPermissionChecker.aidl b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
index 1f0e32d..d3a331e 100644
--- a/libs/permission/aidl/android/permission/IPermissionChecker.aidl
+++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
@@ -28,9 +28,10 @@
int checkPermission(String permission, in AttributionSourceState attributionSource,
@nullable String message, boolean forDataDelivery, boolean startDataDelivery,
- boolean fromDatasource);
+ boolean fromDatasource, int attributedOp);
- void finishDataDelivery(String op, in AttributionSourceState attributionSource);
+ void finishDataDelivery(int op, in AttributionSourceState attributionSource,
+ boolean fromDatasource);
int checkOp(int op, in AttributionSourceState attributionSource,
String message, boolean forDataDelivery, boolean startDataDelivery);
diff --git a/libs/permission/android/permission/PermissionChecker.cpp b/libs/permission/android/permission/PermissionChecker.cpp
index a8083ee..66526f9 100644
--- a/libs/permission/android/permission/PermissionChecker.cpp
+++ b/libs/permission/android/permission/PermissionChecker.cpp
@@ -29,7 +29,7 @@
#endif
#define LOG_TAG "PermissionChecker"
-namespace android {
+namespace android::permission {
using android::content::AttributionSourceState;
@@ -37,7 +37,7 @@
{
}
-sp<IPermissionChecker> PermissionChecker::getService()
+sp<android::permission::IPermissionChecker> PermissionChecker::getService()
{
static String16 permission_checker("permission_checker");
@@ -59,56 +59,74 @@
sleep(1);
} else {
mService = interface_cast<IPermissionChecker>(binder);
+ break;
}
}
return mService;
}
-PermissionChecker::PermissionResult
- PermissionChecker::checkPermissionForDataDeliveryFromDatasource(
- const String16& permission, AttributionSourceState& attributionSource,
- const String16& message)
+PermissionChecker::PermissionResult PermissionChecker::checkPermissionForDataDeliveryFromDatasource(
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode)
{
- return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
- /*forDataDelivery*/ true, /*startDataDelivery*/ false,/*fromDatasource*/ true));
+ return checkPermission(permission, attributionSource, message, /*forDataDelivery*/ true,
+ /*startDataDelivery*/ false,/*fromDatasource*/ true, attributedOpCode);
}
PermissionChecker::PermissionResult
- PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource(
- const String16& permission, AttributionSourceState& attributionSource,
- const String16& message)
+ PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource(
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode)
{
- return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
- /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true));
+ return checkPermission(permission, attributionSource, message, /*forDataDelivery*/ true,
+ /*startDataDelivery*/ true, /*fromDatasource*/ true, attributedOpCode);
}
-void PermissionChecker::finishDataDelivery(const String16& op,
- AttributionSourceState& attributionSource)
+PermissionChecker::PermissionResult PermissionChecker::checkPermissionForPreflight(
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode)
+{
+ return checkPermission(permission, attributionSource, message, /*forDataDelivery*/ false,
+ /*startDataDelivery*/ false, /*fromDatasource*/ false, attributedOpCode);
+}
+
+PermissionChecker::PermissionResult PermissionChecker::checkPermissionForPreflightFromDatasource(
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode)
+{
+ return checkPermission(permission, attributionSource, message, /*forDataDelivery*/ false,
+ /*startDataDelivery*/ false, /*fromDatasource*/ true, attributedOpCode);
+}
+
+void PermissionChecker::finishDataDeliveryFromDatasource(int32_t op,
+ const AttributionSourceState& attributionSource)
{
sp<IPermissionChecker> service = getService();
if (service != nullptr) {
- binder::Status status = service->finishDataDelivery(op, attributionSource);
+ binder::Status status = service->finishDataDelivery(op, attributionSource,
+ /*fromDatasource*/ true);
if (!status.isOk()) {
ALOGE("finishDataDelivery failed: %s", status.exceptionMessage().c_str());
}
}
}
-int32_t PermissionChecker::checkPermission(const String16& permission,
- AttributionSourceState& attributionSource, const String16& message,
- bool forDataDelivery, bool startDataDelivery, bool fromDatasource)
+PermissionChecker::PermissionResult PermissionChecker::checkPermission(const String16& permission,
+ const AttributionSourceState& attributionSource, const String16& message,
+ bool forDataDelivery, bool startDataDelivery, bool fromDatasource,
+ int32_t attributedOpCode)
{
sp<IPermissionChecker> service = getService();
if (service != nullptr) {
int32_t result;
binder::Status status = service->checkPermission(permission, attributionSource, message,
- forDataDelivery, startDataDelivery, fromDatasource, &result);
+ forDataDelivery, startDataDelivery, fromDatasource, attributedOpCode, &result);
if (status.isOk()) {
- return result;
+ return static_cast<PermissionResult>(result);
}
ALOGE("checkPermission failed: %s", status.exceptionMessage().c_str());
}
- return PERMISSION_DENIED;
+ return PERMISSION_HARD_DENIED;
}
-} // namespace android
+} // namespace android::permission
diff --git a/libs/permission/include/android/permission/PermissionChecker.h b/libs/permission/include/android/permission/PermissionChecker.h
index 20ab51f..21515e3 100644
--- a/libs/permission/include/android/permission/PermissionChecker.h
+++ b/libs/permission/include/android/permission/PermissionChecker.h
@@ -30,6 +30,8 @@
// ---------------------------------------------------------------------------
namespace android {
+namespace permission {
+
using android::content::AttributionSourceState;
using android::permission::IPermissionChecker;
@@ -71,7 +73,8 @@
* Checks whether a given data access chain described by the given attribution source
* has a given permission and whether the app op that corresponds to this permission
* is allowed. Call this method if you are the datasource which would not blame you for
- * access to the data since you are the data. Note that the attribution source chain
+ * access to the data since you are the data. Use this API if you are the datasource of
+ * the protected state.
*
* NOTE: The attribution source should be for yourself with its next attribution
* source being the app that would receive the data from you.
@@ -82,18 +85,72 @@
* @param permission The permission to check.
* @param attributionSource The attribution chain to check.
* @param message A message describing the reason the permission was checked.
+ * @param attributedOpCode The op code towards which to blame the access. If this
+ * is a valid app op the op corresponding to the checked permission (if such)
+ * would only be checked to ensure it is allowed and if that succeeds the
+ * noting would be against the attributed op.
* @return The permission check result which is either PERMISSION_GRANTED,
* or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
*/
PermissionChecker::PermissionResult checkPermissionForDataDeliveryFromDatasource(
- const String16& permission, AttributionSourceState& attributionSource,
- const String16& message);
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode);
+
+ /**
+ * Checks whether a given data access chain described by the given attribution source
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. The app ops are not noted/started.
+ *
+ * NOTE: Use this method only for permission checks at the preflight point where you
+ * will not deliver the permission protected data to clients but schedule permission
+ * data delivery, apps register listeners, etc.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message A message describing the reason the permission was checked.
+ * @param attributedOpCode The op code towards which to blame the access. If this
+ * is a valid app op the op corresponding to the checked permission (if such)
+ * would only be checked to ensure it is allowed and if that succeeds the
+ * starting would be against the attributed op.
+ * @return The permission check result which is either PERMISSION_GRANTED,
+ * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+ */
+ PermissionResult checkPermissionForPreflight(
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode);
+
+ /**
+ * Checks whether a given data access chain described by the given attribution source
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. The app ops are not noted/started.
+ *
+ * NOTE: The attribution source should be for yourself with its next attribution
+ * source being the app that would receive the data from you.
+ *
+ * NOTE: Use this method only for permission checks at the preflight point where you
+ * will not deliver the permission protected data to clients but schedule permission
+ * data delivery, apps register listeners, etc.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message A message describing the reason the permission was checked.
+ * @param attributedOpCode The op code towards which to blame the access. If this
+ * is a valid app op the op corresponding to the checked permission (if such)
+ * would only be checked to ensure it is allowed and if that succeeds the
+ * starting would be against the attributed op.
+ * @return The permission check result which is either PERMISSION_GRANTED,
+ * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+ */
+ PermissionResult checkPermissionForPreflightFromDatasource(
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode);
/**
* Checks whether a given data access chain described by the given attribution source
* has a given permission and whether the app op that corresponds to this permission
* is allowed. The app ops are also marked as started. This is useful for long running
- * permissions like camera and microphone.
+ * permissions like camera and microphone. Use this API if you are the datasource of
+ * the protected state.
*
* NOTE: The attribution source should be for yourself with its next attribution
* source being the app that would receive the data from you.
@@ -104,32 +161,45 @@
* @param permission The permission to check.
* @param attributionSource The attribution chain to check.
* @param message A message describing the reason the permission was checked.
+ * @param attributedOpCode The op code towards which to blame the access. If this
+ * is a valid app op the op corresponding to the checked permission (if such)
+ * would only be checked to ensure it is allowed and if that succeeds the
+ * starting would be against the attributed op.
* @return The permission check result which is either PERMISSION_GRANTED,
* or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
*/
PermissionResult checkPermissionForStartDataDeliveryFromDatasource(
- const String16& permission, AttributionSourceState& attributionSource,
- const String16& message);
+ const String16& permission, const AttributionSourceState& attributionSource,
+ const String16& message, int32_t attributedOpCode);
/**
* Finishes an ongoing op for data access chain described by the given
- * attribution source.
+ * attribution source. Use this API if you are the datasource of the protected
+ * state. Use this API if you are the datasource of the protected state.
+ *
+ * NOTE: The attribution source should be for yourself with its next attribution
+ * source being the app that would receive the data from you.
*
* @param op The op to finish.
* @param attributionSource The attribution chain for which to finish data delivery.
+ * @param attributedOpCode The op code towards which to blame the access. If this
+ * is a valid app op it is the op that would be finished.
*/
- void finishDataDelivery(const String16& op, AttributionSourceState& attributionSource);
+ void finishDataDeliveryFromDatasource(int32_t op,
+ const AttributionSourceState& attributionSource);
private:
Mutex mLock;
sp<IPermissionChecker> mService;
sp<IPermissionChecker> getService();
- int32_t checkPermission(const String16& permission, AttributionSourceState& attributionSource,
+ PermissionResult checkPermission(const String16& permission,
+ const AttributionSourceState& attributionSource,
const String16& message, bool forDataDelivery, bool startDataDelivery,
- bool fromDatasource);
+ bool fromDatasource, int32_t attributedOpCode);
};
+} // namespace permission
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 3c58238..b5dd8ac 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,9 +515,10 @@
return mDrawingBuffer.get();
}
-void GLESRenderEngine::primeCache() {
+std::future<void> GLESRenderEngine::primeCache() {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
mUseColorManagement, mPrecacheToneMapperShaderOnly);
+ return {};
}
base::unique_fd GLESRenderEngine::flush() {
@@ -969,37 +970,17 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
+bool GLESRenderEngine::canSkipPostRenderCleanup() const {
+ return mPriorResourcesCleaned ||
+ (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled);
+}
+
+void GLESRenderEngine::cleanupPostRender() {
ATRACE_CALL();
- if (mPriorResourcesCleaned ||
- (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
+ if (canSkipPostRenderCleanup()) {
// If we don't have a prior frame needing cleanup, then don't do anything.
- return false;
- }
-
- // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
- // not need to keep memory around if we don't need to do so.
- if (mode == CleanupMode::CLEAN_ALL) {
- // TODO: SurfaceFlinger memory utilization may benefit from resetting
- // texture bindings as well. Assess if it does and there's no performance regression
- // when rebinding the same image data to the same texture, and if so then its mode
- // behavior can be tweaked.
- if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
- for (auto [textureName, bufferId] : mTextureView) {
- if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
- static_cast<GLeglImageOES>(mPlaceholderImage));
- mTextureView[textureName] = std::nullopt;
- checkErrors();
- }
- }
- }
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- mImageCache.clear();
- }
+ return;
}
// Bind the texture to placeholder so that backing image data can be freed.
@@ -1010,7 +991,6 @@
// we could no-op repeated calls of this method instead.
mLastDrawFence = nullptr;
mPriorResourcesCleaned = true;
- return true;
}
void GLESRenderEngine::cleanFramebufferCache() {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index e7ed9c0..915dba3 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() override;
+ std::future<void> primeCache() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
bool isProtected() const override { return mInProtectedContext; }
@@ -68,7 +68,7 @@
const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) override;
- bool cleanupPostRender(CleanupMode mode) override;
+ void cleanupPostRender() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
void onPrimaryDisplaySizeChanged(ui::Size size) override {}
@@ -106,6 +106,7 @@
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
EXCLUDES(mRenderingMutex);
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+ bool canSkipPostRenderCleanup() const override;
private:
friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ddaa7c7..ac0affb 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -29,6 +29,7 @@
#include <ui/GraphicTypes.h>
#include <ui/Transform.h>
+#include <future>
#include <memory>
/**
@@ -43,6 +44,16 @@
*/
#define PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS "debug.renderengine.capture_skia_ms"
+/**
+ * Set to the most recently saved file once the capture is finished.
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME "debug.renderengine.capture_filename"
+
+/**
+ * Allows recording of Skia drawing commands with systrace.
+ */
+#define PROPERTY_SKIA_ATRACE_ENABLED "debug.renderengine.skia_atrace_enabled"
+
struct ANativeWindowBuffer;
namespace android {
@@ -89,17 +100,13 @@
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
- RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
-
- RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
-
virtual ~RenderEngine() = 0;
// ----- BEGIN DEPRECATED INTERFACE -----
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual void primeCache() = 0;
+ virtual std::future<void> primeCache() = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
@@ -107,25 +114,6 @@
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
- enum class CleanupMode {
- CLEAN_OUTPUT_RESOURCES,
- CLEAN_ALL,
- };
- // Clean-up method that should be called on the main thread after the
- // drawFence returned by drawLayers fires. This method will free up
- // resources used by the most recently drawn frame. If the frame is still
- // being drawn, then this call is silently ignored.
- //
- // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
- // output framebuffer are cleaned up, including the sibling texture.
- //
- // If mode is CLEAN_ALL, then we also cleanup resources related to any input
- // buffers.
- //
- // Returns true if resources were cleaned up, and false if we didn't need to
- // do any work.
- virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
-
// queries that are required to be thread safe
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
@@ -179,6 +167,13 @@
const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) = 0;
+
+ // Clean-up method that should be called on the main thread after the
+ // drawFence returned by drawLayers fires. This method will free up
+ // resources used by the most recently drawn frame. If the frame is still
+ // being drawn, then the implementation is free to silently ignore this call.
+ virtual void cleanupPostRender() = 0;
+
virtual void cleanFramebufferCache() = 0;
// Returns the priority this context was actually created with. Note: this may not be
// the same as specified at context creation time, due to implementation limits on the
@@ -199,6 +194,10 @@
static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
protected:
+ RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+
+ RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
+
// Maps GPU resources for this buffer.
// Note that work may be deferred to an additional thread, i.e. this call
// is made asynchronously, but the caller can expect that map/unmap calls
@@ -223,8 +222,15 @@
// that's conflict serializable, i.e. unmap a buffer should never occur before binding the
// buffer if the caller called mapExternalTextureBuffer before calling unmap.
virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+
+ // A thread safe query to determine if any post rendering cleanup is necessary. Returning true
+ // is a signal that calling the postRenderCleanup method would be a no-op and that callers can
+ // avoid any thread synchronization that may be required by directly calling postRenderCleanup.
+ virtual bool canSkipPostRenderCleanup() const = 0;
+
friend class ExternalTexture;
friend class threaded::RenderEngineThreaded;
+ friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
const RenderEngineType mRenderEngineType;
};
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 27dbd1e..0175af3 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD0(primeCache, void());
+ MOCK_METHOD0(primeCache, std::future<void>());
MOCK_METHOD1(dump, void(std::string&));
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
@@ -45,7 +45,8 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
- MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
+ MOCK_METHOD0(cleanupPostRender, void());
+ MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 8ae69de..5c122d4 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -29,8 +29,8 @@
namespace skia {
AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
- bool isOutputBuffer)
- : mIsOutputBuffer(isOutputBuffer) {
+ bool isOutputBuffer, CleanupManager& cleanupMgr)
+ : mCleanupMgr(cleanupMgr), mIsOutputBuffer(isOutputBuffer) {
ATRACE_CALL();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
@@ -49,6 +49,13 @@
this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
}
+AutoBackendTexture::~AutoBackendTexture() {
+ if (mBackendTexture.isValid()) {
+ mDeleteProc(mImageCtx);
+ mBackendTexture = {};
+ }
+}
+
void AutoBackendTexture::unref(bool releaseLocalResources) {
if (releaseLocalResources) {
mSurface = nullptr;
@@ -57,11 +64,7 @@
mUsageCount--;
if (mUsageCount <= 0) {
- if (mBackendTexture.isValid()) {
- mDeleteProc(mImageCtx);
- mBackendTexture = {};
- }
- delete this;
+ mCleanupMgr.add(this);
}
}
@@ -87,8 +90,15 @@
mUpdateProc(mImageCtx, context);
}
+ auto colorType = mColorType;
+ if (alphaType == kOpaque_SkAlphaType) {
+ if (colorType == kRGBA_8888_SkColorType) {
+ colorType = kRGB_888x_SkColorType;
+ }
+ }
+
sk_sp<SkImage> image =
- SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, mColorType,
+ SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType,
alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
if (image.get()) {
// The following ref will be counteracted by releaseProc, when SkImage is discarded.
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 3133de6..00b901b 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -25,6 +25,9 @@
#include "android-base/macros.h"
+#include <mutex>
+#include <vector>
+
namespace android {
namespace renderengine {
namespace skia {
@@ -36,13 +39,50 @@
*/
class AutoBackendTexture {
public:
+ // Manager class that is responsible for the immediate or deferred cleanup
+ // of AutoBackendTextures. Clients of AutoBackendTexture are responsible for
+ // ensuring that access to this class is thread safe. Clients also control when
+ // the resources are reclaimed by setting the manager into deferred mode.
+ class CleanupManager {
+ public:
+ CleanupManager() = default;
+ void add(AutoBackendTexture* abt) {
+ if (mDeferCleanup) {
+ mCleanupList.push_back(abt);
+ } else {
+ delete abt;
+ }
+ }
+
+ void setDeferredStatus(bool enabled) { mDeferCleanup = enabled; }
+
+ bool isEmpty() const { return mCleanupList.empty(); }
+
+ // If any AutoBackedTextures were added while in deferred mode this method
+ // will ensure they are deleted before returning. It must only be called
+ // on the thread where the GPU context that created the AutoBackedTexture
+ // is active.
+ void cleanup() {
+ for (auto abt : mCleanupList) {
+ delete abt;
+ }
+ mCleanupList.clear();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CleanupManager);
+ bool mDeferCleanup = false;
+ std::vector<AutoBackendTexture*> mCleanupList;
+ };
+
// Local reference that supports RAII-style management of an AutoBackendTexture
// AutoBackendTexture by itself can't be managed in a similar fashion because
// of shared ownership with Skia objects, so we wrap it here instead.
class LocalRef {
public:
- LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer) {
- mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer);
+ LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+ CleanupManager& cleanupMgr) {
+ mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr);
mTexture->ref();
}
@@ -65,6 +105,8 @@
return mTexture->getOrCreateSurface(dataspace, context);
}
+ SkColorType colorType() const { return mTexture->mColorType; }
+
DISALLOW_COPY_AND_ASSIGN(LocalRef);
private:
@@ -73,10 +115,11 @@
private:
// Creates a GrBackendTexture whose contents come from the provided buffer.
- AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer);
+ AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+ CleanupManager& cleanupMgr);
// The only way to invoke dtor is with unref, when mUsageCount is 0.
- ~AutoBackendTexture() {}
+ ~AutoBackendTexture();
void ref() { mUsageCount++; }
@@ -98,6 +141,8 @@
GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+ CleanupManager& mCleanupMgr;
+
static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
static void releaseImageProc(SkImage::ReleaseContext releaseContext);
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 77e01f4..b3975b0 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -37,6 +37,10 @@
0.f, 0.7f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
67.3f, 52.2f, 0.f, 1.f);
+const auto kScaleAsymmetric = mat4(0.8f, 0.f, 0.f, 0.f,
+ 0.f, 1.1f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);
// clang-format on
// When setting layer.sourceDataspace, whether it matches the destination or not determines whether
// a color correction effect is added to the shader.
@@ -77,12 +81,7 @@
// This matrix, which has different scales for x and y, will
// generate the slower (more general case) version, which has variants for translucent
// casters and rounded rects.
- // clang-format off
- layer.geometry.positionTransform = mat4(0.7f, 0.f, 0.f, 0.f,
- 0.f, 0.8f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 0.f, 0.f, 0.f, 1.f);
- // clang-format on
+ layer.geometry.positionTransform = kScaleAsymmetric;
for (auto translucent : {false, true}) {
layer.shadow.casterIsTranslucent = translucent;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
@@ -150,7 +149,7 @@
PixelSource{
.solidColor = half3(0.1f, 0.2f, 0.3f),
},
- .alpha = 1,
+ .alpha = 0.5,
};
auto layers = std::vector<const LayerSettings*>{&layer};
@@ -186,23 +185,26 @@
// The unique feature of these layers is that the boundary is slightly smaller than the rounded
// rect crop, so the rounded edges intersect that boundary and require a different clipping method.
+// For buffers, this is done with a stage that computes coverage and it will differ for round and
+// elliptical corners.
static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
const std::shared_ptr<ExternalTexture>& dstTexture,
const std::shared_ptr<ExternalTexture>& srcTexture) {
const Rect& displayRect = display.physicalDisplay;
FloatRect rect(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller
- // clang-format off
- const auto symmetric = mat4(0.9f, 0.f, 0.f, 0.f,
- 0.f, 0.9f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 8.8f, 8.1f, 0.f, 1.f);
- const auto asymmetric = mat4(0.9f, 0.f, 0.f, 0.f,
- 0.f, 0.7f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 8.8f, 8.1f, 0.f, 1.f);
+ PixelSource bufferSource{.buffer = Buffer{
+ .buffer = srcTexture,
+ .isOpaque = 0,
+ .maxLuminanceNits = 1000.f,
+ }};
+ PixelSource bufferOpaque{.buffer = Buffer{
+ .buffer = srcTexture,
+ .isOpaque = 1,
+ .maxLuminanceNits = 1000.f,
+ }};
+ PixelSource colorSource{.solidColor = half3(0.1f, 0.2f, 0.3f)};
- // clang-format on
LayerSettings layer{
.geometry =
Geometry{
@@ -211,23 +213,24 @@
.roundedCornersCrop =
FloatRect(0, 0, displayRect.width(), displayRect.height()),
},
- .source = PixelSource{.buffer =
- Buffer{
- .buffer = srcTexture,
- .isOpaque = 0,
- .maxLuminanceNits = 1000.f,
- }},
- .sourceDataspace = kOtherDataSpace,
};
auto layers = std::vector<const LayerSettings*>{&layer};
- for (auto transform : {symmetric, asymmetric}) {
- layer.geometry.positionTransform = transform;
- // In real use, I saw alpha of 1.0 and 0.999, probably a mistake, but cache both shaders.
- for (float alpha : {0.5f, 1.f}) {
- layer.alpha = alpha,
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
+ layer.source = pixelSource;
+ for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
+ layer.sourceDataspace = dataspace;
+ // Produce a CircularRRect clip and an EllipticalRRect clip
+ for (auto transform : {kScaleAndTranslate, kScaleAsymmetric}) {
+ layer.geometry.positionTransform = transform;
+ // In real use, I saw alpha of 1.0 and 0.999, probably a mistake, but cache both
+ // shaders.
+ for (float alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha,
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+ }
}
}
}
@@ -289,7 +292,11 @@
drawSolidLayers(renderengine, display, dstTexture);
drawShadowLayers(renderengine, display, srcTexture);
- drawBlurLayers(renderengine, display, dstTexture);
+
+ if (renderengine->supportsBackgroundBlur()) {
+ drawBlurLayers(renderengine, display, dstTexture);
+ }
+
// The majority of shaders are related to sampling images.
drawImageLayers(renderengine, display, dstTexture, srcTexture);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 47c330f..d28d623 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -36,6 +36,7 @@
#include <SkSurface.h>
#include <android-base/stringprintf.h>
#include <gl/GrGLInterface.h>
+#include <gui/TraceUtils.h>
#include <sync/sync.h>
#include <ui/BlurRegion.h>
#include <ui/DebugUtils.h>
@@ -234,8 +235,9 @@
return engine;
}
-void SkiaGLRenderEngine::primeCache() {
+std::future<void> SkiaGLRenderEngine::primeCache() {
Cache::primeShaderCache(this);
+ return {};
}
EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
@@ -315,6 +317,7 @@
GrContextOptions options;
options.fDisableDriverCorrectnessWorkarounds = true;
options.fDisableDistanceFieldPaths = true;
+ options.fReducedShaderVariations = true;
options.fPersistentCache = &mSkSLCacheMonitor;
mGrContext = GrDirectContext::MakeGL(glInterface, options);
if (useProtectedContext(true)) {
@@ -506,17 +509,18 @@
return;
}
// We currently don't attempt to map a buffer if the buffer contains protected content
- // or we are using a protected context because GPU resources for protected buffers is
- // much more limited.
+ // because GPU resources for protected buffers is much more limited.
const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
- if (isProtectedBuffer || mInProtectedContext) {
+ if (isProtectedBuffer) {
return;
}
ATRACE_CALL();
- // If we were to support caching protected buffers then we will need to switch the currently
- // bound context if we are not already using the protected context (and subsequently switch
- // back after the buffer is cached).
+ // If we were to support caching protected buffers then we will need to switch the
+ // currently bound context if we are not already using the protected context (and subsequently
+ // switch back after the buffer is cached). However, for non-protected content we can bind
+ // the texture in either GL context because they are initialized with the same share_context
+ // which allows the texture state to be shared between them.
auto grContext = getActiveGrContext();
auto& cache = mTextureCache;
@@ -527,7 +531,7 @@
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
- isRenderable);
+ isRenderable, mTextureCleanupMgr);
cache.insert({buffer->getId(), imageTextureRef});
}
}
@@ -550,12 +554,36 @@
if (iter->second == 0) {
mTextureCache.erase(buffer->getId());
- mProtectedTextureCache.erase(buffer->getId());
mGraphicBufferExternalRefs.erase(buffer->getId());
}
}
}
+bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return mTextureCleanupMgr.isEmpty();
+}
+
+void SkiaGLRenderEngine::cleanupPostRender() {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mTextureCleanupMgr.cleanup();
+}
+
+// Helper class intended to be used on the stack to ensure that texture cleanup
+// is deferred until after this class goes out of scope.
+class DeferTextureCleanup final {
+public:
+ DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
+ mMgr.setDeferredStatus(true);
+ }
+ ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
+ AutoBackendTexture::CleanupManager& mMgr;
+};
+
sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
sk_sp<SkShader> shader,
const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
@@ -702,7 +730,10 @@
validateOutputBufferUsage(buffer->getBuffer());
auto grContext = getActiveGrContext();
- auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+ auto& cache = mTextureCache;
+
+ // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
+ DeferTextureCleanup dtc(mTextureCleanupMgr);
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
@@ -712,7 +743,7 @@
std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->getBuffer()
->toAHardwareBuffer(),
- true);
+ true, mTextureCleanupMgr);
}
const ui::Dataspace dstDataspace =
@@ -792,7 +823,7 @@
}
for (const auto& layer : layers) {
- ATRACE_NAME("DrawLayer");
+ ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
if (kPrintLayerSettings) {
std::stringstream ls;
@@ -968,14 +999,31 @@
imageTextureRef = std::make_shared<
AutoBackendTexture::LocalRef>(grContext,
item.buffer->getBuffer()->toAHardwareBuffer(),
- false);
+ false, mTextureCleanupMgr);
}
- sk_sp<SkImage> image =
- imageTextureRef->makeImage(layerDataspace,
- item.usePremultipliedAlpha ? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType,
- grContext);
+ // isOpaque means we need to ignore the alpha in the image,
+ // replacing it with the alpha specified by the LayerSettings. See
+ // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
+ // The proper way to do this is to use an SkColorType that ignores
+ // alpha, like kRGB_888x_SkColorType, and that is used if the
+ // incoming image is kRGBA_8888_SkColorType. However, the incoming
+ // image may be kRGBA_F16_SkColorType, for which there is no RGBX
+ // SkColorType, or kRGBA_1010102_SkColorType, for which we have
+ // kRGB_101010x_SkColorType, but it is not yet supported as a source
+ // on the GPU. (Adding both is tracked in skbug.com/12048.) In the
+ // meantime, we'll use a workaround that works unless we need to do
+ // any color conversion. The workaround requires that we pretend the
+ // image is already premultiplied, so that we do not premultiply it
+ // before applying SkBlendMode::kPlus.
+ const bool useIsOpaqueWorkaround = item.isOpaque &&
+ (imageTextureRef->colorType() == kRGBA_1010102_SkColorType ||
+ imageTextureRef->colorType() == kRGBA_F16_SkColorType);
+ const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType
+ : item.isOpaque ? kOpaque_SkAlphaType
+ : item.usePremultipliedAlpha ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType;
+ sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -1004,27 +1052,7 @@
shader = image->makeShader(SkSamplingOptions(), matrix);
}
- // Handle opaque images - it's a little nonstandard how we do this.
- // Fundamentally we need to support SurfaceControl.Builder#setOpaque:
- // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
- // The important language is that when isOpaque is set, opacity is not sampled from the
- // alpha channel, but blending may still be supported on a transaction via setAlpha. So,
- // here's the conundrum:
- // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated
- // as an internal hint - composition is undefined when there are alpha bits present.
- // 2. We can try to lie about the pixel layout, but that only works for RGBA8888
- // buffers, i.e., treating them as RGBx8888 instead. But we can't do the same for
- // RGBA1010102 because RGBx1010102 is not supported as a pixel layout for SkImages. It's
- // also not clear what to use for F16 either, and lying about the pixel layout is a bit
- // of a hack anyways.
- // 3. We can't change the blendmode to src, because while this satisfies the requirement
- // for ignoring the alpha channel, it doesn't quite satisfy the blending requirement
- // because src always clobbers the destination content.
- //
- // So, what we do here instead is an additive blend mode where we compose the input
- // image with a solid black. This might need to be reassess if this does not support
- // FP16 incredibly well, but FP16 end-to-end isn't well supported anyway at the moment.
- if (item.isOpaque) {
+ if (useIsOpaqueWorkaround) {
shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
SkShaders::Color(SkColors::kBlack,
toSkColorSpace(layerDataspace)));
@@ -1448,12 +1476,6 @@
StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
gpuProtectedReporter.logOutput(result, true);
- StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
- mProtectedTextureCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, unused] : mProtectedTextureCache) {
- StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
- }
StringAppendF(&result, "\n");
StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
for (const auto& [linearEffect, unused] : mRuntimeEffects) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 97d3b72..b30355b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -53,13 +53,14 @@
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() override;
+ std::future<void> primeCache() override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) override;
- void cleanFramebufferCache() override {}
+ void cleanupPostRender() override;
+ void cleanFramebufferCache() override{};
int getContextPriority() override;
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
@@ -75,6 +76,7 @@
size_t getMaxViewportDims() const override;
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ bool canSkipPostRenderCleanup() const override;
private:
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -126,19 +128,18 @@
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
GUARDED_BY(mRenderingMutex);
- // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+ // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
- mProtectedTextureCache GUARDED_BY(mRenderingMutex);
std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+ AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
// multiple threads is guaranteed thread-safe.
- std::mutex mRenderingMutex;
+ mutable std::mutex mRenderingMutex;
sp<Fence> mLastDrawFence;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 81f0b6f..29175a2 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -14,13 +14,22 @@
* limitations under the License.
*/
-//#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "SkiaRenderEngine.h"
+
+#include <android-base/properties.h>
+#include <src/core/SkTraceEventCommon.h>
+
namespace android {
namespace renderengine {
-namespace skia {} // namespace skia
+namespace skia {
+SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {
+ SkAndroidFrameworkTraceUtil::setEnableTracing(
+ base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
+}
+} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 308c5ff..31ad63e 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -36,10 +36,10 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
+ SkiaRenderEngine(RenderEngineType type);
~SkiaRenderEngine() override {}
- virtual void primeCache() override{};
+ virtual std::future<void> primeCache() override { return {}; };
virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
virtual bool isProtected() const override { return false; } // mInProtectedContext; }
@@ -53,15 +53,14 @@
base::unique_fd* /*drawFence*/) override {
return 0;
};
- virtual bool cleanupPostRender(CleanupMode) override { return true; };
virtual int getContextPriority() override { return 0; }
virtual void assertShadersCompiled(int numShaders) {}
virtual int reportShadersCompiled() { return 0; }
protected:
virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
- bool /*isRenderable*/) override;
- virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override;
+ bool /*isRenderable*/) override = 0;
+ virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
};
} // namespace skia
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index 40f5cf2..856fff4 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -34,7 +34,7 @@
namespace skia {
// The root of the filename to write a recorded SKP to. In order for this file to
-// be written to /data/user/, user must run 'adb shell setenforce 0' in the device.
+// be written to /data/user/, user must run 'adb shell setenforce 0' on the device.
static const std::string CAPTURED_FILENAME_BASE = "/data/user/re_skiacapture";
SkiaCapture::~SkiaCapture() {
@@ -152,11 +152,12 @@
// a smart pointer makes the lambda non-copyable. The lambda is only called
// once, so this is safe.
SkFILEWStream* stream = mOpenMultiPicStream.release();
- CommonPool::post([doc = std::move(mMultiPic), stream] {
+ CommonPool::post([doc = std::move(mMultiPic), stream, name = std::move(mCaptureFile)] {
ALOGD("Finalizing multi frame SKP");
doc->close();
delete stream;
- ALOGD("Multi frame SKP complete.");
+ ALOGD("Multi frame SKP saved to %s.", name.c_str());
+ base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, name);
});
mCaptureRunning = false;
}
@@ -164,12 +165,14 @@
bool SkiaCapture::setupMultiFrameCapture() {
ATRACE_CALL();
ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
+ base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, "");
+ const std::scoped_lock lock(mMutex);
- std::string captureFile;
// Attach a timestamp to the file.
- base::StringAppendF(&captureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(),
+ mCaptureFile.clear();
+ base::StringAppendF(&mCaptureFile, "%s_%lld.mskp", CAPTURED_FILENAME_BASE.c_str(),
std::chrono::steady_clock::now().time_since_epoch().count());
- auto stream = std::make_unique<SkFILEWStream>(captureFile.c_str());
+ auto stream = std::make_unique<SkFILEWStream>(mCaptureFile.c_str());
// We own this stream and need to hold it until close() finishes.
if (stream->isValid()) {
mOpenMultiPicStream = std::move(stream);
@@ -194,7 +197,7 @@
mCaptureRunning = true;
return true;
} else {
- ALOGE("Could not open \"%s\" for writing.", captureFile.c_str());
+ ALOGE("Could not open \"%s\" for writing.", mCaptureFile.c_str());
return false;
}
}
diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h
index 5e18e60..f194629 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.h
+++ b/libs/renderengine/skia/debug/SkiaCapture.h
@@ -85,6 +85,8 @@
// Mutex to ensure that a frame in progress when the timer fires is allowed to run to
// completion before we write the file to disk.
std::mutex mMutex;
+
+ std::string mCaptureFile;
};
} // namespace skia
diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh
index 25c8cef..e99b7ae 100755
--- a/libs/renderengine/skia/debug/record.sh
+++ b/libs/renderengine/skia/debug/record.sh
@@ -16,14 +16,22 @@
# first time use requires these changes
adb root
adb shell setenforce 0
- adb shell setprop debug.renderengine.backend "skiagl"
+ adb shell setprop debug.renderengine.backend "skiaglthreaded"
adb shell stop
adb shell start
exit 1;
fi
-# name of the newest file in /data/user/ before starting
-oldname=$(adb shell ls -cr /data/user/ | head -n 1)
+check_permission() {
+ adb shell getenforce
+}
+
+mode=$(check_permission)
+
+if [ "$mode" != "Permissive" ]; then
+ echo "Cannot write to disk from RenderEngine. run 'record.sh rootandsetup'"
+ exit 5
+fi
# record frames for some number of milliseconds.
adb shell setprop debug.renderengine.capture_skia_ms $1
@@ -38,26 +46,6 @@
# the process it is recording.
# /data/user/re_skiacapture_56204430551705.mskp
-# list the files here from newest to oldest, keep only the name of the newest.
-name=$(adb shell ls -cr /data/user/ | head -n 1)
-remote_path=/data/user/$name
-
-if [[ $oldname = $name ]]; then
- echo "No new file written, probably no RenderEngine activity during recording period."
- exit 1
-fi
-
-# return the size of a file in bytes
-adb_filesize() {
- adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}'
-}
-
-mskp_size=$(adb_filesize "/data/user/$name")
-if [[ $mskp_size = "0" ]]; then
- echo "File opened, but remains empty after recording period + wait. Either there was no RenderEngine activity during recording period, or recording process is still working. Check /data/user/$name manually later."
- exit 1
-fi
-
spin() {
case "$spin" in
1) printf '\b|';;
@@ -69,38 +57,28 @@
sleep $1
}
-printf "MSKP captured, Waiting for file serialization to finish.\n"
+local_path=~/Downloads/
-local_path=~/Downloads/$name
+get_filename() {
+ adb shell getprop debug.renderengine.capture_filename
+}
-# wait for the file size to stop changing
-
-timeout=$(( $(date +%s) + 300))
-last_size='0' # output of last size check command
-unstable=true # false once the file size stops changing
-counter=0 # used to perform size check only 1/sec though we update spinner 20/sec
-# loop until the file size is unchanged for 1 second.
-while [ $unstable != 0 ] ; do
+remote_path=""
+counter=0 # used to check only 1/sec though we update spinner 20/sec
+while [ -z $remote_path ] ; do
spin 0.05
counter=$(( $counter+1 ))
if ! (( $counter % 20)) ; then
- new_size=$(adb_filesize "$remote_path")
- unstable=$(($new_size != $last_size))
- last_size=$new_size
- fi
- if [ $(date +%s) -gt $timeout ] ; then
- printf '\bTimed out.\n'
- exit 3
+ remote_path=$(get_filename)
fi
done
printf '\b'
-printf "MSKP file serialized: %s\n" $(echo $last_size | numfmt --to=iec)
+printf "MSKP file serialized to: $remote_path\n"
-adb pull "$remote_path" "$local_path"
-if ! [ -f "$local_path" ] ; then
- printf "something went wrong with `adb pull`."
- exit 4
-fi
+adb_pull_cmd="adb pull $remote_path $local_path"
+echo $adb_pull_cmd
+$adb_pull_cmd
+
adb shell rm "$remote_path"
-printf 'SKP saved to %s\n\n' "$local_path"
\ No newline at end of file
+printf 'SKP saved to %s\n\n' "$local_path"
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 1ca7a16..e258741 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -43,6 +43,7 @@
constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
namespace android {
+namespace renderengine {
class RenderEngineFactory {
public:
@@ -54,6 +55,7 @@
virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
return nullptr;
}
+ virtual bool useColorManagement() const = 0;
};
class GLESRenderEngineFactory : public RenderEngineFactory {
@@ -82,6 +84,8 @@
.build();
return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
+
+ bool useColorManagement() const override { return false; }
};
class GLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -110,6 +114,8 @@
.build();
return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
+
+ bool useColorManagement() const override { return true; }
};
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
@@ -130,9 +136,16 @@
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
+ // FIXME (b/189935602): This version is currently color managed.
+ // We should change it and fix the tests that fail.
+ //.setUseColorManagerment(false)
.build();
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
+
+ // FIXME (b/189935602): This version is currently color managed.
+ // We should change it and fix the tests that fail.
+ bool useColorManagement() const override { return true; }
};
class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -157,6 +170,8 @@
.build();
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
+
+ bool useColorManagement() const override { return true; }
};
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -295,6 +310,7 @@
const uint8_t expected[4] = {r, g, b, a};
bool equal = colorCompare(src, expected);
EXPECT_TRUE(equal)
+ << GetParam()->name().c_str() << ": "
<< "pixel @ (" << region.left + i << ", " << region.top + j << "): "
<< "expected (" << static_cast<uint32_t>(r) << ", "
<< static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
@@ -1764,13 +1780,6 @@
}
TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
- const auto& renderEngineFactory = GetParam();
-
- if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
- // GLES-specific test
- return;
- }
-
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -1795,53 +1804,9 @@
sync_wait(fd, -1);
}
// Only cleanup the first time.
- EXPECT_TRUE(mRE->cleanupPostRender(
- renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
- EXPECT_FALSE(mRE->cleanupPostRender(
- renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
-}
-
-TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
- const auto& renderEngineFactory = GetParam();
-
- if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
- // GLES-specific test
- return;
- }
-
- initializeRenderEngine();
-
- renderengine::DisplaySettings settings;
- settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- settings.physicalDisplay = fullscreenRect();
- settings.clip = fullscreenRect();
-
- std::vector<const renderengine::LayerSettings*> layers;
- renderengine::LayerSettings layer;
- layer.geometry.boundaries = fullscreenRect().toFloatRect();
- BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layer.alpha = 1.0;
- layers.push_back(&layer);
-
- base::unique_fd fence;
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
-
- const int fd = fence.get();
- if (fd >= 0) {
- sync_wait(fd, -1);
- }
-
- uint64_t bufferId = layer.source.buffer.buffer->getBuffer()->getId();
- uint32_t texName = layer.source.buffer.textureName;
- EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
-
- EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
-
- // Now check that our view of memory is good.
- EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(std::nullopt, mGLESRE->getBufferIdForTextureNameForTesting(bufferId));
- EXPECT_TRUE(mGLESRE->isTextureNameKnownForTesting(texName));
+ EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
+ mRE->cleanupPostRender();
+ EXPECT_TRUE(mRE->canSkipPostRenderCleanup());
}
TEST_P(RenderEngineTest, testRoundedCornersCrop) {
@@ -2015,6 +1980,57 @@
expectBufferColor(rect, 0, 128, 0, 128);
}
+TEST_P(RenderEngineTest, test_isOpaque) {
+ initializeRenderEngine();
+
+ const auto rect = Rect(0, 0, 1, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ };
+
+ // Create an unpremul buffer that is green with no alpha. Using isOpaque
+ // should make the green show.
+ const auto buf = allocateSourceBuffer(1, 1);
+ {
+ uint8_t* pixels;
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 0;
+ pixels[1] = 255;
+ pixels[2] = 0;
+ pixels[3] = 0;
+ buf->getBuffer()->unlock();
+ }
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = buf,
+ // Although the pixels are not
+ // premultiplied in practice, this
+ // matches the input we see.
+ .usePremultipliedAlpha = true,
+ .isOpaque = true,
+ },
+ },
+ .alpha = 1.0f,
+ };
+
+ std::vector<const renderengine::LayerSettings*> layers{&greenLayer};
+ invokeDraw(display, layers);
+
+ if (GetParam()->useColorManagement()) {
+ expectBufferColor(rect, 117, 251, 76, 255);
+ } else {
+ expectBufferColor(rect, 0, 255, 0, 255);
+ }
+}
+} // namespace renderengine
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index e3917cc..c65e731 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -130,22 +130,22 @@
ASSERT_EQ(true, result);
}
-TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
- EXPECT_CALL(*mRenderEngine,
- cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
- .WillOnce(Return(false));
- status_t result =
- mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
- ASSERT_EQ(false, result);
+TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) {
+ EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0);
+ mThreadedRE->cleanupPostRender();
+
+ // call ANY synchronous function to ensure that cleanupPostRender has completed.
+ mThreadedRE->getContextPriority();
}
-TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
- EXPECT_CALL(*mRenderEngine,
- cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
- .WillOnce(Return(true));
- status_t result =
- mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
- ASSERT_EQ(true, result);
+TEST_F(RenderEngineThreadedTest, PostRenderCleanup_notSkipped) {
+ EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return());
+ mThreadedRE->cleanupPostRender();
+
+ // call ANY synchronous function to ensure that cleanupPostRender has completed.
+ mThreadedRE->getContextPriority();
}
TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsFalse) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 9009ce4..ea3871f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -24,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <private/gui/SyncFeatures.h>
+#include <processgroup/processgroup.h>
#include <utils/Trace.h>
#include "gl/GLESRenderEngine.h"
@@ -48,44 +49,74 @@
}
RenderEngineThreaded::~RenderEngineThreaded() {
- {
- std::lock_guard lock(mThreadMutex);
- mRunning = false;
- mCondition.notify_one();
- }
+ mRunning = false;
+ mCondition.notify_one();
if (mThread.joinable()) {
mThread.join();
}
}
+status_t RenderEngineThreaded::setSchedFifo(bool enabled) {
+ static constexpr int kFifoPriority = 2;
+ static constexpr int kOtherPriority = 0;
+
+ struct sched_param param = {0};
+ int sched_policy;
+ if (enabled) {
+ sched_policy = SCHED_FIFO;
+ param.sched_priority = kFifoPriority;
+ } else {
+ sched_policy = SCHED_OTHER;
+ param.sched_priority = kOtherPriority;
+ }
+
+ if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
+ return -errno;
+ }
+ return NO_ERROR;
+}
+
// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO");
+ if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
+ ALOGW("Failed to set render-engine task profile!");
+ }
+
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO");
}
mRenderEngine = factory();
- std::unique_lock<std::mutex> lock(mThreadMutex);
pthread_setname_np(pthread_self(), mThreadName);
{
- std::unique_lock<std::mutex> lock(mInitializedMutex);
+ std::scoped_lock lock(mInitializedMutex);
mIsInitialized = true;
}
mInitializedCondition.notify_all();
while (mRunning) {
- if (!mFunctionCalls.empty()) {
- auto task = mFunctionCalls.front();
- mFunctionCalls.pop();
- task(*mRenderEngine);
+ const auto getNextTask = [this]() -> std::optional<Work> {
+ std::scoped_lock lock(mThreadMutex);
+ if (!mFunctionCalls.empty()) {
+ Work task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ return std::make_optional<Work>(task);
+ }
+ return std::nullopt;
+ };
+
+ const auto task = getNextTask();
+
+ if (task) {
+ (*task)(*mRenderEngine);
}
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
return !mRunning || !mFunctionCalls.empty();
});
@@ -100,18 +131,31 @@
mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
}
-void RenderEngineThreaded::primeCache() {
+std::future<void> RenderEngineThreaded::primeCache() {
+ const auto resultPromise = std::make_shared<std::promise<void>>();
+ std::future<void> resultFuture = resultPromise->get_future();
ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::primeCache");
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_OTHER for primeCache");
+ }
+
instance.primeCache();
+ resultPromise->set_value();
+
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO for primeCache");
+ }
});
}
mCondition.notify_one();
+
+ return resultFuture;
}
void RenderEngineThreaded::dump(std::string& result) {
@@ -231,19 +275,26 @@
return resultFuture.get();
}
-bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
- std::promise<bool> resultPromise;
- std::future<bool> resultFuture = resultPromise.get_future();
+void RenderEngineThreaded::cleanupPostRender() {
+ if (canSkipPostRenderCleanup()) {
+ return;
+ }
+
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::cleanupPostRender");
- bool returnValue = instance.cleanupPostRender(mode);
- resultPromise.set_value(returnValue);
+ mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ instance.cleanupPostRender();
});
}
mCondition.notify_one();
- return resultFuture.get();
+}
+
+bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
+ waitUntilInitialized();
+ return mRenderEngine->canSkipPostRenderCleanup();
}
status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index eb6098e..9b523b2 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
- void primeCache() override;
+ std::future<void> primeCache() override;
void dump(std::string& result) override;
@@ -54,7 +54,7 @@
bool isProtected() const override;
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
- bool cleanupPostRender(CleanupMode mode) override;
+ void cleanupPostRender() override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
@@ -70,10 +70,12 @@
protected:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ bool canSkipPostRenderCleanup() const override;
private:
void threadMain(CreateInstanceFactory factory);
void waitUntilInitialized() const;
+ static status_t setSchedFifo(bool enabled);
/* ------------------------------------------------------------------------
* Threading
@@ -82,9 +84,10 @@
// Protects the creation and destruction of mThread.
mutable std::mutex mThreadMutex;
std::thread mThread GUARDED_BY(mThreadMutex);
- bool mRunning GUARDED_BY(mThreadMutex) = true;
- mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
- GUARDED_BY(mThreadMutex);
+ std::atomic<bool> mRunning = true;
+
+ using Work = std::function<void(renderengine::RenderEngine&)>;
+ mutable std::queue<Work> mFunctionCalls GUARDED_BY(mThreadMutex);
mutable std::condition_variable mCondition;
// Used to allow select thread safe methods to be accessed without requiring the
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 91d2d58..3f958ba 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -128,8 +128,9 @@
}
// Ensure that layerCount is valid.
- if (layerCount < 1)
+ if (layerCount < 1) {
layerCount = 1;
+ }
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
@@ -140,7 +141,7 @@
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
width, height, layerCount, format, usage, error);
- return NO_MEMORY;
+ return error;
}
if (!importBuffer) {
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index f576660..de36a7a 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -1453,7 +1453,9 @@
if (attribute == EGL_TIMESTAMPS_ANDROID) {
if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ // According to the spec, "if surface is not a window surface this has no
+ // effect."
+ return EGL_TRUE;
}
int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7e069c8..d2b8739 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3787,7 +3787,7 @@
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
- policyFlags |= POLICY_FLAG_FILTERED;
+ policyFlags |= POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
@@ -3893,12 +3893,6 @@
args->xCursorPosition, args->yCursorPosition,
args->downTime, args->pointerCount,
args->pointerProperties, args->pointerCoords, 0, 0);
- if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
- IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
- !mInputFilterEnabled) {
- const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
- mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
- }
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
@@ -4015,6 +4009,19 @@
policyFlags |= POLICY_FLAG_TRUSTED;
}
+ // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events
+ // that have gone through the InputFilter. If the event passed through the InputFilter,
+ // but did not get modified, assign the provided device id. If the InputFilter modifies the
+ // events in any way, it is responsible for removing this flag.
+ // If the injected event originated from accessibility, assign the accessibility device id,
+ // so that it can be distinguished from regular injected events.
+ int32_t resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
+ if (policyFlags & POLICY_FLAG_INPUTFILTER_TRUSTED) {
+ resolvedDeviceId = event->getDeviceId();
+ } else if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+ resolvedDeviceId = ACCESSIBILITY_DEVICE_ID;
+ }
+
std::queue<std::unique_ptr<EventEntry>> injectedEntries;
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -4027,10 +4034,10 @@
int32_t flags = incomingKey.getFlags();
int32_t keyCode = incomingKey.getKeyCode();
int32_t metaState = incomingKey.getMetaState();
- accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action,
+ accelerateMetaShortcuts(resolvedDeviceId, action,
/*byref*/ keyCode, /*byref*/ metaState);
KeyEvent keyEvent;
- keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+ keyEvent.initialize(incomingKey.getId(), resolvedDeviceId, incomingKey.getSource(),
incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
incomingKey.getDownTime(), incomingKey.getEventTime());
@@ -4051,7 +4058,7 @@
mLock.lock();
std::unique_ptr<KeyEntry> injectedEntry =
std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
- VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+ resolvedDeviceId, incomingKey.getSource(),
incomingKey.getDisplayId(), policyFlags, action,
flags, keyCode, incomingKey.getScanCode(), metaState,
incomingKey.getRepeatCount(),
@@ -4061,18 +4068,18 @@
}
case AINPUT_EVENT_TYPE_MOTION: {
- const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
- int32_t action = motionEvent->getAction();
- size_t pointerCount = motionEvent->getPointerCount();
- const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
- int32_t actionButton = motionEvent->getActionButton();
- int32_t displayId = motionEvent->getDisplayId();
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ int32_t action = motionEvent.getAction();
+ size_t pointerCount = motionEvent.getPointerCount();
+ const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
+ int32_t actionButton = motionEvent.getActionButton();
+ int32_t displayId = motionEvent.getDisplayId();
if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
return InputEventInjectionResult::FAILED;
}
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
- nsecs_t eventTime = motionEvent->getEventTime();
+ nsecs_t eventTime = motionEvent.getEventTime();
android::base::Timer t;
mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
@@ -4082,47 +4089,46 @@
}
mLock.lock();
- const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
- const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+ const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
+ const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
std::unique_ptr<MotionEntry> injectedEntry =
- std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
- VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags, action,
- actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(),
- motionEvent->getButtonState(),
- motionEvent->getClassification(),
- motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(),
- motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(),
- uint32_t(pointerCount), pointerProperties,
- samplePointerCoords, motionEvent->getXOffset(),
- motionEvent->getYOffset());
+ std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
+ resolvedDeviceId, motionEvent.getSource(),
+ motionEvent.getDisplayId(), policyFlags, action,
+ actionButton, motionEvent.getFlags(),
+ motionEvent.getMetaState(),
+ motionEvent.getButtonState(),
+ motionEvent.getClassification(),
+ motionEvent.getEdgeFlags(),
+ motionEvent.getXPrecision(),
+ motionEvent.getYPrecision(),
+ motionEvent.getRawXCursorPosition(),
+ motionEvent.getRawYCursorPosition(),
+ motionEvent.getDownTime(), uint32_t(pointerCount),
+ pointerProperties, samplePointerCoords,
+ motionEvent.getXOffset(),
+ motionEvent.getYOffset());
injectedEntries.push(std::move(injectedEntry));
- for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+ for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
samplePointerCoords += pointerCount;
std::unique_ptr<MotionEntry> nextInjectedEntry =
- std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
- VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags,
- action, actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(),
- motionEvent->getButtonState(),
- motionEvent->getClassification(),
- motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(),
- motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(),
+ std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
+ resolvedDeviceId, motionEvent.getSource(),
+ motionEvent.getDisplayId(), policyFlags,
+ action, actionButton, motionEvent.getFlags(),
+ motionEvent.getMetaState(),
+ motionEvent.getButtonState(),
+ motionEvent.getClassification(),
+ motionEvent.getEdgeFlags(),
+ motionEvent.getXPrecision(),
+ motionEvent.getYPrecision(),
+ motionEvent.getRawXCursorPosition(),
+ motionEvent.getRawYCursorPosition(),
+ motionEvent.getDownTime(),
uint32_t(pointerCount), pointerProperties,
- samplePointerCoords,
- motionEvent->getXOffset(),
- motionEvent->getYOffset());
+ samplePointerCoords, motionEvent.getXOffset(),
+ motionEvent.getYOffset());
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 19abfd9..7fdbbfd 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -113,9 +113,9 @@
/* Get battery status of a particular input device. */
virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
- virtual std::vector<int32_t> getLightIds(int32_t deviceId) = 0;
+ virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
- virtual const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) = 0;
+ virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
/* Return true if the device can send input events to the specified display. */
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e3e6c12..b19b419 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -67,6 +67,8 @@
// v4l2 devices go directly into /dev
static const char* VIDEO_DEVICE_PATH = "/dev";
+static constexpr size_t OBFUSCATED_LENGTH = 8;
+
static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
@@ -1828,9 +1830,21 @@
void EventHub::reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
Flags<InputDeviceClass> classes) {
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, reinterpret_cast<const uint8_t*>(identifier.uniqueId.c_str()),
+ identifier.uniqueId.size());
+ std::array<uint8_t, SHA256_DIGEST_LENGTH> digest;
+ SHA256_Final(digest.data(), &ctx);
+
+ std::string obfuscatedId;
+ for (size_t i = 0; i < OBFUSCATED_LENGTH; i++) {
+ obfuscatedId += StringPrintf("%02x", digest[i]);
+ }
+
android::util::stats_write(android::util::INPUTDEVICE_REGISTERED, identifier.name.c_str(),
identifier.vendor, identifier.product, identifier.version,
- identifier.bus, identifier.uniqueId.c_str(), classes.get());
+ identifier.bus, obfuscatedId.c_str(), classes.get());
}
void EventHub::openDeviceLocked(const std::string& devicePath) {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ad503fd..7af014c 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -89,8 +89,7 @@
}
void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
- InputDeviceInfo deviceInfo;
- getDeviceInfo(&deviceInfo);
+ InputDeviceInfo deviceInfo = getDeviceInfo();
dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
deviceInfo.getDisplayName().c_str());
@@ -417,15 +416,17 @@
for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
}
-void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
- outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
- mHasMic);
+InputDeviceInfo InputDevice::getDeviceInfo() {
+ InputDeviceInfo outDeviceInfo;
+ outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
+ mHasMic);
for_each_mapper(
- [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
+ [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); });
if (mController) {
- mController->populateDeviceInfo(outDeviceInfo);
+ mController->populateDeviceInfo(&outDeviceInfo);
}
+ return outDeviceInfo;
}
int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index e91f84e..10c04f6 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -406,9 +406,7 @@
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS) && !device->isIgnored()) {
- InputDeviceInfo info;
- device->getDeviceInfo(&info);
- outDevices.push_back(info);
+ outDevices.push_back(device->getDeviceInfo());
}
}
}
@@ -498,9 +496,7 @@
for (const auto& [device, eventHubIds] : mDeviceToEventHubIdsMap) {
if (!device->isIgnored()) {
- InputDeviceInfo info;
- device->getDeviceInfo(&info);
- outInputDevices.push_back(info);
+ outInputDevices.push_back(device->getDeviceInfo());
}
}
return outInputDevices;
@@ -695,28 +691,26 @@
return std::nullopt;
}
-std::vector<int32_t> InputReader::getLightIds(int32_t deviceId) {
+std::vector<InputDeviceLightInfo> InputReader::getLights(int32_t deviceId) {
std::scoped_lock _l(mLock);
InputDevice* device = findInputDeviceLocked(deviceId);
- if (device) {
- InputDeviceInfo info;
- device->getDeviceInfo(&info);
- return info.getLightIds();
+ if (device == nullptr) {
+ return {};
}
- return {};
+
+ return device->getDeviceInfo().getLights();
}
-const InputDeviceLightInfo* InputReader::getLightInfo(int32_t deviceId, int32_t lightId) {
+std::vector<InputDeviceSensorInfo> InputReader::getSensors(int32_t deviceId) {
std::scoped_lock _l(mLock);
InputDevice* device = findInputDeviceLocked(deviceId);
- if (device) {
- InputDeviceInfo info;
- device->getDeviceInfo(&info);
- return info.getLightInfo(lightId);
+ if (device == nullptr) {
+ return {};
}
- return nullptr;
+
+ return device->getDeviceInfo().getSensors();
}
bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) {
diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
index c075078..c7c8e28 100644
--- a/services/inputflinger/reader/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -169,8 +169,9 @@
mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
std::make_move_iterator(frames.end()));
if (mFrames.size() > MAX_QUEUE_SIZE) {
- ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
- mFrames.size() - MAX_QUEUE_SIZE);
+ // A user-space grip suppression process may be processing the video frames, and holding
+ // back the input events. This could result in video frames being produced without the
+ // matching input events. Drop the oldest frame here to prepare for the next input event.
mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
}
return numFrames;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 291f105..2f2eba7 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -80,7 +80,7 @@
void timeoutExpired(nsecs_t when);
void updateExternalStylusState(const StylusState& state);
- void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
+ InputDeviceInfo getDeviceInfo();
int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index bc79ccf..a00c5af 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -99,9 +99,9 @@
std::optional<int32_t> getBatteryStatus(int32_t deviceId) override;
- std::vector<int32_t> getLightIds(int32_t deviceId) override;
+ std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) override;
- const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) override;
+ std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override;
bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override;
@@ -130,24 +130,24 @@
// lock is already held by the input loop
void updateGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
int32_t getGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
- void disableVirtualKeysUntil(nsecs_t time) NO_THREAD_SAFETY_ANALYSIS override;
- bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode,
- int32_t scanCode) NO_THREAD_SAFETY_ANALYSIS override;
- void fadePointer() NO_THREAD_SAFETY_ANALYSIS override;
+ void disableVirtualKeysUntil(nsecs_t time) REQUIRES(mReader->mLock) override;
+ bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode)
+ REQUIRES(mReader->mLock) override;
+ void fadePointer() REQUIRES(mReader->mLock) override;
std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId)
- NO_THREAD_SAFETY_ANALYSIS override;
- void requestTimeoutAtTime(nsecs_t when) NO_THREAD_SAFETY_ANALYSIS override;
+ REQUIRES(mReader->mLock) override;
+ void requestTimeoutAtTime(nsecs_t when) REQUIRES(mReader->mLock) override;
int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override;
void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices)
- NO_THREAD_SAFETY_ANALYSIS override;
+ REQUIRES(mReader->mLock) override;
void dispatchExternalStylusState(const StylusState& outState)
- NO_THREAD_SAFETY_ANALYSIS override;
- InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override;
- InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override;
- EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override;
+ REQUIRES(mReader->mLock) override;
+ InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override;
+ InputListenerInterface* getListener() REQUIRES(mReader->mLock) override;
+ EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override;
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
- void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override;
- int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override;
+ void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
+ int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
} mContext;
friend class ContextImpl;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 93aa6ac..d51acce 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -473,6 +473,7 @@
const sp<InputWindowHandle>& focusedWindow = nullptr) {
FocusRequest request;
request.token = window->getToken();
+ request.windowName = window->getName();
if (focusedWindow) {
request.focusedToken = focusedWindow->getToken();
}
@@ -1085,6 +1086,20 @@
return mInputReceiver->consume();
}
+ MotionEvent* consumeMotion() {
+ InputEvent* event = consume();
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed : no event";
+ return nullptr;
+ }
+ if (event->getType() != AINPUT_EVENT_TYPE_MOTION) {
+ ADD_FAILURE() << "Instead of motion event, got "
+ << inputEventTypeToString(event->getType());
+ return nullptr;
+ }
+ return static_cast<MotionEvent*>(event);
+ }
+
void assertNoEvents() {
if (mInputReceiver == nullptr &&
mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
@@ -2446,13 +2461,10 @@
generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&motionArgs);
- InputEvent* event = window->consume();
+ MotionEvent* event = window->consumeMotion();
ASSERT_NE(event, nullptr);
- ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
- << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
- << " event, got " << inputEventTypeToString(event->getType()) << " event";
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ const MotionEvent& motionEvent = *event;
EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction());
EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
@@ -3118,6 +3130,70 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+class InputFilterInjectionPolicyTest : public InputDispatcherTest {
+protected:
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ /**
+ * We don't need to enable input filter to test the injected event policy, but we enabled it
+ * here to make the tests more realistic, since this policy only matters when inputfilter is
+ * on.
+ */
+ mDispatcher->setInputFilterEnabled(true);
+
+ std::shared_ptr<InputApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+ mWindow =
+ new FakeWindowHandle(application, mDispatcher, "Test Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mWindow->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ setFocusedWindow(mWindow);
+ mWindow->consumeFocusEvent(true);
+ }
+
+ void testInjectedKey(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId) {
+ KeyEvent event;
+
+ const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ event.initialize(InputEvent::nextId(), injectedDeviceId, AINPUT_SOURCE_KEYBOARD,
+ ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A,
+ KEY_A, AMETA_NONE, 0 /*repeatCount*/, eventTime, eventTime);
+ const int32_t additionalPolicyFlags =
+ POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+ policyFlags | additionalPolicyFlags));
+
+ InputEvent* received = mWindow->consume();
+ ASSERT_NE(nullptr, received);
+ ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
+ }
+
+private:
+ sp<FakeWindowHandle> mWindow;
+};
+
+TEST_F(InputFilterInjectionPolicyTest, TrustedFilteredEvents_KeepOriginalDeviceId) {
+ // We don't need POLICY_FLAG_FILTERED here, but it will be set in practice, so keep it to make
+ // the test more closely resemble the real usage
+ testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED, 3 /*injectedDeviceId*/,
+ 3 /*resolvedDeviceId*/);
+}
+
+TEST_F(InputFilterInjectionPolicyTest, EventsInjectedFromAccessibility_HaveAccessibilityDeviceId) {
+ testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+ 3 /*injectedDeviceId*/, ACCESSIBILITY_DEVICE_ID /*resolvedDeviceId*/);
+}
+
+TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDeviceId) {
+ testInjectedKey(0 /*policyFlags*/, 3 /*injectedDeviceId*/,
+ VIRTUAL_KEYBOARD_ID /*resolvedDeviceId*/);
+}
+
class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 7a11ca7..73198bc 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1524,20 +1524,6 @@
}
};
-TEST_F(InputReaderTest, ReaderGetInputDevices) {
- ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
- ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
- nullptr)); // no classes so device will be ignored
-
- const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
- ASSERT_EQ(1U, inputDevices.size());
- ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
- ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
- ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
-}
-
TEST_F(InputReaderTest, PolicyGetInputDevices) {
ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
@@ -1550,7 +1536,7 @@
ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
- ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
+ ASSERT_EQ(0U, inputDevices[0].getMotionRanges().size());
}
TEST_F(InputReaderTest, GetMergedInputDevices) {
@@ -1571,7 +1557,7 @@
addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
// Two devices will be merged to one input device as they have same identifier
- ASSERT_EQ(1U, mReader->getInputDevices().size());
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
}
TEST_F(InputReaderTest, GetMergedInputDevicesEnabled) {
@@ -2189,7 +2175,7 @@
ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
// Find the test device by its name.
- const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
+ const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
const auto& it =
std::find_if(inputDevices.begin(), inputDevices.end(),
[&keyboard](const InputDeviceInfo& info) {
@@ -2463,8 +2449,7 @@
ASSERT_TRUE(mDevice->isIgnored());
ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mDevice->getSources());
- InputDeviceInfo info;
- mDevice->getDeviceInfo(&info);
+ InputDeviceInfo info = mDevice->getDeviceInfo();
ASSERT_EQ(DEVICE_ID, info.getId());
ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType());
@@ -2533,8 +2518,7 @@
ASSERT_FALSE(mDevice->isIgnored());
ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), mDevice->getSources());
- InputDeviceInfo info;
- mDevice->getDeviceInfo(&info);
+ InputDeviceInfo info = mDevice->getDeviceInfo();
ASSERT_EQ(DEVICE_ID, info.getId());
ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType());
@@ -8444,8 +8428,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
- InputDeviceInfo deviceInfo;
- mDevice->getDeviceInfo(&deviceInfo);
+ InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
const InputDeviceInfo::MotionRange* relRangeX =
deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
@@ -8755,12 +8738,12 @@
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
InputDeviceInfo info;
controller.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::MONO, info.getLightInfo(ids[0])->type);
+ std::vector<InputDeviceLightInfo> lights = info.getLights();
+ ASSERT_EQ(1U, lights.size());
+ ASSERT_EQ(InputDeviceLightType::MONO, lights[0].type);
- ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS));
- ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
+ ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
+ ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
}
TEST_F(LightControllerTest, RGBLight) {
@@ -8786,12 +8769,12 @@
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
InputDeviceInfo info;
controller.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
+ std::vector<InputDeviceLightInfo> lights = info.getLights();
+ ASSERT_EQ(1U, lights.size());
+ ASSERT_EQ(InputDeviceLightType::RGB, lights[0].type);
- ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
- ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+ ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+ ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
}
TEST_F(LightControllerTest, MultiColorRGBLight) {
@@ -8808,12 +8791,12 @@
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
InputDeviceInfo info;
controller.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
+ std::vector<InputDeviceLightInfo> lights = info.getLights();
+ ASSERT_EQ(1U, lights.size());
+ ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, lights[0].type);
- ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
- ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+ ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+ ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
}
TEST_F(LightControllerTest, PlayerIdLight) {
@@ -8845,13 +8828,13 @@
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
InputDeviceInfo info;
controller.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
+ std::vector<InputDeviceLightInfo> lights = info.getLights();
+ ASSERT_EQ(1U, lights.size());
+ ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
- ASSERT_FALSE(controller.setLightColor(ids[0], LIGHT_COLOR));
- ASSERT_TRUE(controller.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
- ASSERT_EQ(controller.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
+ ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+ ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID));
+ ASSERT_EQ(controller.getLightPlayerId(lights[0].id).value_or(-1), LIGHT_PLAYER_ID);
}
} // namespace android
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 73a6db5..560834f 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -17,6 +17,7 @@
#include "SensorInterface.h"
#include "SensorDevice.h"
#include "SensorFusion.h"
+#include "SensorService.h"
#include <stdint.h>
#include <sys/types.h>
@@ -85,4 +86,35 @@
}
// ---------------------------------------------------------------------------
+
+ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service)
+ : HardwareSensor(sensor), mSensorService(service) {
+}
+
+status_t ProximitySensor::activate(void* ident, bool enabled) {
+ bool wasActive = mActive;
+ status_t status = HardwareSensor::activate(ident, enabled);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ mActive = enabled;
+ if (wasActive != enabled) {
+ mSensorService.onProximityActiveLocked(enabled);
+ }
+ return NO_ERROR;
+}
+
+void ProximitySensor::willDisableAllSensors() {
+ if (mSensorDevice.isSensorActive(mSensor.getHandle())) {
+ mSensorService.onProximityActiveLocked(false);
+ }
+}
+
+void ProximitySensor::didEnableAllSensors() {
+ if (mSensorDevice.isSensorActive(mSensor.getHandle())) {
+ mSensorService.onProximityActiveLocked(true);
+ }
+}
+
+// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index b5375cb..ea181c9 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -26,6 +26,7 @@
// ---------------------------------------------------------------------------
class SensorDevice;
class SensorFusion;
+class SensorService;
class SensorInterface : public VirtualLightRefBase {
public:
@@ -43,6 +44,9 @@
virtual const Sensor& getSensor() const = 0;
virtual bool isVirtual() const = 0;
virtual void autoDisable(void* /*ident*/, int /*handle*/) = 0;
+
+ virtual void willDisableAllSensors() = 0;
+ virtual void didEnableAllSensors() = 0;
};
class BaseSensor : public SensorInterface {
@@ -65,6 +69,9 @@
virtual const Sensor& getSensor() const override { return mSensor; }
virtual void autoDisable(void* /*ident*/, int /*handle*/) override { }
+
+ virtual void willDisableAllSensors() override { }
+ virtual void didEnableAllSensors() override { }
protected:
SensorDevice& mSensorDevice;
Sensor mSensor;
@@ -100,6 +107,20 @@
SensorFusion& mSensorFusion;
};
+// ---------------------------------------------------------------------------
+
+class ProximitySensor : public HardwareSensor {
+public:
+ explicit ProximitySensor(const sensor_t& sensor, SensorService& service);
+
+ status_t activate(void* ident, bool enabled) override;
+
+ void willDisableAllSensors() override;
+ void didEnableAllSensors() override;
+private:
+ SensorService& mSensorService;
+ bool mActive;
+};
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 617ceef..049ae7c 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -36,6 +36,15 @@
class SensorList : public Dumpable {
public:
+ struct Entry {
+ sp<SensorInterface> si;
+ const bool isForDebug;
+ const bool isVirtual;
+ Entry(SensorInterface* si_, bool debug_, bool virtual_) :
+ si(si_), isForDebug(debug_), isVirtual(virtual_) {
+ }
+ };
+
// After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
// object it pointed to and the object should not be released elsewhere.
bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false);
@@ -69,25 +78,6 @@
template <typename TF>
void forEachSensor(const TF& f) const;
- const Sensor& getNonSensor() const { return mNonSensor;}
-
- // Dumpable interface
- virtual std::string dump() const override;
- virtual void dump(util::ProtoOutputStream* proto) const override;
-
- virtual ~SensorList();
-private:
- struct Entry {
- sp<SensorInterface> si;
- const bool isForDebug;
- const bool isVirtual;
- Entry(SensorInterface* si_, bool debug_, bool virtual_) :
- si(si_), isForDebug(debug_), isVirtual(virtual_) {
- }
- };
-
- const static Sensor mNonSensor; //.getName() == "unknown",
-
// Iterate through Entry in sensor list and perform operation f on each Entry.
//
// TF is a function with the signature:
@@ -99,6 +89,16 @@
template <typename TF>
void forEachEntry(const TF& f) const;
+ const Sensor& getNonSensor() const { return mNonSensor;}
+
+ // Dumpable interface
+ virtual std::string dump() const override;
+ virtual void dump(util::ProtoOutputStream* proto) const override;
+
+ virtual ~SensorList();
+private:
+ const static Sensor mNonSensor; //.getName() == "unknown",
+
template <typename T, typename TF>
T getOne(int handle, const TF& accessor, T def = T()) const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index f949196..2281721 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -51,7 +51,6 @@
#include "SensorRecord.h"
#include "SensorRegistrationInfo.h"
-#include <ctime>
#include <inttypes.h>
#include <math.h>
#include <sched.h>
@@ -61,8 +60,13 @@
#include <sys/types.h>
#include <unistd.h>
+#include <ctime>
+#include <future>
+
#include <private/android_filesystem_config.h>
+using namespace std::chrono_literals;
+
namespace android {
// ---------------------------------------------------------------------------
@@ -83,6 +87,8 @@
String16 SensorService::sSensorInterfaceDescriptorPrefix =
String16("android.frameworks.sensorservice@");
AppOpsManager SensorService::sAppOpsManager;
+std::atomic_uint64_t SensorService::curProxCallbackSeq(0);
+std::atomic_uint64_t SensorService::completedCallbackSeq(0);
#define SENSOR_SERVICE_DIR "/data/system/sensor_service"
#define SENSOR_SERVICE_HMAC_KEY_FILE SENSOR_SERVICE_DIR "/hmac_key"
@@ -97,7 +103,7 @@
SensorService::SensorService()
: mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
- mWakeLockAcquired(false) {
+ mWakeLockAcquired(false), mProximityActiveCount(0) {
mUidPolicy = new UidPolicy(this);
mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
}
@@ -168,7 +174,7 @@
(1<<SENSOR_TYPE_GAME_ROTATION_VECTOR);
for (ssize_t i=0 ; i<count ; i++) {
- bool useThisSensor=true;
+ bool useThisSensor = true;
switch (list[i].type) {
case SENSOR_TYPE_ACCELEROMETER:
@@ -197,7 +203,11 @@
break;
}
if (useThisSensor) {
- registerSensor( new HardwareSensor(list[i]) );
+ if (list[i].type == SENSOR_TYPE_PROXIMITY) {
+ registerSensor(new ProximitySensor(list[i], *this));
+ } else {
+ registerSensor( new HardwareSensor(list[i]) );
+ }
}
}
@@ -670,6 +680,10 @@
bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName());
conn->onSensorAccessChanged(hasAccess);
}
+ mSensors.forEachEntry([](const SensorServiceUtil::SensorList::Entry& e) {
+ e.si->willDisableAllSensors();
+ return true;
+ });
dev.disableAllSensors();
// Clear all pending flush connections for all active sensors. If one of the active
// connections has called flush() and the underlying sensor has been disabled before a
@@ -695,6 +709,10 @@
}
SensorDevice& dev(SensorDevice::getInstance());
dev.enableAllSensors();
+ mSensors.forEachEntry([](const SensorServiceUtil::SensorList::Entry& e) {
+ e.si->didEnableAllSensors();
+ return true;
+ });
for (const sp<SensorDirectConnection>& conn : connLock->getDirectConnections()) {
bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName());
conn->onSensorAccessChanged(hasAccess);
@@ -1520,6 +1538,10 @@
if (err == NO_ERROR) {
mCurrentOperatingMode = NORMAL;
dev.enableAllSensors();
+ mSensors.forEachEntry([](const SensorServiceUtil::SensorList::Entry& e) {
+ e.si->didEnableAllSensors();
+ return true;
+ });
}
return err;
}
@@ -1584,6 +1606,78 @@
mConnectionHolder.removeDirectConnection(c);
}
+void SensorService::onProximityActiveLocked(bool isActive) {
+ int prevCount = mProximityActiveCount;
+ bool activeStateChanged = false;
+ if (isActive) {
+ mProximityActiveCount++;
+ activeStateChanged = prevCount == 0;
+ } else {
+ mProximityActiveCount--;
+ if (mProximityActiveCount < 0) {
+ ALOGE("Proximity active count is negative (%d)!", mProximityActiveCount);
+ }
+ activeStateChanged = prevCount > 0 && mProximityActiveCount <= 0;
+ }
+
+ if (activeStateChanged) {
+ notifyProximityStateLocked(mProximityActiveListeners);
+ }
+}
+
+void SensorService::notifyProximityStateLocked(
+ const std::vector<sp<ProximityActiveListener>>& listeners) {
+ const bool isActive = mProximityActiveCount > 0;
+ const uint64_t mySeq = ++curProxCallbackSeq;
+ std::thread t([isActive, mySeq, listenersCopy = listeners]() {
+ while (completedCallbackSeq.load() != mySeq - 1)
+ std::this_thread::sleep_for(1ms);
+ for (auto& listener : listenersCopy)
+ listener->onProximityActive(isActive);
+ completedCallbackSeq++;
+ });
+ t.detach();
+}
+
+status_t SensorService::addProximityActiveListener(const sp<ProximityActiveListener>& callback) {
+ if (callback == nullptr) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ // Check if the callback was already added.
+ for (const auto& cb : mProximityActiveListeners) {
+ if (cb == callback) {
+ return ALREADY_EXISTS;
+ }
+ }
+
+ mProximityActiveListeners.push_back(callback);
+ std::vector<sp<ProximityActiveListener>> listener(1, callback);
+ notifyProximityStateLocked(listener);
+ return OK;
+}
+
+status_t SensorService::removeProximityActiveListener(
+ const sp<ProximityActiveListener>& callback) {
+ if (callback == nullptr) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ for (auto iter = mProximityActiveListeners.begin();
+ iter != mProximityActiveListeners.end();
+ ++iter) {
+ if (*iter == callback) {
+ mProximityActiveListeners.erase(iter);
+ return OK;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
return mSensors.getInterface(handle);
}
@@ -2076,6 +2170,13 @@
return true;
}
+/**
+ * Checks if a sensor should be capped according to HIGH_SAMPLING_RATE_SENSORS
+ * permission.
+ *
+ * This needs to be kept in sync with the list defined on the Java side
+ * in frameworks/base/core/java/android/hardware/SystemSensorManager.java
+ */
bool SensorService::isSensorInCappedSet(int sensorType) {
return (sensorType == SENSOR_TYPE_ACCELEROMETER
|| sensorType == SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index a563a60..def6611 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -89,9 +89,23 @@
UID_STATE_IDLE,
};
+ class ProximityActiveListener : public virtual RefBase {
+ public:
+ // Note that the callback is invoked from an async thread and can interact with the
+ // SensorService directly.
+ virtual void onProximityActive(bool isActive) = 0;
+ };
+
+ static char const* getServiceName() ANDROID_API { return "sensorservice"; }
+ SensorService() ANDROID_API;
+
void cleanupConnection(SensorEventConnection* connection);
void cleanupConnection(SensorDirectConnection* c);
+ // Call with mLock held.
+ void onProximityActiveLocked(bool isActive);
+ void notifyProximityStateLocked(const std::vector<sp<ProximityActiveListener>>& listeners);
+
status_t enable(const sp<SensorEventConnection>& connection, int handle,
nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
const String16& opPackageName);
@@ -104,6 +118,9 @@
status_t flushSensor(const sp<SensorEventConnection>& connection,
const String16& opPackageName);
+ status_t addProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
+ status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
+
// Returns true if a sensor should be throttled according to our rate-throttling rules.
static bool isSensorInCappedSet(int sensorType);
@@ -305,8 +322,6 @@
};
static const char* WAKE_LOCK_NAME;
- static char const* getServiceName() ANDROID_API { return "sensorservice"; }
- SensorService() ANDROID_API;
virtual ~SensorService();
virtual void onFirstRef();
@@ -326,6 +341,7 @@
virtual int setOperationParameter(
int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
virtual status_t dump(int fd, const Vector<String16>& args);
+
status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
String8 getSensorName(int handle) const;
String8 getSensorStringType(int handle) const;
@@ -433,6 +449,9 @@
static uint8_t sHmacGlobalKey[128];
static bool sHmacGlobalKeyIsValid;
+ static std::atomic_uint64_t curProxCallbackSeq;
+ static std::atomic_uint64_t completedCallbackSeq;
+
SensorServiceUtil::SensorList mSensors;
status_t mInitCheck;
@@ -476,6 +495,10 @@
std::map<userid_t, sp<SensorPrivacyPolicy>> mMicSensorPrivacyPolicies;
// Checks if the mic sensor privacy is enabled for the uid
bool isMicSensorPrivacyEnabledForUid(uid_t uid);
+
+ // Counts how many proximity sensors are currently active.
+ int mProximityActiveCount;
+ std::vector<sp<ProximityActiveListener>> mProximityActiveListeners;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index cacad52..23779be 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -425,7 +425,8 @@
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
refreshRate, renderRate,
frameRateToSetFrameRateVotePayload(
- mDrawingState.frameRate));
+ mDrawingState.frameRate),
+ getGameMode());
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
@@ -439,7 +440,8 @@
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
refreshRate, renderRate,
frameRateToSetFrameRateVotePayload(
- mDrawingState.frameRate));
+ mDrawingState.frameRate),
+ getGameMode());
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 54daa10..d68a0e0 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -43,11 +43,13 @@
using PresentState = frametimeline::SurfaceFrame::PresentState;
namespace {
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
- const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) {
+ const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence,
+ uint32_t transformHint) {
if (!listener) {
return;
}
- listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE);
+ listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE,
+ transformHint);
}
} // namespace
@@ -72,7 +74,8 @@
// issue with the clone layer trying to use the texture.
if (mBufferInfo.mBuffer != nullptr && !isClone()) {
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
- mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence);
+ mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence,
+ mTransformHint);
}
}
@@ -427,7 +430,8 @@
// call any release buffer callbacks if set.
callReleaseBufferCallback(mCurrentState.releaseBufferListener,
mCurrentState.buffer->getBuffer(),
- mCurrentState.acquireFence);
+ mCurrentState.acquireFence,
+ mTransformHint);
decrementPendingBufferCount();
if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
addSurfaceFrameDroppedForBuffer(mCurrentState.bufferSurfaceFrameTX);
@@ -444,7 +448,7 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
- mOwnerUid, postTime);
+ mOwnerUid, postTime, getGameMode());
mCurrentState.desiredPresentTime = desiredPresentTime;
mCurrentState.isAutoTimestamp = isAutoTimestamp;
@@ -946,7 +950,8 @@
// then we will drop a buffer and should decrement the pending buffer count and
// call any release buffer callbacks if set.
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
- mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence);
+ mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence,
+ mTransformHint);
decrementPendingBufferCount();
}
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 08147ed..d738ccd 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -57,6 +57,7 @@
"src/planner/LayerState.cpp",
"src/planner/Planner.cpp",
"src/planner/Predictor.cpp",
+ "src/planner/TexturePool.cpp",
"src/ClientCompositionRequestCache.cpp",
"src/CompositionEngine.cpp",
"src/Display.cpp",
@@ -107,6 +108,7 @@
"tests/planner/FlattenerTest.cpp",
"tests/planner/LayerStateTest.cpp",
"tests/planner/PredictorTest.cpp",
+ "tests/planner/TexturePoolTest.cpp",
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 289cb11..29937fb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -82,6 +82,9 @@
// The earliest time to send the present command to the HAL
std::chrono::steady_clock::time_point earliestPresentTime;
+
+ // The predicted next invalidation time
+ std::optional<std::chrono::steady_clock::time_point> nextInvalidateTime;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 633668e..14eddb1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -21,13 +21,10 @@
#include <string>
#include <ui/DisplayId.h>
-#include <ui/PixelFormat.h>
#include <ui/Size.h>
#include <ui/StaticDisplayInfo.h>
-#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/PowerAdvisor.h"
-#include "DisplayIdGenerator.h"
namespace android::compositionengine {
@@ -37,24 +34,14 @@
* A parameter object for creating Display instances
*/
struct DisplayCreationArgs {
- struct Physical {
- DisplayId id;
- ui::DisplayConnectionType type;
- };
+ DisplayId id;
- // Required for physical displays. Gives the HWC display id for the existing
- // display along with the connection type.
- std::optional<Physical> physical;
+ // Unset for virtual displays
+ std::optional<ui::DisplayConnectionType> connectionType;
// Size of the display in pixels
ui::Size pixels = ui::Size::INVALID;
- // Pixel format of the display
- ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
-
- // True if virtual displays should be created with the HWC API if possible
- bool useHwcVirtualDisplays = false;
-
// True if this display should be considered secure
bool isSecure = false;
@@ -67,9 +54,6 @@
// Debugging. Human readable name for the display.
std::string name;
-
- // Generator for IDs of virtual displays, which are backed by the GPU.
- DisplayIdGenerator<GpuVirtualDisplayId>* gpuVirtualDisplayIdGenerator;
};
/**
@@ -80,8 +64,13 @@
public:
DisplayCreationArgs build() { return std::move(mArgs); }
- DisplayCreationArgsBuilder& setPhysical(DisplayCreationArgs::Physical physical) {
- mArgs.physical = physical;
+ DisplayCreationArgsBuilder& setId(DisplayId id) {
+ mArgs.id = id;
+ return *this;
+ }
+
+ DisplayCreationArgsBuilder& setConnectionType(ui::DisplayConnectionType connectionType) {
+ mArgs.connectionType = connectionType;
return *this;
}
@@ -90,22 +79,6 @@
return *this;
}
- DisplayCreationArgsBuilder& setPixelFormat(ui::PixelFormat pixelFormat) {
- mArgs.pixelFormat = pixelFormat;
- return *this;
- }
-
- DisplayCreationArgsBuilder& setUseHwcVirtualDisplays(bool useHwcVirtualDisplays) {
- mArgs.useHwcVirtualDisplays = useHwcVirtualDisplays;
- return *this;
- }
-
- DisplayCreationArgsBuilder& setGpuVirtualDisplayIdGenerator(
- DisplayIdGenerator<GpuVirtualDisplayId>& generator) {
- mArgs.gpuVirtualDisplayIdGenerator = &generator;
- return *this;
- }
-
DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
mArgs.isSecure = isSecure;
return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 791e7db..e51019a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -78,6 +78,30 @@
virtual void prepareCompositionState(StateSubset) = 0;
struct ClientCompositionTargetSettings {
+ enum class BlurSetting {
+ Disabled,
+ BackgroundBlurOnly,
+ BlurRegionsOnly,
+ Enabled,
+ };
+
+ friend std::string toString(BlurSetting blurSetting) {
+ switch (blurSetting) {
+ case BlurSetting::Enabled:
+ return "Enabled";
+ case BlurSetting::BlurRegionsOnly:
+ return "BlurRegionsOnly";
+ case BlurSetting::BackgroundBlurOnly:
+ return "BackgroundBlurOnly";
+ case BlurSetting::Disabled:
+ return "Disabled";
+ }
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const BlurSetting& setting) {
+ return os << toString(setting);
+ }
+
// The clip region, or visible region that is being rendered to
const Region& clip;
@@ -110,8 +134,8 @@
// This may be requested by the HWC
const bool clearContent;
- // If set to true, change the layer settings to not use any blurs.
- const bool disableBlurs;
+ // Configure layer settings for using blurs
+ BlurSetting blurSetting;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
@@ -186,6 +210,7 @@
PrintTo(settings.dataspace, os);
*os << "\n .realContentIsVisible = " << settings.realContentIsVisible;
*os << "\n .clearContent = " << settings.clearContent;
+ *os << "\n .blurSetting = " << settings.blurSetting;
*os << "\n}";
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 257974f..1416b1e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -286,7 +286,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 renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual void chooseCompositionStrategy() = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
index a8d372c..4110346 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -24,21 +24,17 @@
struct ANativeWindow;
-namespace android {
-
-namespace compositionengine {
-
-class Display;
+namespace android::compositionengine {
/**
* A parameter object for creating RenderSurface instances
*/
struct RenderSurfaceCreationArgs {
// The initial width of the surface
- int32_t displayWidth;
+ int32_t displayWidth = -1;
// The initial height of the surface
- int32_t displayHeight;
+ int32_t displayHeight = -1;
// The ANativeWindow for the buffer queue for this surface
sp<ANativeWindow> nativeWindow;
@@ -46,22 +42,16 @@
// The DisplaySurface for this surface
sp<DisplaySurface> displaySurface;
- size_t maxTextureCacheSize;
+ // The maximum size of the renderengine::ExternalTexture cache
+ size_t maxTextureCacheSize = 0;
+
+private:
+ friend class RenderSurfaceCreationArgsBuilder;
+
+ // Not defaulted to disable aggregate initialization.
+ RenderSurfaceCreationArgs() {}
};
-/**
- * A helper for setting up a RenderSurfaceCreationArgs value in-line.
- * Prefer this builder over raw structure initialization.
- *
- * Instead of:
- *
- * RenderSurfaceCreationArgs{1000, 1000, nativeWindow, displaySurface}
- *
- * Prefer:
- *
- * RenderSurfaceCreationArgsBuilder().setDisplayWidth(1000).setDisplayHeight(1000)
- * .setNativeWindow(nativeWindow).setDisplaySurface(displaySurface).Build();
- */
class RenderSurfaceCreationArgsBuilder {
public:
RenderSurfaceCreationArgs build() { return std::move(mArgs); }
@@ -75,11 +65,11 @@
return *this;
}
RenderSurfaceCreationArgsBuilder& setNativeWindow(sp<ANativeWindow> nativeWindow) {
- mArgs.nativeWindow = nativeWindow;
+ mArgs.nativeWindow = std::move(nativeWindow);
return *this;
}
RenderSurfaceCreationArgsBuilder& setDisplaySurface(sp<DisplaySurface> displaySurface) {
- mArgs.displaySurface = displaySurface;
+ mArgs.displaySurface = std::move(displaySurface);
return *this;
}
@@ -92,5 +82,4 @@
RenderSurfaceCreationArgs mArgs;
};
-} // namespace compositionengine
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 54e91ae..bb540ea 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -80,19 +80,13 @@
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
- virtual std::optional<DisplayId> maybeAllocateDisplayIdForVirtualDisplay(ui::Size,
- ui::PixelFormat) const;
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
- // Testing
- void setDisplayIdForTesting(DisplayId displayId);
-
private:
bool mIsVirtual = false;
bool mIsDisconnected = false;
DisplayId mId;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
- DisplayIdGenerator<GpuVirtualDisplayId>* mGpuVirtualDisplayIdGenerator;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index f10ff25..f832084 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -93,7 +93,7 @@
std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
void postFramebuffer() override;
- void renderCachedSets() override;
+ void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
// Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 2ffd472..244f8ab 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -73,10 +73,11 @@
void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
- void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
+ bool skipLayer);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
- void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer);
void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
bool isPeekingThrough, bool skipLayer);
void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 3f670a1..7564c54 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -103,6 +103,13 @@
// be visible through it. Unowned - the OutputLayer's lifetime will
// outlast this.)
compositionengine::OutputLayer* peekThroughLayer = nullptr;
+ // True when this layer's blur has been cached with a previous layer, so that this layer
+ // does not need to request blurring.
+ // TODO(b/188816867): support blur regions too, which are less likely to be common if a
+ // device supports cross-window blurs. Blur region support should be doable, but we would
+ // need to make sure that layer caching works well with the blur region transform passed
+ // into RenderEngine
+ bool disableBackgroundBlur = false;
} overrideInfo;
/*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index fdcd6ab..7cb0f6b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -19,6 +19,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/TexturePool.h>
#include <renderengine/RenderEngine.h>
#include <chrono>
@@ -39,6 +40,7 @@
const LayerState* getState() const { return mState; }
const std::string& getName() const { return mState->getName(); }
+ int32_t getBackgroundBlurRadius() const { return mState->getBackgroundBlurRadius(); }
Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
const Region& getVisibleRegion() const { return mState->getVisibleRegion(); }
const sp<GraphicBuffer>& getBuffer() const {
@@ -63,9 +65,12 @@
size_t getLayerCount() const { return mLayers.size(); }
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
+ Rect getTextureBounds() const { return mOutputSpace.content; }
const Region& getVisibleRegion() const { return mVisibleRegion; }
size_t getAge() const { return mAge; }
- const std::shared_ptr<renderengine::ExternalTexture>& getBuffer() const { return mTexture; }
+ std::shared_ptr<renderengine::ExternalTexture> getBuffer() const {
+ return mTexture ? mTexture->get() : nullptr;
+ }
const sp<Fence>& getDrawFence() const { return mDrawFence; }
const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
@@ -88,9 +93,12 @@
void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
void append(const CachedSet& other) {
- mTexture = nullptr;
+ mTexture.reset();
mOutputDataspace = ui::Dataspace::UNKNOWN;
mDrawFence = nullptr;
+ mBlurLayer = nullptr;
+ mHolePunchLayer = nullptr;
+ mSkipCount = 0;
mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
Region boundingRegion;
@@ -100,9 +108,12 @@
mVisibleRegion.orSelf(other.mVisibleRegion);
}
void incrementAge() { ++mAge; }
+ void incrementSkipCount() { mSkipCount++; }
+ size_t getSkipCount() { return mSkipCount; }
// Renders the cached set with the supplied output composition state.
- void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
+ void render(renderengine::RenderEngine& re, TexturePool& texturePool,
+ const OutputCompositionState& outputState);
void dump(std::string& result) const;
@@ -123,9 +134,17 @@
// nothing (besides the hole punch layer) will be drawn behind it.
void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
+ void addBackgroundBlurLayer(const CachedSet&);
+
// Retrieve the layer that will be drawn behind this one.
compositionengine::OutputLayer* getHolePunchLayer() const;
+ compositionengine::OutputLayer* getBlurLayer() const;
+
+ bool hasHdrLayers() const;
+
+ bool hasProtectedLayers() const;
+
private:
CachedSet() = default;
@@ -135,11 +154,15 @@
// Unowned.
const LayerState* mHolePunchLayer = nullptr;
+ const LayerState* mBlurLayer = nullptr;
Rect mBounds = Rect::EMPTY_RECT;
Region mVisibleRegion;
size_t mAge = 0;
+ size_t mSkipCount = 0;
- std::shared_ptr<renderengine::ExternalTexture> mTexture;
+ // TODO(b/190411067): This is a shared pointer only because CachedSets are copied into different
+ // containers in the Flattener. Logically this should have unique ownership otherwise.
+ std::shared_ptr<TexturePool::AutoTexture> mTexture;
sp<Fence> mDrawFence;
ProjectionSpace mOutputSpace;
ui::Dataspace mOutputDataspace;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 213c55e..7534548 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -20,6 +20,7 @@
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>
+#include <chrono>
#include <numeric>
#include <vector>
@@ -37,20 +38,53 @@
class Flattener {
public:
- Flattener(bool enableHolePunch = false) : mEnableHolePunch(enableHolePunch) {}
+ struct CachedSetRenderSchedulingTunables {
+ // This default assumes that rendering a cached set takes about 3ms. That time is then cut
+ // in half - the next frame using the cached set would have the same workload, meaning that
+ // composition cost is the same. This is best illustrated with the following example:
+ //
+ // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
+ // renderCachedSets costs 3ms, then two consecutive frames have timings:
+ //
+ // First frame: Start at 0ms, end at 6.8ms.
+ // renderCachedSets: Start at 6.8ms, end at 9.8ms.
+ // Second frame: Start at 9.8ms, end at 16.6ms.
+ //
+ // Now the second frame won't render a cached set afterwards, but the first frame didn't
+ // really steal time from the second frame.
+ static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = 1500us;
- void setDisplaySize(ui::Size size) { mDisplaySize = size; }
+ static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;
+
+ // Duration allocated for rendering a cached set. If we don't have enough time for rendering
+ // a cached set, then rendering is deferred to another frame.
+ const std::chrono::nanoseconds cachedSetRenderDuration;
+ // Maximum of times that we defer rendering a cached set. If we defer rendering a cached set
+ // too many times, then render it anyways so that future frames would benefit from the
+ // flattened cached set.
+ const size_t maxDeferRenderAttempts;
+ };
+ Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false,
+ std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables =
+ std::nullopt);
+
+ void setDisplaySize(ui::Size size) {
+ mDisplaySize = size;
+ mTexturePool.setDisplaySize(size);
+ }
NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
std::chrono::steady_clock::time_point now);
// Renders the newest cached sets with the supplied output composition state
- void renderCachedSets(renderengine::RenderEngine& re,
- const OutputCompositionState& outputState);
+ void renderCachedSets(const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline);
void dump(std::string& result) const;
void dumpLayers(std::string& result) const;
+ const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
+
private:
size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
@@ -72,6 +106,7 @@
std::vector<CachedSet>::const_iterator mStart;
std::vector<size_t> mLengths;
const CachedSet* mHolePunchCandidate = nullptr;
+ const CachedSet* mBlurringLayer = nullptr;
public:
// Initializes a Builder a CachedSet to start from.
@@ -90,6 +125,10 @@
mHolePunchCandidate = holePunchCandidate;
}
+ void setBlurringLayer(const CachedSet* blurringLayer) {
+ mBlurringLayer = blurringLayer;
+ }
+
// Builds a Run instance, if a valid Run may be built.
std::optional<Run> validateAndBuild() {
if (mLengths.size() <= 1) {
@@ -99,7 +138,7 @@
return Run(mStart,
std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
[](size_t left, size_t right) { return left + right; }),
- mHolePunchCandidate);
+ mHolePunchCandidate, mBlurringLayer);
}
void reset() { *this = {}; }
@@ -112,14 +151,19 @@
size_t getLayerLength() const { return mLength; }
// Gets the hole punch candidate for this Run.
const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; }
+ const CachedSet* getBlurringLayer() const { return mBlurringLayer; }
private:
Run(std::vector<CachedSet>::const_iterator start, size_t length,
- const CachedSet* holePunchCandidate)
- : mStart(start), mLength(length), mHolePunchCandidate(holePunchCandidate) {}
+ const CachedSet* holePunchCandidate, const CachedSet* blurringLayer)
+ : mStart(start),
+ mLength(length),
+ mHolePunchCandidate(holePunchCandidate),
+ mBlurringLayer(blurringLayer) {}
const std::vector<CachedSet>::const_iterator mStart;
const size_t mLength;
const CachedSet* const mHolePunchCandidate;
+ const CachedSet* const mBlurringLayer;
friend class Builder;
};
@@ -130,15 +174,23 @@
void buildCachedSets(std::chrono::steady_clock::time_point now);
+ renderengine::RenderEngine& mRenderEngine;
const bool mEnableHolePunch;
+ const std::optional<CachedSetRenderSchedulingTunables> mCachedSetRenderSchedulingTunables;
+ TexturePool mTexturePool;
+
+protected:
+ // mNewCachedSet must be destroyed before mTexturePool is.
+ std::optional<CachedSet> mNewCachedSet;
+
+private:
ui::Size mDisplaySize;
NonBufferHash mCurrentGeometry;
std::chrono::steady_clock::time_point mLastGeometryUpdate;
std::vector<CachedSet> mLayers;
- std::optional<CachedSet> mNewCachedSet;
// Statistics
size_t mUnflattenedDisplayCost = 0;
@@ -148,6 +200,7 @@
size_t mCachedSetCreationCount = 0;
size_t mCachedSetCreationCost = 0;
std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
+ std::chrono::nanoseconds mActiveLayerTimeout = kActiveLayerTimeout;
static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index fef0dfb..a20d7b3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -231,6 +231,7 @@
bool hasBlurBehind() const {
return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
}
+ int32_t getBackgroundBlurRadius() const { return mBackgroundBlurRadius.get(); }
hardware::graphics::composer::hal::Composition getCompositionType() const {
return mCompositionType.get();
}
@@ -239,6 +240,19 @@
void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
int64_t getFramesSinceBufferUpdate() const { return mFramesSinceBufferUpdate; }
+ ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
+
+ bool isHdr() const {
+ const ui::Dataspace transfer =
+ static_cast<ui::Dataspace>(getDataspace() & ui::Dataspace::TRANSFER_MASK);
+ return (transfer == ui::Dataspace::TRANSFER_ST2084 ||
+ transfer == ui::Dataspace::TRANSFER_HLG);
+ }
+
+ bool isProtected() const {
+ return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
+ }
+
void dump(std::string& result) const;
std::optional<std::string> compare(const LayerState& other) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index 4365b93..be34153 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -41,7 +41,7 @@
// as a more efficient representation of parts of the layer stack.
class Planner {
public:
- Planner();
+ Planner(renderengine::RenderEngine& renderengine);
void setDisplaySize(ui::Size);
@@ -58,9 +58,11 @@
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& re,
- const OutputCompositionState& outputState);
+ // The planner will call to the Flattener to render any pending cached set.
+ // Rendering a pending cached set is optional: if the renderDeadline is not far enough in the
+ // future then the planner may opt to skip rendering the cached set.
+ void renderCachedSets(const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline);
void dump(const Vector<String16>& args, std::string&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
new file mode 100644
index 0000000..fb53ee0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -0,0 +1,102 @@
+/*
+ * 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/ProjectionSpace.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
+
+#include <renderengine/ExternalTexture.h>
+#include <chrono>
+#include "android-base/macros.h"
+
+namespace android::compositionengine::impl::planner {
+
+// A pool of textures that only manages textures of a single size.
+// While it is possible to define a texture pool supporting variable-sized textures to save on
+// memory, it is a simpler implementation to only manage screen-sized textures. The texture pool is
+// unbounded - there are a minimum number of textures preallocated. Under heavy system load, new
+// textures may be allocated, but only a maximum number of retained once those textures are no
+// longer necessary.
+class TexturePool {
+public:
+ // RAII class helping with managing textures from the texture pool
+ // Textures once they're no longer used should be returned to the pool instead of outright
+ // deleted.
+ class AutoTexture {
+ public:
+ AutoTexture(TexturePool& texturePool,
+ std::shared_ptr<renderengine::ExternalTexture> texture, const sp<Fence>& fence)
+ : mTexturePool(texturePool), mTexture(texture), mFence(fence) {}
+
+ ~AutoTexture() { mTexturePool.returnTexture(std::move(mTexture), mFence); }
+
+ sp<Fence> getReadyFence() { return mFence; }
+
+ void setReadyFence(const sp<Fence>& fence) { mFence = fence; }
+
+ // Disable copying and assigning
+ AutoTexture(const AutoTexture&) = delete;
+ AutoTexture& operator=(const AutoTexture&) = delete;
+
+ // Gets a pointer to the underlying external texture
+ const std::shared_ptr<renderengine::ExternalTexture>& get() const { return mTexture; }
+
+ private:
+ TexturePool& mTexturePool;
+ std::shared_ptr<renderengine::ExternalTexture> mTexture;
+ sp<Fence> mFence;
+ };
+
+ TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine) {}
+
+ virtual ~TexturePool() = default;
+
+ // Sets the display size for the texture pool.
+ // This will trigger a reallocation for all remaining textures in the pool.
+ // setDisplaySize must be called for the texture pool to be used.
+ void setDisplaySize(ui::Size size);
+
+ // Borrows a new texture from the pool.
+ // If the pool is currently starved of textures, then a new texture is generated.
+ // When the AutoTexture object is destroyed, the scratch texture is automatically returned
+ // to the pool.
+ std::shared_ptr<AutoTexture> borrowTexture();
+
+protected:
+ // Proteted visibility so that they can be used for testing
+ const static constexpr size_t kMinPoolSize = 3;
+ const static constexpr size_t kMaxPoolSize = 4;
+
+ struct Entry {
+ std::shared_ptr<renderengine::ExternalTexture> texture;
+ sp<Fence> fence;
+ };
+
+ std::deque<Entry> mPool;
+
+private:
+ std::shared_ptr<renderengine::ExternalTexture> genTexture();
+ // Returns a previously borrowed texture to the pool.
+ void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
+ const sp<Fence>& fence);
+ renderengine::RenderEngine& mRenderEngine;
+ ui::Size mSize;
+};
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4b4d375..8e777e3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -109,7 +109,7 @@
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
- MOCK_METHOD0(renderCachedSets, void());
+ MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 953eb76..ae1336e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -50,36 +50,14 @@
Display::~Display() = default;
void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
- mIsVirtual = !args.physical;
+ mId = args.id;
+ mIsVirtual = !args.connectionType;
mPowerAdvisor = args.powerAdvisor;
editState().isSecure = args.isSecure;
editState().displaySpace.bounds = Rect(args.pixels);
setLayerStackFilter(args.layerStackId,
- args.physical &&
- args.physical->type == ui::DisplayConnectionType::Internal);
+ args.connectionType == ui::DisplayConnectionType::Internal);
setName(args.name);
- mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator;
-
- if (args.physical) {
- mId = args.physical->id;
- } else {
- std::optional<DisplayId> id;
- if (args.useHwcVirtualDisplays) {
- id = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
- }
- if (!id) {
- id = mGpuVirtualDisplayIdGenerator->nextId();
- }
- LOG_ALWAYS_FATAL_IF(!id, "Failed to generate display ID");
- mId = *id;
- }
-}
-
-std::optional<DisplayId> Display::maybeAllocateDisplayIdForVirtualDisplay(
- ui::Size pixels, ui::PixelFormat pixelFormat) const {
- auto& hwc = getCompositionEngine().getHwComposer();
- return hwc.allocateVirtualDisplay(static_cast<uint32_t>(pixels.width),
- static_cast<uint32_t>(pixels.height), &pixelFormat);
}
bool Display::isValid() const {
@@ -102,23 +80,16 @@
return mId;
}
-void Display::setDisplayIdForTesting(DisplayId displayId) {
- mId = displayId;
-}
-
void Display::disconnect() {
if (mIsDisconnected) {
return;
}
mIsDisconnected = true;
- if (const auto id = GpuVirtualDisplayId::tryCast(mId)) {
- mGpuVirtualDisplayIdGenerator->markUnused(*id);
- return;
+
+ if (const auto id = HalDisplayId::tryCast(mId)) {
+ getCompositionEngine().getHwComposer().disconnectDisplay(*id);
}
- const auto halDisplayId = HalDisplayId::tryCast(mId);
- LOG_FATAL_IF(!halDisplayId);
- getCompositionEngine().getHwComposer().disconnectDisplay(*halDisplayId);
}
void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
@@ -344,8 +315,8 @@
if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
return;
}
- auto outputState = editState();
- outputState.dataspace = clientTargetProperty.dataspace;
+
+ editState().dataspace = clientTargetProperty.dataspace;
getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 088a400..67bb149 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -129,7 +129,7 @@
}
if (enabled) {
- mPlanner = std::make_unique<planner::Planner>();
+ mPlanner = std::make_unique<planner::Planner>(getCompositionEngine().getRenderEngine());
if (mRenderSurface) {
mPlanner->setDisplaySize(mRenderSurface->getSize());
}
@@ -434,7 +434,7 @@
devOptRepaintFlash(refreshArgs);
finishFrame(refreshArgs);
postFramebuffer();
- renderCachedSets();
+ renderCachedSets(refreshArgs);
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -1188,19 +1188,6 @@
!layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
if (clientComposition || clearClientComposition) {
- compositionengine::LayerFE::ClientCompositionTargetSettings
- targetSettings{.clip = clip,
- .needsFiltering =
- layer->needsFiltering() || outputState.needsFiltering,
- .isSecure = outputState.isSecure,
- .supportsProtectedContent = supportsProtectedContent,
- .clearRegion = clientComposition ? clearRegion : stubRegion,
- .viewport = outputState.layerStackSpace.content,
- .dataspace = outputDataspace,
- .realContentIsVisible = realContentIsVisible,
- .clearContent = !clientComposition,
- .disableBlurs = disableBlurs};
-
std::vector<LayerFE::LayerSettings> results;
if (layer->getState().overrideInfo.buffer != nullptr) {
if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
@@ -1212,6 +1199,25 @@
layer->getLayerFE().getDebugName());
}
} else {
+ LayerFE::ClientCompositionTargetSettings::BlurSetting blurSetting = disableBlurs
+ ? LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled
+ : (layer->getState().overrideInfo.disableBackgroundBlur
+ ? LayerFE::ClientCompositionTargetSettings::BlurSetting::
+ BlurRegionsOnly
+ : LayerFE::ClientCompositionTargetSettings::BlurSetting::
+ Enabled);
+ compositionengine::LayerFE::ClientCompositionTargetSettings
+ targetSettings{.clip = clip,
+ .needsFiltering = layer->needsFiltering() ||
+ outputState.needsFiltering,
+ .isSecure = outputState.isSecure,
+ .supportsProtectedContent = supportsProtectedContent,
+ .clearRegion = clientComposition ? clearRegion : stubRegion,
+ .viewport = outputState.layerStackSpace.content,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = realContentIsVisible,
+ .clearContent = !clientComposition,
+ .blurSetting = blurSetting};
results = layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
layer->editState().clientCompositionTimestamp = systemTime();
@@ -1306,9 +1312,9 @@
mReleasedLayers.clear();
}
-void Output::renderCachedSets() {
+void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
if (mPlanner) {
- mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
+ mPlanner->renderCachedSets(getState(), refreshArgs.nextInvalidateTime);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index cd14327..e4e46a7 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -349,7 +349,7 @@
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
- writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
+ writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer);
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
skipLayer);
@@ -471,7 +471,8 @@
}
void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
- HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+ HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
case hal::Error::NONE:
break;
@@ -504,7 +505,7 @@
break;
case hal::Composition::CURSOR:
case hal::Composition::DEVICE:
- writeBufferStateToHWC(hwcLayer, outputIndependentState);
+ writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
break;
case hal::Composition::INVALID:
case hal::Composition::CLIENT:
@@ -541,7 +542,8 @@
}
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
- const LayerFECompositionState& outputIndependentState) {
+ const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
auto supportedPerFrameMetadata =
getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata();
if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata,
@@ -554,7 +556,7 @@
sp<GraphicBuffer> buffer = outputIndependentState.buffer;
sp<Fence> acquireFence = outputIndependentState.acquireFence;
int slot = outputIndependentState.bufferSlot;
- if (getState().overrideInfo.buffer != nullptr) {
+ if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
buffer = getState().overrideInfo.buffer->getBuffer();
acquireFence = getState().overrideInfo.acquireFence;
slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index b4c314c..cfa740e 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -78,6 +78,8 @@
std::string visibleRegionString;
overrideInfo.visibleRegion.dump(visibleRegionString, "");
dumpVal(out, "override visible region", visibleRegionString);
+ dumpVal(out, "override peekThroughLayer", overrideInfo.peekThroughLayer);
+ dumpVal(out, "override disableBackgroundBlur", overrideInfo.disableBackgroundBlur);
if (hwc) {
dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index b61daeb..acc7ed2 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -25,6 +25,7 @@
#include <math/HashCombine.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
#include <utils/Trace.h>
@@ -133,7 +134,7 @@
}
bool CachedSet::hasReadyBuffer() const {
- return mTexture != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+ return mTexture && mDrawFence->getStatus() == Fence::Status::Signaled;
}
std::vector<CachedSet> CachedSet::decompose() const {
@@ -155,7 +156,7 @@
}
}
-void CachedSet::render(renderengine::RenderEngine& renderEngine,
+void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
const OutputCompositionState& outputState) {
ATRACE_CALL();
const Rect& viewport = outputState.layerStackSpace.content;
@@ -164,10 +165,7 @@
ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
renderengine::DisplaySettings displaySettings{
- .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
- -mBounds.top + outputState.framebufferSpace.content.top,
- -mBounds.left + outputState.framebufferSpace.content.right,
- -mBounds.top + outputState.framebufferSpace.content.bottom),
+ .physicalDisplay = outputState.framebufferSpace.content,
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
@@ -184,7 +182,7 @@
.dataspace = outputDataspace,
.realContentIsVisible = true,
.clearContent = false,
- .disableBlurs = false,
+ .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
std::vector<renderengine::LayerSettings> layerSettings;
@@ -201,6 +199,24 @@
std::transform(layerSettings.cbegin(), layerSettings.cend(),
std::back_inserter(layerSettingsPointers),
[](const renderengine::LayerSettings& settings) { return &settings; });
+
+ renderengine::LayerSettings blurLayerSettings;
+ if (mBlurLayer) {
+ auto blurSettings = targetSettings;
+ blurSettings.blurSetting =
+ LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
+ auto clientCompositionList =
+ mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ blurSettings);
+ blurLayerSettings = clientCompositionList.back();
+ // This mimics Layer::prepareClearClientComposition
+ blurLayerSettings.skipContentDraw = true;
+ blurLayerSettings.name = std::string("blur layer");
+ // Clear out the shadow settings
+ blurLayerSettings.shadow = {};
+ layerSettingsPointers.push_back(&blurLayerSettings);
+ }
+
renderengine::LayerSettings holePunchSettings;
if (mHolePunchLayer) {
auto clientCompositionList =
@@ -209,7 +225,6 @@
// Assume that the final layer contains the buffer that we want to
// replace with a hole punch.
holePunchSettings = clientCompositionList.back();
- LOG_ALWAYS_FATAL_IF(!holePunchSettings.source.buffer.buffer, "Expected to have a buffer!");
// This mimics Layer::prepareClearClientComposition
holePunchSettings.source.buffer.buffer = nullptr;
holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
@@ -237,30 +252,34 @@
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);
- const auto texture = std::make_shared<
- renderengine::ExternalTexture>(buffer, renderEngine,
- renderengine::ExternalTexture::Usage::READABLE |
- renderengine::ExternalTexture::Usage::WRITEABLE);
- LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
- base::unique_fd drawFence;
+ auto texture = texturePool.borrowTexture();
+ LOG_ALWAYS_FATAL_IF(texture->get()->getBuffer()->initCheck() != OK);
- status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture,
- false, base::unique_fd(), &drawFence);
+ base::unique_fd bufferFence;
+ if (texture->getReadyFence()) {
+ // Bail out if the buffer is not ready, because there is some pending GPU work left.
+ if (texture->getReadyFence()->getStatus() != Fence::Status::Signaled) {
+ return;
+ }
+ bufferFence.reset(texture->getReadyFence()->dup());
+ }
+
+ base::unique_fd drawFence;
+ status_t result =
+ renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false,
+ std::move(bufferFence), &drawFence);
if (result == NO_ERROR) {
mDrawFence = new Fence(drawFence.release());
mOutputSpace = outputState.framebufferSpace;
- mTexture = std::move(texture);
+ mTexture = texture;
+ mTexture->setReadyFence(mDrawFence);
mOutputSpace.orientation = outputState.framebufferSpace.orientation;
mOutputDataspace = outputDataspace;
mOrientation = orientation;
+ mSkipCount = 0;
} else {
- mTexture = nullptr;
+ mTexture.reset();
}
}
@@ -276,6 +295,12 @@
return false;
}
+ // Do not use a hole punch with an HDR layer; this should be done in client
+ // composition to properly mix HDR with SDR.
+ if (hasHdrLayers()) {
+ return false;
+ }
+
const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
if (layerFE.getCompositionState()->forceClientComposition) {
return false;
@@ -315,10 +340,28 @@
}
}
+void CachedSet::addBackgroundBlurLayer(const CachedSet& blurLayer) {
+ mBlurLayer = blurLayer.getFirstLayer().getState();
+}
+
compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
}
+compositionengine::OutputLayer* CachedSet::getBlurLayer() const {
+ return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
+}
+
+bool CachedSet::hasHdrLayers() const {
+ return std::any_of(mLayers.cbegin(), mLayers.cend(),
+ [](const Layer& layer) { return layer.getState()->isHdr(); });
+}
+
+bool CachedSet::hasProtectedLayers() const {
+ return std::any_of(mLayers.cbegin(), mLayers.cend(),
+ [](const Layer& layer) { return layer.getState()->isProtected(); });
+}
+
void CachedSet::dump(std::string& result) const {
const auto now = std::chrono::steady_clock::now();
@@ -327,7 +370,7 @@
base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
mFingerprint, durationString(lastUpdate).c_str(), mAge);
{
- const auto b = mTexture ? mTexture->getBuffer().get() : nullptr;
+ const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
base::StringAppendF(&result, " Override buffer: %p\n", b);
}
base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 2def99d..2bcaf60 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -19,10 +19,11 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/properties.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
-#include <utils/Trace.h>
+#include <gui/TraceUtils.h>
using time_point = std::chrono::steady_clock::time_point;
using namespace std::chrono_literals;
@@ -59,6 +60,20 @@
} // namespace
+Flattener::Flattener(
+ renderengine::RenderEngine& renderEngine, bool enableHolePunch,
+ std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables)
+ : mRenderEngine(renderEngine),
+ mEnableHolePunch(enableHolePunch),
+ mCachedSetRenderSchedulingTunables(cachedSetRenderSchedulingTunables),
+ mTexturePool(mRenderEngine) {
+ const int timeoutInMs =
+ base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0);
+ if (timeoutInMs != 0) {
+ mActiveLayerTimeout = std::chrono::milliseconds(timeoutInMs);
+ }
+}
+
NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
NonBufferHash hash, time_point now) {
ATRACE_CALL();
@@ -93,14 +108,46 @@
return hash;
}
-void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
- const OutputCompositionState& outputState) {
+void Flattener::renderCachedSets(
+ const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
ATRACE_CALL();
- if (!mNewCachedSet || mNewCachedSet->hasRenderedBuffer()) {
+
+ if (!mNewCachedSet) {
return;
}
- mNewCachedSet->render(renderEngine, outputState);
+ // Ensure that a cached set has a valid buffer first
+ if (mNewCachedSet->hasRenderedBuffer()) {
+ ATRACE_NAME("mNewCachedSet->hasRenderedBuffer()");
+ return;
+ }
+
+ const auto now = std::chrono::steady_clock::now();
+
+ // If we have a render deadline, and the flattener is configured to skip rendering if we don't
+ // have enough time, then we skip rendering the cached set if we think that we'll steal too much
+ // time from the next frame.
+ if (renderDeadline && mCachedSetRenderSchedulingTunables) {
+ if (const auto estimatedRenderFinish =
+ now + mCachedSetRenderSchedulingTunables->cachedSetRenderDuration;
+ estimatedRenderFinish > *renderDeadline) {
+ mNewCachedSet->incrementSkipCount();
+
+ if (mNewCachedSet->getSkipCount() <=
+ mCachedSetRenderSchedulingTunables->maxDeferRenderAttempts) {
+ ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us",
+ std::chrono::duration_cast<std::chrono::microseconds>(
+ estimatedRenderFinish - *renderDeadline)
+ .count());
+ return;
+ } else {
+ ATRACE_NAME("DeadlinePassed: exceeded max skips");
+ }
+ }
+ }
+
+ mNewCachedSet->render(mRenderEngine, mTexturePool, outputState);
}
void Flattener::dumpLayers(std::string& result) const {
@@ -231,6 +278,7 @@
return false;
}
+ // the compiler should strip out the following no-op loops when ALOGV is off
ALOGV("[%s] Incoming layers:", __func__);
for (const LayerState* layer : layers) {
ALOGV("%s", layer->getName().c_str());
@@ -238,13 +286,22 @@
ALOGV("[%s] Current layers:", __func__);
for (const CachedSet& layer : mLayers) {
- std::string dump;
- layer.dump(dump);
- ALOGV("%s", dump.c_str());
+ const auto dumper = [&] {
+ std::string dump;
+ layer.dump(dump);
+ return dump;
+ };
+ ALOGV("%s", dumper().c_str());
}
auto currentLayerIter = mLayers.begin();
auto incomingLayerIter = layers.begin();
+
+ // If not null, this represents the layer that is blurring the layer before
+ // currentLayerIter. The blurring was stored in the override buffer, so the
+ // layer that requests the blur no longer needs to do any blurring.
+ compositionengine::OutputLayer* priorBlurLayer = nullptr;
+
while (incomingLayerIter != layers.end()) {
if (mNewCachedSet &&
mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
@@ -259,17 +316,20 @@
auto* peekThroughLayer = mNewCachedSet->getHolePunchLayer();
const size_t layerCount = currentLayerIter->getLayerCount();
for (size_t i = 0; i < layerCount; ++i) {
+ bool disableBlur = priorBlurLayer &&
+ priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
OutputLayer::CompositionState& state =
(*incomingLayerIter)->getOutputLayer()->editState();
state.overrideInfo = {
.buffer = mNewCachedSet->getBuffer(),
.acquireFence = mNewCachedSet->getDrawFence(),
- .displayFrame = mNewCachedSet->getBounds(),
+ .displayFrame = mNewCachedSet->getTextureBounds(),
.dataspace = mNewCachedSet->getOutputDataspace(),
.displaySpace = mNewCachedSet->getOutputSpace(),
.damageRegion = Region::INVALID_REGION,
.visibleRegion = mNewCachedSet->getVisibleRegion(),
.peekThroughLayer = peekThroughLayer,
+ .disableBackgroundBlur = disableBlur,
};
++incomingLayerIter;
}
@@ -281,6 +341,7 @@
skipCount -= layerCount;
}
+ priorBlurLayer = mNewCachedSet->getBlurLayer();
merged.emplace_back(std::move(*mNewCachedSet));
mNewCachedSet = std::nullopt;
continue;
@@ -295,17 +356,20 @@
const size_t layerCount = currentLayerIter->getLayerCount();
auto* peekThroughLayer = currentLayerIter->getHolePunchLayer();
for (size_t i = 0; i < layerCount; ++i) {
+ bool disableBlur =
+ priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
OutputLayer::CompositionState& state =
(*incomingLayerIter)->getOutputLayer()->editState();
state.overrideInfo = {
.buffer = currentLayerIter->getBuffer(),
.acquireFence = currentLayerIter->getDrawFence(),
- .displayFrame = currentLayerIter->getBounds(),
+ .displayFrame = currentLayerIter->getTextureBounds(),
.dataspace = currentLayerIter->getOutputDataspace(),
.displaySpace = currentLayerIter->getOutputSpace(),
.damageRegion = Region(),
.visibleRegion = currentLayerIter->getVisibleRegion(),
.peekThroughLayer = peekThroughLayer,
+ .disableBackgroundBlur = disableBlur,
};
++incomingLayerIter;
}
@@ -313,15 +377,26 @@
// Break the current layer into its constituent layers
++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
for (CachedSet& layer : currentLayerIter->decompose()) {
+ bool disableBlur =
+ priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo.disableBackgroundBlur = disableBlur;
layer.updateAge(now);
merged.emplace_back(layer);
++incomingLayerIter;
}
} else {
+ bool disableBlur =
+ priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo.disableBackgroundBlur = disableBlur;
currentLayerIter->updateAge(now);
merged.emplace_back(*currentLayerIter);
++incomingLayerIter;
}
+ priorBlurLayer = currentLayerIter->getBlurLayer();
++currentLayerIter;
}
@@ -342,9 +417,10 @@
bool runHasFirstLayer = false;
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
- const bool layerIsInactive = now - currentSet->getLastUpdate() > kActiveLayerTimeout;
+ const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout;
const bool layerHasBlur = currentSet->hasBlurBehind();
- if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur)) {
+ if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
+ !currentSet->hasHdrLayers() && !currentSet->hasProtectedLayers()) {
if (isPartOfRun) {
builder.append(currentSet->getLayerCount());
} else {
@@ -361,6 +437,15 @@
}
} else if (isPartOfRun) {
builder.setHolePunchCandidate(&(*currentSet));
+
+ // If we're here then this blur layer recently had an active buffer updating, meaning
+ // that there is exactly one layer. Blur radius currently is part of layer stack
+ // geometry, so we're also guaranteed that the background blur radius hasn't changed for
+ // at least as long as this new inactive cached set.
+ if (runHasFirstLayer && layerHasBlur &&
+ currentSet->getFirstLayer().getBackgroundBlurRadius() > 0) {
+ builder.setBlurringLayer(&(*currentSet));
+ }
if (auto run = builder.validateAndBuild(); run) {
runs.push_back(*run);
}
@@ -422,6 +507,10 @@
mNewCachedSet->append(*currentSet);
}
+ if (bestRun->getBlurringLayer()) {
+ mNewCachedSet->addBackgroundBlurLayer(*bestRun->getBlurringLayer());
+ }
+
if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
bestRun->getHolePunchCandidate()->requiresHolePunch()) {
// Add the pip layer to mNewCachedSet, but in a special way - it should
@@ -435,9 +524,14 @@
++mCachedSetCreationCount;
mCachedSetCreationCost += mNewCachedSet->getCreationCost();
- std::string setDump;
- mNewCachedSet->dump(setDump);
- ALOGV("[%s] Added new cached set:\n%s", __func__, setDump.c_str());
+
+ // note the compiler should strip the follow no-op statements when ALOGV is off
+ const auto dumper = [&] {
+ std::string setDump;
+ mNewCachedSet->dump(setDump);
+ return setDump;
+ };
+ ALOGV("[%s] Added new cached set:\n%s", __func__, dumper().c_str());
}
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 297c0b2..be2510f 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -26,15 +26,43 @@
#include <compositionengine/impl/planner/Planner.h>
#include <utils/Trace.h>
+#include <chrono>
namespace android::compositionengine::impl::planner {
-Planner::Planner()
+namespace {
+
+std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() {
+ if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) {
+ return std::nullopt;
+ }
+
+ auto renderDuration = std::chrono::nanoseconds(
+ base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"),
+ Flattener::CachedSetRenderSchedulingTunables::
+ kDefaultCachedSetRenderDuration.count()));
+
+ auto maxDeferRenderAttempts = base::GetUintProperty<
+ size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"),
+ Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts);
+
+ return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>(
+ Flattener::CachedSetRenderSchedulingTunables{
+ .cachedSetRenderDuration = renderDuration,
+ .maxDeferRenderAttempts = maxDeferRenderAttempts,
+ });
+}
+
+} // namespace
+
+Planner::Planner(renderengine::RenderEngine& renderEngine)
// Implicitly, layer caching must also be enabled for the hole punch or
// predictor to have any effect.
// E.g., setprop debug.sf.enable_layer_caching 1, or
// adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
- : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) {
+ : mFlattener(renderEngine,
+ base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true),
+ buildFlattenerTuneables()) {
mPredictorEnabled =
base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
}
@@ -160,10 +188,11 @@
finalPlan);
}
-void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
- const OutputCompositionState& outputState) {
+void Planner::renderCachedSets(
+ const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
ATRACE_CALL();
- mFlattener.renderCachedSets(renderEngine, outputState);
+ mFlattener.renderCachedSets(outputState, renderDeadline);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
new file mode 100644
index 0000000..e3772a2
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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/TexturePool.h>
+#include <utils/Log.h>
+
+namespace android::compositionengine::impl::planner {
+
+void TexturePool::setDisplaySize(ui::Size size) {
+ if (mSize == size) {
+ return;
+ }
+ mSize = size;
+ mPool.clear();
+ mPool.resize(kMinPoolSize);
+ std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; });
+}
+
+std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
+ if (mPool.empty()) {
+ return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
+ }
+
+ const auto entry = mPool.front();
+ mPool.pop_front();
+ return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
+}
+
+void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
+ const sp<Fence>& fence) {
+ // Drop the texture on the floor if the pool is no longer tracking textures of the same size.
+ if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
+ static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
+ ALOGV("Deallocating texture from Planner's pool - display size changed (previous: (%dx%d), "
+ "current: (%dx%d))",
+ texture->getBuffer()->getWidth(), texture->getBuffer()->getHeight(), mSize.getWidth(),
+ mSize.getHeight());
+ return;
+ }
+
+ // Also ensure the pool does not grow beyond a maximum size.
+ if (mPool.size() == kMaxPoolSize) {
+ ALOGD("Deallocating texture from Planner's pool - max size [%" PRIu64 "] reached",
+ static_cast<uint64_t>(kMaxPoolSize));
+ return;
+ }
+
+ mPool.push_back({std::move(texture), fence});
+}
+
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
+ LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
+ return std::make_shared<
+ renderengine::ExternalTexture>(sp<GraphicBuffer>::
+ make(mSize.getWidth(), mSize.getHeight(),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GraphicBuffer::USAGE_HW_RENDER |
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ "Planner"),
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+}
+
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index e12cb57..db9437b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
#include <cmath>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
@@ -60,12 +56,12 @@
using testing::SetArgPointee;
using testing::StrictMock;
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId{42};
-// TODO(b/160679868) Use VirtualDisplayId
-constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId{43};
-constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
-constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr int32_t DEFAULT_LAYER_STACK = 123;
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123u);
+constexpr HalVirtualDisplayId HAL_VIRTUAL_DISPLAY_ID{456u};
+constexpr GpuVirtualDisplayId GPU_VIRTUAL_DISPLAY_ID{789u};
+
+const ui::Size DEFAULT_RESOLUTION{1920, 1080};
+constexpr uint32_t DEFAULT_LAYER_STACK = 42;
struct Layer {
Layer() {
@@ -94,8 +90,6 @@
public:
using impl::Display::injectOutputLayerForTest;
virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
-
- using impl::Display::maybeAllocateDisplayIdForVirtualDisplay;
};
// Uses a special implementation with key internal member functions set up
@@ -169,21 +163,19 @@
DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
return DisplayCreationArgsBuilder()
- .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
- .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setId(DEFAULT_DISPLAY_ID)
+ .setConnectionType(ui::DisplayConnectionType::Internal)
+ .setPixels(DEFAULT_RESOLUTION)
.setIsSecure(true)
.setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.build();
}
- DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
+ DisplayCreationArgs getDisplayCreationArgsForGpuVirtualDisplay() {
return DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(false)
- .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
- .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setId(GPU_VIRTUAL_DISPLAY_ID)
+ .setPixels(DEFAULT_RESOLUTION)
.setIsSecure(false)
.setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
@@ -195,7 +187,6 @@
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
- RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuDisplayIdGenerator;
};
struct PartialMockDisplayTestCommon : public DisplayTestCommon {
@@ -247,9 +238,9 @@
EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
}
-TEST_F(DisplayCreationTest, createNonHwcVirtualDisplay) {
- auto display = impl::createDisplay(mCompositionEngine,
- getDisplayCreationArgsForNonHWCVirtualDisplay());
+TEST_F(DisplayCreationTest, createGpuVirtualDisplay) {
+ auto display =
+ impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForGpuVirtualDisplay());
EXPECT_FALSE(display->isSecure());
EXPECT_TRUE(display->isVirtual());
EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
@@ -262,17 +253,15 @@
using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
- mDisplay->setConfiguration(
- DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(true)
- .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
- .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
- .setIsSecure(true)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&mPowerAdvisor)
- .setName(getDisplayNameFromCurrentTest())
- .build());
+ mDisplay->setConfiguration(DisplayCreationArgsBuilder()
+ .setId(DEFAULT_DISPLAY_ID)
+ .setConnectionType(ui::DisplayConnectionType::Internal)
+ .setPixels(DEFAULT_RESOLUTION)
+ .setIsSecure(true)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
EXPECT_TRUE(mDisplay->isSecure());
@@ -283,17 +272,15 @@
}
TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
- mDisplay->setConfiguration(
- DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(true)
- .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::External})
- .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
- .setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&mPowerAdvisor)
- .setName(getDisplayNameFromCurrentTest())
- .build());
+ mDisplay->setConfiguration(DisplayCreationArgsBuilder()
+ .setId(DEFAULT_DISPLAY_ID)
+ .setConnectionType(ui::DisplayConnectionType::External)
+ .setPixels(DEFAULT_RESOLUTION)
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
@@ -303,25 +290,17 @@
EXPECT_FALSE(mDisplay->isValid());
}
-TEST_F(DisplaySetConfigurationTest, configuresHwcBackedVirtualDisplay) {
- EXPECT_CALL(mHwComposer,
- allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
- Pointee(Eq(static_cast<ui::PixelFormat>(
- PIXEL_FORMAT_RGBA_8888)))))
- .WillOnce(Return(VIRTUAL_DISPLAY_ID));
+TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) {
+ mDisplay->setConfiguration(DisplayCreationArgsBuilder()
+ .setId(HAL_VIRTUAL_DISPLAY_ID)
+ .setPixels(DEFAULT_RESOLUTION)
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
- mDisplay->setConfiguration(
- DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(true)
- .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
- .setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&mPowerAdvisor)
- .setName(getDisplayNameFromCurrentTest())
- .build());
-
- EXPECT_EQ(VIRTUAL_DISPLAY_ID, mDisplay->getId());
+ EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -329,47 +308,17 @@
EXPECT_FALSE(mDisplay->isValid());
}
-TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAllocationFails) {
- EXPECT_CALL(mHwComposer,
- allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
- Pointee(Eq(static_cast<ui::PixelFormat>(
- PIXEL_FORMAT_RGBA_8888)))))
- .WillOnce(Return(std::nullopt));
+TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) {
+ mDisplay->setConfiguration(DisplayCreationArgsBuilder()
+ .setId(GPU_VIRTUAL_DISPLAY_ID)
+ .setPixels(DEFAULT_RESOLUTION)
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
- mDisplay->setConfiguration(
- DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(true)
- .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
- .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
- .setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&mPowerAdvisor)
- .setName(getDisplayNameFromCurrentTest())
- .build());
-
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
- EXPECT_FALSE(mDisplay->isSecure());
- EXPECT_TRUE(mDisplay->isVirtual());
- EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
- EXPECT_FALSE(mDisplay->getState().layerStackInternal);
- EXPECT_FALSE(mDisplay->isValid());
-}
-
-TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShouldNotUseHwc) {
- mDisplay->setConfiguration(
- DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(false)
- .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
- .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
- .setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&mPowerAdvisor)
- .setName(getDisplayNameFromCurrentTest())
- .build());
-
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
+ EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -476,7 +425,7 @@
TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
using ColorProfile = Output::ColorProfile;
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
std::shared_ptr<impl::Display> virtualDisplay = impl::createDisplay(mCompositionEngine, args);
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
@@ -521,7 +470,11 @@
TEST_F(DisplayCreateRenderSurfaceTest, setsRenderSurface) {
EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
- mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
+ mDisplay->createRenderSurface(RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(640)
+ .setDisplayHeight(480)
+ .setNativeWindow(mNativeWindow)
+ .build());
EXPECT_TRUE(mDisplay->getRenderSurface() != nullptr);
}
@@ -551,25 +504,25 @@
using DisplaySetReleasedLayersTest = DisplayWithLayersTestCommon;
-TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNotHwcDisplay) {
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfGpuDisplay) {
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
{
Output::ReleasedLayers releasedLayers;
releasedLayers.emplace_back(layerXLayerFE);
- nonHwcDisplay->setReleasedLayers(std::move(releasedLayers));
+ gpuDisplay->setReleasedLayers(std::move(releasedLayers));
}
CompositionRefreshArgs refreshArgs;
refreshArgs.layersWithQueuedFrames.push_back(layerXLayerFE);
- nonHwcDisplay->setReleasedLayers(refreshArgs);
+ gpuDisplay->setReleasedLayers(refreshArgs);
- const auto& releasedLayers = nonHwcDisplay->getReleasedLayersForTest();
- ASSERT_EQ(1, releasedLayers.size());
+ const auto& releasedLayers = gpuDisplay->getReleasedLayersForTest();
+ ASSERT_EQ(1u, releasedLayers.size());
}
TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) {
@@ -585,7 +538,7 @@
mDisplay->setReleasedLayers(refreshArgs);
const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
- ASSERT_EQ(1, releasedLayers.size());
+ ASSERT_EQ(1u, releasedLayers.size());
}
TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) {
@@ -599,7 +552,7 @@
mDisplay->setReleasedLayers(refreshArgs);
const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
- ASSERT_EQ(2, releasedLayers.size());
+ ASSERT_EQ(2u, releasedLayers.size());
ASSERT_EQ(mLayer1.layerFE.get(), releasedLayers[0].promote().get());
ASSERT_EQ(mLayer2.layerFE.get(), releasedLayers[1].promote().get());
}
@@ -610,15 +563,15 @@
using DisplayChooseCompositionStrategyTest = PartialMockDisplayTestCommon;
-TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) {
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- std::shared_ptr<Display> nonHwcDisplay =
+TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfGpuDisplay) {
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<Display> gpuDisplay =
createPartialMockDisplay<Display>(mCompositionEngine, args);
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(nonHwcDisplay->getId()));
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
- nonHwcDisplay->chooseCompositionStrategy();
+ gpuDisplay->chooseCompositionStrategy();
- auto& state = nonHwcDisplay->getState();
+ auto& state = gpuDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
EXPECT_FALSE(state.usesDeviceComposition);
}
@@ -700,12 +653,12 @@
using DisplayGetSkipColorTransformTest = DisplayWithLayersTestCommon;
-TEST_F(DisplayGetSkipColorTransformTest, checksCapabilityIfNonHwcDisplay) {
+TEST_F(DisplayGetSkipColorTransformTest, checksCapabilityIfGpuDisplay) {
EXPECT_CALL(mHwComposer, hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM))
.WillOnce(Return(true));
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
- EXPECT_TRUE(nonHwcDisplay->getSkipColorTransform());
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ auto gpuDisplay{impl::createDisplay(mCompositionEngine, args)};
+ EXPECT_TRUE(gpuDisplay->getSkipColorTransform());
}
TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
@@ -847,16 +800,39 @@
}
/*
+ * Display::applyClientTargetRequests()
+ */
+
+using DisplayApplyClientTargetRequests = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyLayerRequestsToLayersTest, applyClientTargetRequests) {
+ Display::ClientTargetProperty clientTargetProperty = {
+ .pixelFormat = hal::PixelFormat::RGB_565,
+ .dataspace = hal::Dataspace::STANDARD_BT470M,
+ };
+
+ mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+ mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+ EXPECT_CALL(*renderSurface, setBufferPixelFormat(clientTargetProperty.pixelFormat));
+ EXPECT_CALL(*renderSurface, setBufferDataspace(clientTargetProperty.dataspace));
+ mDisplay->applyClientTargetRequests(clientTargetProperty);
+
+ auto& state = mDisplay->getState();
+ EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
+}
+
+/*
* Display::presentAndGetFrameFences()
*/
using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon;
-TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnNonHwcDisplay) {
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnGpuDisplay) {
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ auto gpuDisplay{impl::createDisplay(mCompositionEngine, args)};
- auto result = nonHwcDisplay->presentAndGetFrameFences();
+ auto result = gpuDisplay->presentAndGetFrameFences();
ASSERT_TRUE(result.presentFence.get());
EXPECT_FALSE(result.presentFence->isValid());
@@ -885,9 +861,9 @@
EXPECT_EQ(presentFence, result.presentFence);
EXPECT_EQ(2u, result.layerFences.size());
- ASSERT_EQ(1, result.layerFences.count(&mLayer1.hwc2Layer));
+ ASSERT_EQ(1u, result.layerFences.count(&mLayer1.hwc2Layer));
EXPECT_EQ(layer1Fence, result.layerFences[&mLayer1.hwc2Layer]);
- ASSERT_EQ(1, result.layerFences.count(&mLayer2.hwc2Layer));
+ ASSERT_EQ(1u, result.layerFences.count(&mLayer2.hwc2Layer));
EXPECT_EQ(layer2Fence, result.layerFences[&mLayer2.hwc2Layer]);
}
@@ -933,66 +909,66 @@
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
- nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+ gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect no calls to queueBuffer if composition was skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
- nonHwcDisplay->editState().isEnabled = true;
- nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
- nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+ gpuDisplay->editState().isEnabled = true;
+ gpuDisplay->editState().usesClientComposition = false;
+ gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
refreshArgs.repaintEverything = false;
- nonHwcDisplay->finishFrame(refreshArgs);
+ gpuDisplay->finishFrame(refreshArgs);
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
- nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+ gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect a single call to queueBuffer when composition is not skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
- nonHwcDisplay->editState().isEnabled = true;
- nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
- nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().isEnabled = true;
+ gpuDisplay->editState().usesClientComposition = false;
+ gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
CompositionRefreshArgs refreshArgs;
refreshArgs.repaintEverything = false;
- nonHwcDisplay->finishFrame(refreshArgs);
+ gpuDisplay->finishFrame(refreshArgs);
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
- auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
- std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
- nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+ gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect a single call to queueBuffer when composition is not skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
- nonHwcDisplay->editState().isEnabled = true;
- nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
- nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+ gpuDisplay->editState().isEnabled = true;
+ gpuDisplay->editState().usesClientComposition = false;
+ gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
refreshArgs.repaintEverything = true;
- nonHwcDisplay->finishFrame(refreshArgs);
+ gpuDisplay->finishFrame(refreshArgs);
}
/*
@@ -1017,23 +993,26 @@
NiceMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+
std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated<
Display>(mCompositionEngine,
DisplayCreationArgsBuilder()
- .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
- .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
- .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setId(DEFAULT_DISPLAY_ID)
+ .setConnectionType(ui::DisplayConnectionType::Internal)
+ .setPixels(DEFAULT_RESOLUTION)
.setIsSecure(true)
.setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
- .build()
+ .build());
- );
impl::RenderSurface* mRenderSurface =
new impl::RenderSurface{mCompositionEngine, *mDisplay,
- RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
- mDisplaySurface}};
+ RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(DEFAULT_RESOLUTION.width)
+ .setDisplayHeight(DEFAULT_RESOLUTION.height)
+ .setNativeWindow(mNativeWindow)
+ .setDisplaySurface(mDisplaySurface)
+ .build()};
};
TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
@@ -1049,6 +1028,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index cd2d09e..64cbea9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -39,14 +39,17 @@
HWComposer();
~HWComposer() override;
- MOCK_METHOD2(setConfiguration, void(HWC2::ComposerCallback*, int32_t));
+ MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback*));
MOCK_CONST_METHOD3(getDisplayIdentificationData,
bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
- MOCK_METHOD3(allocateVirtualDisplay,
- std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
+ MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
+ MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
+ MOCK_METHOD4(allocateVirtualDisplay,
+ bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
+ std::optional<PhysicalDisplayId>));
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
MOCK_METHOD4(getDeviceCompositionChanges,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 5bd1216..e9ecf3e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -699,6 +699,7 @@
Hwc2::IComposerClient::BlendMode::PREMULTIPLIED;
static constexpr float kAlpha = 51.f;
static constexpr float kOverrideAlpha = 1.f;
+ static constexpr float kSkipAlpha = 0.f;
static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
@@ -1055,6 +1056,22 @@
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kSkipAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
includeOverrideInfo();
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 6677f40..742b155 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -143,6 +143,7 @@
mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
+ EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
}
void injectOutputLayer(InjectedLayer& layer) {
@@ -156,6 +157,7 @@
static const Rect kDefaultDisplaySize;
StrictMock<mock::CompositionEngine> mCompositionEngine;
+ StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
@@ -1765,7 +1767,7 @@
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(postFramebuffer, void());
- MOCK_METHOD0(renderCachedSets, void());
+ MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
};
StrictMock<OutputPartialMock> mOutput;
@@ -1785,7 +1787,7 @@
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(Ref(args)));
EXPECT_CALL(mOutput, postFramebuffer());
- EXPECT_CALL(mOutput, renderCachedSets());
+ EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
}
@@ -3784,6 +3786,46 @@
EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
}
+MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "") {
+ *result_listener << "ClientCompositionTargetSettings' BlurSettings aren't equal \n";
+ *result_listener << "expected " << expectedBlurSetting << "\n";
+ *result_listener << "actual " << arg.blurSetting << "\n";
+
+ return expectedBlurSetting == arg.blurSetting;
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) {
+ LayerFE::LayerSettings mShadowSettings;
+ mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ mLayers[2].mOutputLayerState.overrideInfo.disableBackgroundBlur = true;
+
+ EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+ EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
+ EXPECT_CALL(*mLayers[2].mLayerFE,
+ prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+ LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly)))
+ .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
+ {mShadowSettings, mLayers[2].mLayerSettings})));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(3u, requests.size());
+ EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
+ EXPECT_EQ(mShadowSettings, requests[1]);
+ EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+
+ // Check that a timestamp was set for the layers that generated requests
+ EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
+ EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
+ EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
+}
+
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
onlyClientComposesClientComposedLayersIfNoClearingNeeded) {
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -3867,7 +3909,7 @@
kDisplayDataspace,
false /* realContentIsVisible */,
true /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -3879,7 +3921,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3923,7 +3965,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
@@ -3935,7 +3977,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
@@ -3947,7 +3989,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -3979,7 +4021,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -3991,7 +4033,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4003,7 +4045,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4035,7 +4077,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
@@ -4048,7 +4090,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4060,7 +4102,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4091,7 +4133,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4103,7 +4145,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4115,7 +4157,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4144,7 +4186,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4156,7 +4198,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4168,7 +4210,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4306,7 +4348,7 @@
kOutputDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4324,7 +4366,7 @@
kOutputDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4358,7 +4400,7 @@
kDisplayDataspace,
false /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
LayerFE::LayerSettings mShadowSettings;
@@ -4404,7 +4446,7 @@
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
- false /* disabledBlurs */,
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 9aeb290..5090bb2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#include "renderengine/ExternalTexture.h"
-#include "ui/GraphicBuffer.h"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
#include <cstdarg>
#include <cstdint>
@@ -32,7 +26,9 @@
#include <compositionengine/mock/NativeWindow.h>
#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
namespace android::compositionengine {
namespace {
@@ -67,9 +63,12 @@
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
- RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
- mDisplaySurface}};
+ RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(DEFAULT_DISPLAY_WIDTH)
+ .setDisplayHeight(DEFAULT_DISPLAY_HEIGHT)
+ .setNativeWindow(mNativeWindow)
+ .setDisplaySurface(mDisplaySurface)
+ .build()};
};
/*
@@ -367,11 +366,8 @@
mSurface.flip();
- EXPECT_EQ(501, mSurface.getPageFlipCount());
+ EXPECT_EQ(501u, mSurface.getPageFlipCount());
}
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 8f44677..0acc317 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -24,6 +24,7 @@
#include <renderengine/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
#include <ui/GraphicTypes.h>
+#include <utils/Errors.h>
#include <memory>
namespace android::compositionengine {
@@ -39,9 +40,19 @@
using impl::planner::CachedSet;
using impl::planner::LayerState;
using impl::planner::LayerStateField;
+using impl::planner::TexturePool;
namespace {
+MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "") {
+ *result_listener << "ClientCompositionTargetSettings' BlurSettings aren't equal \n";
+ *result_listener << "expected " << expectedBlurSetting << "\n";
+ *result_listener << "actual " << arg.blurSetting << "\n";
+
+ return expectedBlurSetting == arg.blurSetting;
+}
+static const ui::Size kOutputSize = ui::Size(1, 1);
+
class CachedSetTest : public testing::Test {
public:
CachedSetTest() = default;
@@ -67,9 +78,11 @@
impl::OutputCompositionState mOutputState;
android::renderengine::mock::RenderEngine mRenderEngine;
+ TexturePool mTexturePool = TexturePool(mRenderEngine);
};
void CachedSetTest::SetUp() {
+ mTexturePool.setDisplaySize(kOutputSize);
for (size_t i = 0; i < kNumLayers; i++) {
auto testLayer = std::make_unique<TestLayer>();
auto pos = static_cast<int32_t>(i);
@@ -210,6 +223,16 @@
EXPECT_EQ(2u, cachedSet.getAge());
}
+TEST_F(CachedSetTest, incrementSkipCount) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet cachedSet(layer);
+ EXPECT_EQ(0u, cachedSet.getSkipCount());
+ cachedSet.incrementSkipCount();
+ EXPECT_EQ(1u, cachedSet.getSkipCount());
+ cachedSet.incrementSkipCount();
+ EXPECT_EQ(2u, cachedSet.getSkipCount());
+}
+
TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) {
CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
@@ -244,6 +267,8 @@
CachedSet cachedSet1(layer1);
CachedSet cachedSet2(layer2);
cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
+ cachedSet1.incrementSkipCount();
+ EXPECT_EQ(1u, cachedSet1.getSkipCount());
cachedSet1.append(cachedSet2);
EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
@@ -255,6 +280,8 @@
EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
EXPECT_EQ(3u, cachedSet1.getLayerCount());
EXPECT_EQ(0u, cachedSet1.getAge());
+ EXPECT_EQ(0u, cachedSet1.getSkipCount());
+
expectNoBuffer(cachedSet1);
// TODO(b/181192080): check that getNonBufferHash returns the correct hash value
// EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
@@ -310,7 +337,7 @@
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
displaySettings.orientation);
@@ -324,10 +351,11 @@
EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
expectReadyBuffer(cachedSet);
EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+ EXPECT_EQ(mOutputState.framebufferSpace.content, cachedSet.getTextureBounds());
// Now check that appending a new cached set properly cleans up RenderEngine resources.
CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
@@ -358,7 +386,7 @@
const std::vector<const renderengine::LayerSettings*>& layers,
const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
displaySettings.orientation);
@@ -372,7 +400,7 @@
EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
expectReadyBuffer(cachedSet);
EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -415,6 +443,20 @@
EXPECT_FALSE(cachedSet.requiresHolePunch());
}
+TEST_F(CachedSetTest, holePunch_requiresNonHdr) {
+ mTestLayers[0]->outputLayerCompositionState.dataspace = ui::Dataspace::BT2020_PQ;
+ mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+ CachedSet cachedSet(layer);
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
TEST_F(CachedSetTest, requiresHolePunch) {
CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
@@ -545,7 +587,76 @@
};
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+}
+
+TEST_F(CachedSetTest, addHolePunch_noBuffer) {
+ // Same as addHolePunch, except that clientCompList3 does not contain a
+ // buffer. This imitates the case where the buffer had protected content, so
+ // BufferLayer did not add it to the LayerSettings. This should not assert.
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
+ clientCompList3.push_back({});
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> size_t {
+ // If the highlight layer is enabled, it will increase the size by 1.
+ // We're interested in the third layer either way.
+ EXPECT_GE(layers.size(), 3u);
+ const auto* holePunchSettings = layers[2];
+ EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
+ EXPECT_TRUE(holePunchSettings->disableBlending);
+ EXPECT_EQ(0.0f, holePunchSettings->alpha);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+}
+
+TEST_F(CachedSetTest, append_removesHolePunch) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.isOpaque = true;
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+ ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+
+ CachedSet cachedSet3(layer3);
+ cachedSet.append(cachedSet3);
+ ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
}
TEST_F(CachedSetTest, decompose_removesHolePunch) {
@@ -593,5 +704,78 @@
EXPECT_TRUE(cachedSet4.hasBlurBehind());
}
+TEST_F(CachedSetTest, addBackgroundBlurLayer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet cachedSet(layer1);
+
+ EXPECT_EQ(nullptr, cachedSet.getBlurLayer());
+
+ cachedSet.addBackgroundBlurLayer(layer2);
+ EXPECT_EQ(layer2.getState()->getOutputLayer(), cachedSet.getBlurLayer());
+}
+
+TEST_F(CachedSetTest, addBlur) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ cachedSet.addBackgroundBlurLayer(layer3);
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
+ clientCompList3.push_back({});
+
+ clientCompList3[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
+ renderengine::ExternalTexture::READABLE);
+
+ EXPECT_CALL(*layerFE1,
+ prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
+ Enabled)))
+ .WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2,
+ prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
+ Enabled)))
+ .WillOnce(Return(clientCompList2));
+ EXPECT_CALL(*layerFE3,
+ prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+ compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
+ BackgroundBlurOnly)))
+ .WillOnce(Return(clientCompList3));
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&, base::unique_fd*) -> int32_t {
+ // If the highlight layer is enabled, it will increase the size by 1.
+ // We're interested in the third layer either way.
+ EXPECT_GE(layers.size(), 3u);
+ const auto* blurSettings = layers[2];
+ EXPECT_TRUE(blurSettings->skipContentDraw);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings->source.solidColor);
+ EXPECT_EQ(0.0f, blurSettings->alpha);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 7ec2c98..334b855 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -24,9 +24,11 @@
#include <renderengine/ExternalTexture.h>
#include <renderengine/LayerSettings.h>
#include <renderengine/mock/RenderEngine.h>
+#include <chrono>
namespace android::compositionengine {
using namespace std::chrono_literals;
+using impl::planner::CachedSet;
using impl::planner::Flattener;
using impl::planner::LayerState;
using impl::planner::NonBufferHash;
@@ -43,19 +45,32 @@
namespace {
+class TestableFlattener : public Flattener {
+public:
+ TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch,
+ std::optional<Flattener::CachedSetRenderSchedulingTunables>
+ cachedSetRenderSchedulingTunables = std::nullopt)
+ : Flattener(renderEngine, enableHolePunch, cachedSetRenderSchedulingTunables) {}
+ const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
+};
+
class FlattenerTest : public testing::Test {
public:
- FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {}
+ FlattenerTest() : FlattenerTest(std::nullopt) {}
void SetUp() override;
protected:
+ FlattenerTest(std::optional<Flattener::CachedSetRenderSchedulingTunables>
+ cachedSetRenderSchedulingTunables)
+ : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true,
+ cachedSetRenderSchedulingTunables)) {}
void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
void initializeFlattener(const std::vector<const LayerState*>& layers);
void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
- // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
+ // mRenderEngine is held as a reference in mFlattener, so explicitly destroy mFlattener first.
renderengine::mock::RenderEngine mRenderEngine;
- std::unique_ptr<Flattener> mFlattener;
+ std::unique_ptr<TestableFlattener> mFlattener;
const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point mTime = kStartTime;
@@ -77,6 +92,7 @@
};
void FlattenerTest::SetUp() {
+ mFlattener->setDisplaySize({1, 1});
for (size_t i = 0; i < kNumLayers; i++) {
auto testLayer = std::make_unique<TestLayer>();
auto pos = static_cast<int32_t>(i);
@@ -139,13 +155,13 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
// same geometry, update the internal layer stack
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
}
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -155,7 +171,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -165,7 +181,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
EXPECT_NE(nullptr, buffer);
@@ -200,7 +216,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
}
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -246,7 +262,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -351,7 +367,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -388,7 +404,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -397,7 +413,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -410,7 +426,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -419,7 +435,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -461,7 +477,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -475,7 +491,7 @@
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -488,7 +504,7 @@
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -503,7 +519,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -515,7 +531,7 @@
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -554,7 +570,7 @@
// This will render a CachedSet.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -567,7 +583,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -616,7 +632,7 @@
// This will render a CachedSet.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -629,7 +645,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -673,7 +689,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -683,7 +699,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
EXPECT_EQ(nullptr, overrideBuffer3);
@@ -717,7 +733,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -728,7 +744,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
}
@@ -769,7 +785,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -779,13 +795,63 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, blurOverrideBuffer);
EXPECT_NE(nullptr, overrideBuffer3);
EXPECT_EQ(overrideBuffer3, overrideBuffer4);
}
+TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiveBehindLayers) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ auto& layerStateWithBlurBehind = mTestLayers[2]->layerState;
+ mTestLayers[2]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerStateWithBlurBehind->update(&mTestLayers[2]->outputLayer);
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& blurOverrideBuffer =
+ layerStateWithBlurBehind->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerStateWithBlurBehind.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the first two layers inactive, but update the blur layer
+ mTime += 200ms;
+ layerStateWithBlurBehind->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
+ ASSERT_NE(std::nullopt, cachedSet);
+ EXPECT_EQ(&mTestLayers[2]->outputLayer, cachedSet->getBlurLayer());
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // the new flattened layer is replaced
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer1);
+ EXPECT_EQ(nullptr, blurOverrideBuffer);
+}
+
TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) {
auto& layerState1 = mTestLayers[0]->layerState;
auto& layerState2 = mTestLayers[1]->layerState;
@@ -807,7 +873,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -815,16 +881,61 @@
// Simulate attempting to render prior to merging the new cached set with the layer stack.
// Here we should not try to re-render.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We provide the override buffer now that it's rendered
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer2, overrideBuffer1);
}
+const constexpr std::chrono::nanoseconds kCachedSetRenderDuration = 0ms;
+const constexpr size_t kMaxDeferRenderAttempts = 2;
+
+class FlattenerRenderSchedulingTest : public FlattenerTest {
+public:
+ FlattenerRenderSchedulingTest()
+ : FlattenerTest(
+ Flattener::CachedSetRenderSchedulingTunables{.cachedSetRenderDuration =
+ kCachedSetRenderDuration,
+ .maxDeferRenderAttempts =
+ kMaxDeferRenderAttempts}) {
+ }
+};
+
+TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToMaxAttempts) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the layers inactive
+ mTime += 200ms;
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) {
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ mFlattener->renderCachedSets(mOutputState,
+ std::chrono::steady_clock::now() -
+ (kCachedSetRenderDuration + 10ms));
+ }
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mOutputState,
+ std::chrono::steady_clock::now() -
+ (kCachedSetRenderDuration + 10ms));
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
new file mode 100644
index 0000000..b802e51
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "TexturePoolTest"
+
+#include <compositionengine/impl/planner/TexturePool.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+const ui::Size kDisplaySize(1, 1);
+const ui::Size kDisplaySizeTwo(2, 2);
+
+class TestableTexturePool : public TexturePool {
+public:
+ TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {}
+
+ size_t getMinPoolSize() const { return kMinPoolSize; }
+ size_t getMaxPoolSize() const { return kMaxPoolSize; }
+ size_t getPoolSize() const { return mPool.size(); }
+};
+
+struct TexturePoolTest : public testing::Test {
+ TexturePoolTest() {
+ 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());
+ mTexturePool.setDisplaySize(kDisplaySize);
+ }
+
+ ~TexturePoolTest() {
+ 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());
+ }
+
+ renderengine::mock::RenderEngine mRenderEngine;
+ TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine);
+};
+
+TEST_F(TexturePoolTest, preallocatesMinPool) {
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+}
+
+TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) {
+ for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) {
+ auto texture = mTexturePool.borrowTexture();
+ }
+
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+}
+
+TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) {
+ std::unordered_set<uint64_t> bufferIds;
+ std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
+ for (size_t i = 0; i < mTexturePool.getMaxPoolSize(); i++) {
+ textures.emplace_back(mTexturePool.borrowTexture());
+ bufferIds.insert(textures.back()->get()->getBuffer()->getId());
+ }
+
+ EXPECT_EQ(mTexturePool.getMaxPoolSize(), bufferIds.size());
+
+ for (size_t i = 0; i < 3; i++) {
+ textures.pop_front();
+ textures.emplace_back(mTexturePool.borrowTexture());
+ bufferIds.insert(textures.back()->get()->getBuffer()->getId());
+ }
+
+ EXPECT_EQ(mTexturePool.getMaxPoolSize(), bufferIds.size());
+}
+
+TEST_F(TexturePoolTest, goesPastMaxSizeAndRebounds) {
+ std::unordered_set<uint64_t> bufferIds;
+ std::vector<std::shared_ptr<TexturePool::AutoTexture>> textures;
+ for (size_t i = 0; i < mTexturePool.getMaxPoolSize() + 2; i++) {
+ textures.emplace_back(mTexturePool.borrowTexture());
+ bufferIds.insert(textures.back()->get()->getBuffer()->getId());
+ }
+
+ EXPECT_EQ(mTexturePool.getMaxPoolSize() + 2, bufferIds.size());
+
+ // Return the textures to the pool.
+ // Now when we cycle through the pool it's again bounded by max textures.
+ textures.clear();
+
+ std::unordered_set<uint64_t> newBufferIds;
+ for (size_t i = 0; i < 2 * mTexturePool.getMaxPoolSize(); i++) {
+ auto texture = mTexturePool.borrowTexture();
+ newBufferIds.insert(texture->get()->getBuffer()->getId());
+ }
+
+ EXPECT_EQ(mTexturePool.getMaxPoolSize(), newBufferIds.size());
+}
+
+TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) {
+ auto texture = mTexturePool.borrowTexture();
+
+ EXPECT_EQ(kDisplaySize.getWidth(),
+ static_cast<int32_t>(texture->get()->getBuffer()->getWidth()));
+ EXPECT_EQ(kDisplaySize.getHeight(),
+ static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
+ mTexturePool.setDisplaySize(kDisplaySizeTwo);
+
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ texture.reset();
+ // When the texture is returned to the pool, the pool now destroys it.
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+
+ texture = mTexturePool.borrowTexture();
+ EXPECT_EQ(kDisplaySizeTwo.getWidth(),
+ static_cast<int32_t>(texture->get()->getBuffer()->getWidth()));
+ EXPECT_EQ(kDisplaySizeTwo.getHeight(),
+ static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 0f18235..ca4b6ab 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -70,13 +70,14 @@
mIsPrimary(args.isPrimary) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
- compositionengine::
- RenderSurfaceCreationArgs{ANativeWindow_getWidth(args.nativeWindow.get()),
- ANativeWindow_getHeight(args.nativeWindow.get()),
- args.nativeWindow, args.displaySurface,
- static_cast<size_t>(
- SurfaceFlinger::
- maxFrameBufferAcquiredBuffers)});
+ compositionengine::RenderSurfaceCreationArgsBuilder()
+ .setDisplayWidth(ANativeWindow_getWidth(args.nativeWindow.get()))
+ .setDisplayHeight(ANativeWindow_getHeight(args.nativeWindow.get()))
+ .setNativeWindow(std::move(args.nativeWindow))
+ .setDisplaySurface(std::move(args.displaySurface))
+ .setMaxTextureCacheSize(
+ static_cast<size_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers))
+ .build());
if (!mFlinger->mDisableClientCompositionCache &&
SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index bf249cd..7e4d923 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -103,15 +103,21 @@
bool needsFiltering() const;
ui::LayerStack getLayerStack() const;
- // Returns the physical ID of this display. This function asserts the ID is physical and it
- // shouldn't be called for other display types, e.g. virtual.
+ DisplayId getId() const;
+
+ // Shorthand to upcast the ID of a display whose type is known as a precondition.
PhysicalDisplayId getPhysicalId() const {
- const auto displayIdOpt = PhysicalDisplayId::tryCast(getId());
- LOG_FATAL_IF(!displayIdOpt);
- return *displayIdOpt;
+ const auto id = PhysicalDisplayId::tryCast(getId());
+ LOG_FATAL_IF(!id);
+ return *id;
}
- DisplayId getId() const;
+ VirtualDisplayId getVirtualId() const {
+ const auto id = VirtualDisplayId::tryCast(getId());
+ LOG_FATAL_IF(!id);
+ return *id;
+ }
+
const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
int32_t getSequenceId() const { return mSequenceId; }
@@ -285,4 +291,16 @@
DisplayModes supportedModes;
};
+// Predicates for display lookup.
+
+struct WithLayerStack {
+ explicit WithLayerStack(ui::LayerStack layerStack) : layerStack(layerStack) {}
+
+ bool operator()(const DisplayDevice& display) const {
+ return display.getLayerStack() == layerStack;
+ }
+
+ ui::LayerStack layerStack;
+};
+
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 1cbcf59..caf0294 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -211,9 +211,8 @@
return unwrapRet(ret, 0);
}
-Error Composer::createVirtualDisplay(uint32_t width, uint32_t height,
- PixelFormat* format, Display* outDisplay)
-{
+Error Composer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ std::optional<Display>, Display* outDisplay) {
const uint32_t bufferSlotCount = 1;
Error error = kDefaultError;
if (mClient_2_2) {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 0619b8c..b525e63 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -18,6 +18,7 @@
#define ANDROID_SF_COMPOSER_HAL_H
#include <memory>
+#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
@@ -94,8 +95,8 @@
virtual Error executeCommands() = 0;
virtual uint32_t getMaxVirtualDisplayCount() = 0;
- virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
- Display* outDisplay) = 0;
+ virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*,
+ std::optional<Display> mirror, Display* outDisplay) = 0;
virtual Error destroyVirtualDisplay(Display display) = 0;
virtual Error acceptDisplayChanges(Display display) = 0;
@@ -341,7 +342,7 @@
uint32_t getMaxVirtualDisplayCount() override;
Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
- Display* outDisplay) override;
+ std::optional<Display> mirror, Display* outDisplay) override;
Error destroyVirtualDisplay(Display display) override;
Error acceptDisplayChanges(Display display) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index fae95e7..871465d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -56,20 +56,16 @@
// Implement this interface to receive hardware composer events.
//
// These callback functions will generally be called on a hwbinder thread, but
-// when first registering the callback the onHotplugReceived() function will
+// when first registering the callback the onComposerHalHotplug() function will
// immediately be called on the thread calling registerCallback().
-//
-// All calls receive a sequenceId, which will be the value that was supplied to
-// HWC2::Device::registerCallback(). It's used to help differentiate callbacks
-// from different hardware composer instances.
struct ComposerCallback {
- virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId, hal::Connection) = 0;
- virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId) = 0;
- virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos>) = 0;
- virtual void onVsyncPeriodTimingChangedReceived(int32_t sequenceId, hal::HWDisplayId,
- const hal::VsyncPeriodChangeTimeline&) = 0;
- virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId) = 0;
+ virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
+ virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
+ virtual void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos>) = 0;
+ virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
+ const hal::VsyncPeriodChangeTimeline&) = 0;
+ virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0;
protected:
~ComposerCallback() = default;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 36876dc..32f04e5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -38,7 +38,6 @@
#include <utils/Trace.h>
#include "../Layer.h" // needed only for debugging
-#include "../SurfaceFlinger.h"
#include "../SurfaceFlingerProperties.h"
#include "ComposerHal.h"
#include "HWC2.h"
@@ -83,25 +82,22 @@
class ComposerCallbackBridge : public hal::IComposerCallback {
public:
- ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId,
- bool vsyncSwitchingSupported)
- : mCallback(callback),
- mSequenceId(sequenceId),
- mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+ ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported)
+ : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
- Return<void> onHotplug(hal::HWDisplayId display, hal::Connection conn) override {
- mCallback->onHotplugReceived(mSequenceId, display, conn);
+ Return<void> onHotplug(hal::HWDisplayId display, hal::Connection connection) override {
+ mCallback->onComposerHalHotplug(display, connection);
return Void();
}
Return<void> onRefresh(hal::HWDisplayId display) override {
- mCallback->onRefreshReceived(mSequenceId, display);
+ mCallback->onComposerHalRefresh(display);
return Void();
}
Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
if (!mVsyncSwitchingSupported) {
- mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+ mCallback->onComposerHalVsync(display, timestamp, std::nullopt);
} else {
ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
}
@@ -111,8 +107,7 @@
Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
hal::VsyncPeriodNanos vsyncPeriodNanos) override {
if (mVsyncSwitchingSupported) {
- mCallback->onVsyncReceived(mSequenceId, display, timestamp,
- std::make_optional(vsyncPeriodNanos));
+ mCallback->onComposerHalVsync(display, timestamp, vsyncPeriodNanos);
} else {
ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
}
@@ -120,20 +115,18 @@
}
Return<void> onVsyncPeriodTimingChanged(
- hal::HWDisplayId display,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline) override {
- mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, updatedTimeline);
+ hal::HWDisplayId display, const hal::VsyncPeriodChangeTimeline& timeline) override {
+ mCallback->onComposerHalVsyncPeriodTimingChanged(display, timeline);
return Void();
}
Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
- mCallback->onSeamlessPossible(mSequenceId, display);
+ mCallback->onComposerHalSeamlessPossible(display);
return Void();
}
private:
- ComposerCallback* mCallback;
- const int32_t mSequenceId;
+ ComposerCallback* const mCallback;
const bool mVsyncSwitchingSupported;
};
@@ -145,8 +138,9 @@
HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
: mComposer(std::move(composer)),
+ mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))),
mUpdateDeviceProductInfoOnHotplugReconnect(
- android::sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
+ sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
: HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {}
@@ -155,7 +149,7 @@
mDisplayData.clear();
}
-void HWComposer::setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) {
+void HWComposer::setCallback(HWC2::ComposerCallback* callback) {
loadCapabilities();
loadLayerMetadataSupport();
@@ -164,10 +158,9 @@
return;
}
mRegisteredCallback = true;
- sp<ComposerCallbackBridge> callbackBridge(
- new ComposerCallbackBridge(callback, sequenceId,
- mComposer->isVsyncPeriodSwitchSupported()));
- mComposer->registerCallback(callbackBridge);
+
+ mComposer->registerCallback(
+ sp<ComposerCallbackBridge>::make(callback, mComposer->isVsyncPeriodSwitchSupported()));
}
bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
@@ -243,38 +236,49 @@
return true;
}
-std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format) {
- if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
- (width > SurfaceFlinger::maxVirtualDisplaySize ||
- height > SurfaceFlinger::maxVirtualDisplaySize)) {
- ALOGE("%s: Display size %ux%u exceeds maximum dimension of %" PRIu64, __FUNCTION__, width,
- height, SurfaceFlinger::maxVirtualDisplaySize);
- return {};
+size_t HWComposer::getMaxVirtualDisplayCount() const {
+ return mComposer->getMaxVirtualDisplayCount();
+}
+
+size_t HWComposer::getMaxVirtualDisplayDimension() const {
+ return mMaxVirtualDisplayDimension;
+}
+
+bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size resolution,
+ ui::PixelFormat* format,
+ std::optional<PhysicalDisplayId> mirror) {
+ if (!resolution.isValid()) {
+ ALOGE("%s: Invalid resolution %dx%d", __func__, resolution.width, resolution.height);
+ return false;
}
- const auto displayId = mVirtualIdGenerator.nextId();
- if (!displayId) {
- ALOGE("%s: No remaining virtual displays", __FUNCTION__);
- return {};
+ const uint32_t width = static_cast<uint32_t>(resolution.width);
+ const uint32_t height = static_cast<uint32_t>(resolution.height);
+
+ if (mMaxVirtualDisplayDimension > 0 &&
+ (width > mMaxVirtualDisplayDimension || height > mMaxVirtualDisplayDimension)) {
+ ALOGE("%s: Resolution %ux%u exceeds maximum dimension %zu", __func__, width, height,
+ mMaxVirtualDisplayDimension);
+ return false;
}
- hal::HWDisplayId hwcDisplayId = 0;
+ std::optional<hal::HWDisplayId> hwcMirrorId;
+ if (mirror) {
+ hwcMirrorId = fromPhysicalDisplayId(*mirror);
+ }
+
+ hal::HWDisplayId hwcDisplayId;
const auto error = static_cast<hal::Error>(
- mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
- if (error != hal::Error::NONE) {
- ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
- mVirtualIdGenerator.markUnused(*displayId);
- return {};
- }
+ mComposer->createVirtualDisplay(width, height, format, hwcMirrorId, &hwcDisplayId));
+ RETURN_IF_HWC_ERROR_FOR("createVirtualDisplay", error, displayId, false);
auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
hwcDisplayId, hal::DisplayType::VIRTUAL);
display->setConnected(true);
- auto& displayData = mDisplayData[*displayId];
+ auto& displayData = mDisplayData[displayId];
displayData.hwcDisplay = std::move(display);
displayData.isVirtual = true;
- return displayId;
+ return true;
}
void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
@@ -670,13 +674,6 @@
void HWComposer::disconnectDisplay(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
-
- // If this was a virtual display, add its slot back for reuse by future
- // virtual displays
- if (displayData.isVirtual) {
- mVirtualIdGenerator.markUnused(*HalVirtualDisplayId::tryCast(displayId));
- }
-
const auto hwcDisplayId = displayData.hwcDisplay->getId();
// TODO(b/74619554): Select internal/external display from remaining displays.
@@ -983,10 +980,6 @@
}
}
-uint32_t HWComposer::getMaxVirtualDisplayCount() const {
- return mComposer->getMaxVirtualDisplayCount();
-}
-
} // namespace impl
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index d0c0c11..cd6f9f5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -39,7 +39,6 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
-#include "DisplayIdGenerator.h"
#include "DisplayIdentification.h"
#include "DisplayMode.h"
#include "HWC2.h"
@@ -101,7 +100,7 @@
virtual ~HWComposer();
- virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
+ virtual void setCallback(HWC2::ComposerCallback*) = 0;
virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const = 0;
@@ -109,9 +108,16 @@
virtual bool hasCapability(hal::Capability) const = 0;
virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
- // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
- virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat*) = 0;
+ virtual size_t getMaxVirtualDisplayCount() const = 0;
+ virtual size_t getMaxVirtualDisplayDimension() const = 0;
+
+ // Attempts to allocate a virtual display on the HWC. The maximum number of virtual displays
+ // supported by the HWC can be queried in advance, but allocation may fail for other reasons.
+ // For virtualized compositors, the PhysicalDisplayId is a hint that this virtual display is
+ // a mirror of a physical display, and that the screen should be captured by the host rather
+ // than guest compositor.
+ virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
+ std::optional<PhysicalDisplayId> mirror) = 0;
virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
@@ -246,7 +252,7 @@
~HWComposer() override;
- void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
+ void setCallback(HWC2::ComposerCallback*) override;
bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const override;
@@ -254,9 +260,11 @@
bool hasCapability(hal::Capability) const override;
bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
- // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
- std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat*) override;
+ size_t getMaxVirtualDisplayCount() const override;
+ size_t getMaxVirtualDisplayDimension() const override;
+
+ bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
+ std::optional<PhysicalDisplayId>) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
@@ -402,7 +410,6 @@
void loadCapabilities();
void loadLayerMetadataSupport();
- uint32_t getMaxVirtualDisplayCount() const;
std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
@@ -416,8 +423,7 @@
std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
bool mHasMultiDisplaySupport = false;
- RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()};
-
+ const size_t mMaxVirtualDisplayDimension;
const bool mUpdateDeviceProductInfoOnHotplugReconnect;
};
diff --git a/services/surfaceflinger/DisplayIdGenerator.h b/services/surfaceflinger/DisplayIdGenerator.h
index e7c69a8..9791a25 100644
--- a/services/surfaceflinger/DisplayIdGenerator.h
+++ b/services/surfaceflinger/DisplayIdGenerator.h
@@ -27,23 +27,16 @@
namespace android {
-template <typename T>
+// Generates pseudo-random IDs of type GpuVirtualDisplayId or HalVirtualDisplayId.
+template <typename Id>
class DisplayIdGenerator {
public:
- virtual std::optional<T> nextId() = 0;
- virtual void markUnused(T id) = 0;
-
-protected:
- ~DisplayIdGenerator() {}
-};
-
-template <typename T>
-class RandomDisplayIdGenerator final : public DisplayIdGenerator<T> {
-public:
- explicit RandomDisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max())
+ explicit DisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max())
: mMaxIdsCount(maxIdsCount) {}
- std::optional<T> nextId() override {
+ bool inUse() const { return !mUsedIds.empty(); }
+
+ std::optional<Id> generateId() {
if (mUsedIds.size() >= mMaxIdsCount) {
return std::nullopt;
}
@@ -51,8 +44,7 @@
constexpr int kMaxAttempts = 1000;
for (int attempts = 0; attempts < kMaxAttempts; attempts++) {
- const auto baseId = mDistribution(mGenerator);
- const T id(baseId);
+ const Id id{mDistribution(mGenerator)};
if (mUsedIds.count(id) == 0) {
mUsedIds.insert(id);
return id;
@@ -62,14 +54,18 @@
LOG_ALWAYS_FATAL("Couldn't generate ID after %d attempts", kMaxAttempts);
}
- void markUnused(T id) override { mUsedIds.erase(id); }
+ void releaseId(Id id) { mUsedIds.erase(id); }
private:
const size_t mMaxIdsCount;
- std::unordered_set<T> mUsedIds;
+ std::unordered_set<Id> mUsedIds;
+
+ // Pseudo-random with random seed, in contrast to physical display IDs, which are stable
+ // across reboots. The only ISurfaceComposer exposure for these IDs is a restricted API
+ // for screencap, so there is little benefit in making them unpredictable.
std::default_random_engine mGenerator{std::random_device()()};
- std::uniform_int_distribution<typename T::BaseId> mDistribution;
+ std::uniform_int_distribution<typename Id::BaseId> mDistribution;
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index fd18c3b..0cc5f33 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -66,6 +66,7 @@
layerSettings->source.solidColor = getColor().rgb;
results.push_back(*layerSettings);
} else if (hasBlur() || drawShadows()) {
+ layerSettings->skipContentDraw = true;
results.push_back(*layerSettings);
}
@@ -126,7 +127,7 @@
bool EffectLayer::isOpaque(const Layer::State& s) const {
// Consider the layer to be opaque if its opaque flag is set or its effective
// alpha (considering the alpha of its parents as well) is 1.0;
- return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
+ return (s.flags & layer_state_t::eLayerOpaque) != 0 || (fillsColor() && getAlpha() == 1.0_hf);
}
ui::Dataspace EffectLayer::getDataSpace() const {
@@ -147,7 +148,7 @@
}
bool EffectLayer::hasBlur() const {
- return getBackgroundBlurRadius() > 0;
+ return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
}
} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index f19e2a7..c294ff2 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -136,6 +136,10 @@
janks.emplace_back("Unknown jank");
jankType &= ~JankType::Unknown;
}
+ if (jankType & JankType::SurfaceFlingerStuffing) {
+ janks.emplace_back("SurfaceFlinger Stuffing");
+ jankType &= ~JankType::SurfaceFlingerStuffing;
+ }
// jankType should be 0 if all types of jank were checked for.
LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -300,7 +304,7 @@
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer)
+ TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode)
: mToken(frameTimelineInfo.vsyncId),
mInputEventId(frameTimelineInfo.inputEventId),
mOwnerPid(ownerPid),
@@ -315,7 +319,8 @@
mTimeStats(timeStats),
mJankClassificationThresholds(thresholds),
mTraceCookieCounter(*traceCookieCounter),
- mIsBuffer(isBuffer) {}
+ mIsBuffer(isBuffer),
+ mGameMode(gameMode) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::scoped_lock lock(mMutex);
@@ -603,8 +608,8 @@
if (mPredictionState != PredictionState::None) {
// Only update janky frames if the app used vsync predictions
mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
- mJankType, displayDeadlineDelta, displayPresentDelta,
- deadlineDelta});
+ mGameMode, mJankType, displayDeadlineDelta,
+ displayPresentDelta, deadlineDelta});
}
}
@@ -688,6 +693,7 @@
actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
+ actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer);
});
// Actual timeline end
@@ -772,14 +778,14 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName, bool isBuffer) {
+ std::string layerName, std::string debugName, bool isBuffer, int32_t gameMode) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter,
- isBuffer);
+ isBuffer, gameMode);
}
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
@@ -788,13 +794,13 @@
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter, isBuffer);
+ &mTraceCookieCounter, isBuffer, gameMode);
}
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter,
- isBuffer);
+ isBuffer, gameMode);
}
FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 42be55a..15ecf13 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -154,7 +154,7 @@
int32_t layerId, std::string layerName, std::string debugName,
PredictionState predictionState, TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer);
+ TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -259,6 +259,8 @@
// Tells if the SurfaceFrame is representing a buffer or a transaction without a
// buffer(animations)
bool mIsBuffer;
+ // GameMode from the layer. Used in metrics.
+ int32_t mGameMode = 0;
};
/*
@@ -278,7 +280,8 @@
// 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,
- int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) = 0;
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
+ int32_t gameMode) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -437,7 +440,8 @@
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
+ int32_t gameMode) 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,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a7c8704..2bf5602 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -574,7 +574,8 @@
layerSettings.geometry.positionTransform = getTransform().asMatrix4();
// skip drawing content if the targetSettings indicate the content will be occluded
- layerSettings.skipContentDraw = !targetSettings.realContentIsVisible;
+ layerSettings.skipContentDraw =
+ layerSettings.skipContentDraw || !targetSettings.realContentIsVisible;
if (hasColorTransform()) {
layerSettings.colorTransform = getColorTransform();
@@ -586,11 +587,24 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
- if (!targetSettings.disableBlurs) {
- layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
- layerSettings.blurRegions = getBlurRegions();
- layerSettings.blurRegionTransform =
- getActiveTransform(getDrawingState()).inverse().asMatrix4();
+ switch (targetSettings.blurSetting) {
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
+ layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+ layerSettings.blurRegions = getBlurRegions();
+ layerSettings.blurRegionTransform =
+ getActiveTransform(getDrawingState()).inverse().asMatrix4();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
+ layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
+ layerSettings.blurRegions = getBlurRegions();
+ layerSettings.blurRegionTransform =
+ getActiveTransform(getDrawingState()).inverse().asMatrix4();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
+ default:
+ break;
}
layerSettings.stretchEffect = getStretchEffect();
// Record the name of the layer for debugging further down the stack.
@@ -1343,7 +1357,7 @@
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
mTransactionName,
- /*isBuffer*/ false);
+ /*isBuffer*/ false, getGameMode());
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
surfaceFrame->setAcquireFenceTime(postTime);
@@ -1360,7 +1374,7 @@
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, getGameMode());
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -1621,7 +1635,8 @@
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
- getName().c_str(), mOwnerUid, newTimestamps->postedTime);
+ getName().c_str(), mOwnerUid, newTimestamps->postedTime,
+ getGameMode());
mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
newTimestamps->acquireFence);
}
@@ -1651,12 +1666,25 @@
return count;
}
+void Layer::setGameModeForTree(int parentGameMode) {
+ int gameMode = parentGameMode;
+ auto& currentState = getCurrentState();
+ if (currentState.metadata.has(METADATA_GAME_MODE)) {
+ gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0);
+ }
+ setGameMode(gameMode);
+ for (const sp<Layer>& child : mCurrentChildren) {
+ child->setGameModeForTree(gameMode);
+ }
+}
+
void Layer::addChild(const sp<Layer>& layer) {
mChildrenChanged = true;
setTransactionFlags(eTransactionNeeded);
mCurrentChildren.add(layer);
layer->setParent(this);
+ layer->setGameModeForTree(mGameMode);
updateTreeHasFrameRateVote();
}
@@ -1668,6 +1696,7 @@
const auto removeResult = mCurrentChildren.remove(layer);
updateTreeHasFrameRateVote();
+ layer->setGameModeForTree(0);
layer->updateTreeHasFrameRateVote();
return removeResult;
@@ -2353,6 +2382,16 @@
info.touchableRegion = inputTransform.transform(info.touchableRegion);
}
+void Layer::fillTouchOcclusionMode(InputWindowInfo& info) {
+ sp<Layer> p = this;
+ while (p != nullptr && !p->hasInputInfo()) {
+ p = p->mDrawingParent.promote();
+ }
+ if (p != nullptr) {
+ info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
+ }
+}
+
InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
@@ -2390,6 +2429,7 @@
// anything.
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
info.alpha = getAlpha();
+ fillTouchOcclusionMode(info);
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 66d7018..5873103 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -847,6 +847,13 @@
*/
bool hasInputInfo() const;
+ // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied
+ // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with
+ // its children) that has the metadata set will use the gameMode from the metadata.
+ void setGameModeForTree(int32_t parentGameMode);
+ void setGameMode(int32_t gameMode) { mGameMode = gameMode; };
+ int32_t getGameMode() const { return mGameMode; }
+
virtual uid_t getOwnerUid() const { return mOwnerUid; }
pid_t getOwnerPid() { return mOwnerPid; }
@@ -1050,6 +1057,10 @@
// null.
sp<Layer> getRootLayer();
+ // Fills in the touch occlusion mode of the first parent (including this layer) that
+ // hasInputInfo() or no-op if no such parent is found.
+ void fillTouchOcclusionMode(InputWindowInfo& info);
+
// Fills in the frame and transform info for the InputWindowInfo
void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
@@ -1089,6 +1100,10 @@
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
float mEffectiveShadowRadius = 0.f;
+ // Game mode for the layer. Set by WindowManagerShell, game mode is used in
+ // metrics(SurfaceFlingerStats).
+ int32_t mGameMode = 0;
+
// A list of regions on this layer that should have blurs.
const std::vector<BlurRegion> getBlurRegions() const;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b062acd..9746076 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -190,6 +190,45 @@
RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
const GlobalSignals& globalSignals,
GlobalSignals* outSignalsConsidered) const {
+ std::lock_guard lock(mLock);
+
+ if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) {
+ return *cached;
+ }
+
+ GlobalSignals signalsConsidered;
+ RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered);
+ lastBestRefreshRateInvocation.emplace(
+ GetBestRefreshRateInvocation{.layerRequirements = layers,
+ .globalSignals = globalSignals,
+ .outSignalsConsidered = signalsConsidered,
+ .resultingBestRefreshRate = result});
+ if (outSignalsConsidered) {
+ *outSignalsConsidered = signalsConsidered;
+ }
+ return result;
+}
+
+std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
+ const bool sameAsLastCall = lastBestRefreshRateInvocation &&
+ lastBestRefreshRateInvocation->layerRequirements == layers &&
+ lastBestRefreshRateInvocation->globalSignals == globalSignals;
+
+ if (sameAsLastCall) {
+ if (outSignalsConsidered) {
+ *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered;
+ }
+ return lastBestRefreshRateInvocation->resultingBestRefreshRate;
+ }
+
+ return {};
+}
+
+RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -206,8 +245,6 @@
}
};
- std::lock_guard lock(mLock);
-
int noVoteLayers = 0;
int minVoteLayers = 0;
int maxVoteLayers = 0;
@@ -592,6 +629,11 @@
void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
std::lock_guard lock(mLock);
+
+ // Invalidate the cached invocation to getBestRefreshRate. This forces
+ // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+ lastBestRefreshRateInvocation.reset();
+
mCurrentRefreshRate = mRefreshRates.at(modeId).get();
}
@@ -605,11 +647,16 @@
void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
DisplayModeId currentModeId) {
std::lock_guard lock(mLock);
+
// The current mode should be supported
LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
return mode->getId() == currentModeId;
}));
+ // Invalidate the cached invocation to getBestRefreshRate. This forces
+ // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+ lastBestRefreshRateInvocation.reset();
+
mRefreshRates.clear();
for (const auto& mode : modes) {
const auto modeId = mode->getId();
@@ -666,6 +713,7 @@
ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
+ lastBestRefreshRateInvocation.reset();
Policy previousPolicy = *getCurrentPolicyLocked();
mDisplayManagerPolicy = policy;
if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -680,6 +728,7 @@
if (policy && !isPolicyValidLocked(*policy)) {
return BAD_VALUE;
}
+ lastBestRefreshRateInvocation.reset();
Policy previousPolicy = *getCurrentPolicyLocked();
mOverridePolicy = policy;
if (*getCurrentPolicyLocked() == previousPolicy) {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ee89149..342fde0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -250,6 +250,10 @@
bool touch = false;
// True if the system hasn't seen any buffers posted to layers recently.
bool idle = false;
+
+ bool operator==(const GlobalSignals& other) const {
+ return touch == other.touch && idle == other.idle;
+ }
};
// Returns the refresh rate that fits best to the given layers.
@@ -350,6 +354,15 @@
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
+ std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const
+ REQUIRES(mLock);
+
+ RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
+
// Returns the refresh rate with the highest score in the collection specified from begin
// to end. If there are more than one with the same highest refresh rate, the first one is
// returned.
@@ -414,6 +427,15 @@
const bool mEnableFrameRateOverride;
bool mSupportsFrameRateOverride;
+
+ struct GetBestRefreshRateInvocation {
+ std::vector<LayerRequirement> layerRequirements;
+ GlobalSignals globalSignals;
+ GlobalSignals outSignalsConsidered;
+ RefreshRate resultingBestRefreshRate;
+ };
+ mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
+ GUARDED_BY(mLock);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 80f4665..208a767 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -61,6 +61,7 @@
if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
return;
}
+ mTimeStats.incrementRefreshRateSwitches();
flushTime();
mCurrentRefreshRate = currRefreshRate;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index fac2c65..4b8cbfb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -194,8 +194,6 @@
std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
const scheduler::RefreshRateConfigs& configs) {
- if (!configs.canSwitch()) return nullptr;
-
return std::make_unique<scheduler::LayerHistory>(configs);
}
@@ -579,8 +577,6 @@
}
void Scheduler::registerLayer(Layer* layer) {
- if (!mLayerHistory) return;
-
scheduler::LayerHistory::LayerVoteType voteType;
if (!mOptions.useContentDetection ||
@@ -600,26 +596,22 @@
}
void Scheduler::deregisterLayer(Layer* layer) {
- if (mLayerHistory) {
- mLayerHistory->deregisterLayer(layer);
- }
+ mLayerHistory->deregisterLayer(layer);
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
- if (mLayerHistory) {
+ if (mRefreshRateConfigs.canSwitch()) {
mLayerHistory->record(layer, presentTime, systemTime(), updateType);
}
}
void Scheduler::setModeChangePending(bool pending) {
- if (mLayerHistory) {
- mLayerHistory->setModeChangePending(pending);
- }
+ mLayerHistory->setModeChangePending(pending);
}
void Scheduler::chooseRefreshRateForContent() {
- if (!mLayerHistory) return;
+ if (!mRefreshRateConfigs.canSwitch()) return;
ATRACE_CALL();
@@ -630,9 +622,6 @@
bool frameRateOverridesChanged;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRequirements == summary) {
- return;
- }
mFeatures.contentRequirements = summary;
newModeId = calculateRefreshRateModeId(&consideredSignals);
@@ -691,9 +680,7 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- if (mLayerHistory) {
- mLayerHistory->clear();
- }
+ mLayerHistory->clear();
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -732,9 +719,7 @@
// NOTE: Instead of checking all the layers, we should be checking the layer
// that is currently on top. b/142507166 will give us this capability.
if (handleTimerStateChanged(&mFeatures.touch, touch)) {
- if (mLayerHistory) {
- mLayerHistory->clear();
- }
+ mLayerHistory->clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
@@ -908,9 +893,7 @@
}
void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
- if (mLayerHistory) {
- mLayerHistory->setDisplayArea(displayArea);
- }
+ mLayerHistory->setDisplayArea(displayArea);
}
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 49d3d93..30a3253 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -268,7 +268,7 @@
VsyncSchedule mVsyncSchedule;
// Used to choose refresh rate if content detection is enabled.
- const std::unique_ptr<LayerHistory> mLayerHistory;
+ std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 028f7a6..329e4a0 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -86,6 +86,9 @@
// in the learning phase we should just clear all timestamps and start
// over.
if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+ // Add the timestamp to mTimestamps before clearing it so we could
+ // update mKnownTimestamp based on the new timestamp.
+ mTimestamps.push_back(timestamp);
clearTimestamps();
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 881ee5b..c1d28b1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -63,6 +63,7 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
+#include <processgroup/processgroup.h>
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
@@ -295,7 +296,6 @@
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
-uint64_t SurfaceFlinger::maxVirtualDisplaySize;
bool SurfaceFlinger::hasSyncFramework;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
uint32_t SurfaceFlinger::maxGraphicsWidth;
@@ -358,8 +358,6 @@
useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
- maxVirtualDisplaySize = max_virtual_display_dimension(0);
-
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
@@ -435,10 +433,6 @@
ALOGI_IF(mPropagateBackpressureClientComposition,
"Enabling backpressure propagation for Client Composition");
- property_get("debug.sf.enable_hwc_vds", value, "0");
- mUseHwcVirtualDisplays = atoi(value);
- ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
-
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlurs = atoi(value);
mSupportsBlur = supportsBlurs;
@@ -579,6 +573,59 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
+void SurfaceFlinger::enableHalVirtualDisplays(bool enable) {
+ auto& generator = mVirtualDisplayIdGenerators.hal;
+ if (!generator && enable) {
+ ALOGI("Enabling HAL virtual displays");
+ generator.emplace(getHwComposer().getMaxVirtualDisplayCount());
+ } else if (generator && !enable) {
+ ALOGW_IF(generator->inUse(), "Disabling HAL virtual displays while in use");
+ generator.reset();
+ }
+}
+
+VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
+ ui::LayerStack layerStack) {
+ if (auto& generator = mVirtualDisplayIdGenerators.hal) {
+ if (const auto id = generator->generateId()) {
+ std::optional<PhysicalDisplayId> mirror;
+
+ if (const auto display = findDisplay([layerStack](const auto& display) {
+ return !display.isVirtual() && display.getLayerStack() == layerStack;
+ })) {
+ mirror = display->getPhysicalId();
+ }
+
+ if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format, mirror)) {
+ return *id;
+ }
+
+ generator->releaseId(*id);
+ } else {
+ ALOGW("%s: Exhausted HAL virtual displays", __func__);
+ }
+
+ ALOGW("%s: Falling back to GPU virtual display", __func__);
+ }
+
+ const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
+ LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
+ return *id;
+}
+
+void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) {
+ if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
+ if (auto& generator = mVirtualDisplayIdGenerators.hal) {
+ generator->releaseId(*id);
+ }
+ return;
+ }
+
+ const auto id = GpuVirtualDisplayId::tryCast(displayId);
+ LOG_ALWAYS_FATAL_IF(!id);
+ mVirtualDisplayIdGenerators.gpu.releaseId(*id);
+}
+
std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const {
Mutex::Autolock lock(mStateLock);
@@ -634,6 +681,10 @@
if (mStartPropertySetThread->join() != NO_ERROR) {
ALOGE("Join StartPropertySetThread failed!");
}
+
+ if (mRenderEnginePrimeCacheFuture.valid()) {
+ mRenderEnginePrimeCacheFuture.get();
+ }
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -735,10 +786,21 @@
? renderengine::RenderEngine::ContextPriority::REALTIME
: renderengine::RenderEngine::ContextPriority::MEDIUM)
.build()));
+
+ // Set SF main policy after initializing RenderEngine which has its own policy.
+ if (!SetTaskProfiles(0, {"SFMainPolicy"})) {
+ ALOGW("Failed to set main task profile");
+ }
+
mCompositionEngine->setTimeStats(mTimeStats);
mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
- mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
+ mCompositionEngine->getHwComposer().setCallback(this);
ClientCache::getInstance().setRenderEngine(&getRenderEngine());
+
+ if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
+ enableHalVirtualDisplays(true);
+ }
+
// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();
const auto display = getDefaultDisplayDeviceLocked();
@@ -756,7 +818,15 @@
char primeShaderCache[PROPERTY_VALUE_MAX];
property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
if (atoi(primeShaderCache)) {
- getRenderEngine().primeCache();
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Can't set SCHED_OTHER for primeCache");
+ }
+
+ mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache();
+
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Can't set SCHED_OTHER for primeCache");
+ }
}
getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
@@ -789,11 +859,6 @@
property_get("persist.sys.sf.color_mode", value, "0");
mForceColorMode = static_cast<ColorMode>(atoi(value));
-
- property_get("persist.sys.sf.disable_blurs", value, "0");
- bool disableBlurs = atoi(value);
- mDisableBlurs = disableBlurs;
- ALOGI_IF(disableBlurs, "Disabling blur effects, user preference.");
}
void SurfaceFlinger::startBootAnim() {
@@ -1034,10 +1099,6 @@
updatePhaseConfiguration(refreshRate.getFps());
mScheduler->setModeChangePending(true);
}
-
- if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(refreshRate.getFps());
- }
}
status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
@@ -1097,7 +1158,18 @@
// have been already updated with the upcoming active mode.
return;
}
- const Fps oldRefreshRate = display->getActiveMode()->getFps();
+
+ if (display->getActiveMode()->getSize() != upcomingMode->getSize()) {
+ auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
+ // We need to generate new sequenceId in order to recreate the display (and this
+ // way the framebuffer).
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.physical->activeMode = upcomingMode;
+ processDisplayChangesLocked();
+
+ // processDisplayChangesLocked will update all necessary components so we're done here.
+ return;
+ }
std::lock_guard<std::mutex> lock(mActiveModeLock);
mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
@@ -1107,9 +1179,6 @@
mRefreshRateStats->setRefreshRate(refreshRate);
- if (!refreshRate.equalsWithMargin(oldRefreshRate)) {
- mTimeStats->incrementRefreshRateSwitches();
- }
updatePhaseConfiguration(refreshRate);
ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
@@ -1195,6 +1264,10 @@
}
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->changeRefreshRate(desiredMode->getFps());
+ }
+
// Scheduler will submit an empty frame to HWC if needed.
mSetActiveModePending = true;
}
@@ -1617,16 +1690,11 @@
return 0;
}
-void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
- int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- ATRACE_NAME("SF onVsync");
+void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
+ ATRACE_CALL();
Mutex::Autolock lock(mStateLock);
- // Ignore any vsyncs from a previous hardware composer.
- if (sequenceId != getBE().mComposerSequenceId) {
- return;
- }
if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId)) {
auto token = getPhysicalDisplayTokenLocked(*displayId);
@@ -1677,16 +1745,11 @@
setDesiredActiveMode({refreshRate.getModeId(), event});
}
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) {
- ALOGI("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
+void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
+ hal::Connection connection) {
+ ALOGI("%s(%" PRIu64 ", %s)", __func__, hwcDisplayId,
connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
- // Ignore events that do not have the right sequenceId.
- if (sequenceId != getBE().mComposerSequenceId) {
- return;
- }
-
// Only lock if we're not on the main thread. This function is normally
// called on a hwbinder thread, but for the primary display it's called on
// the main thread with the state lock already held, so don't attempt to
@@ -1703,26 +1766,19 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
- int32_t sequenceId, hal::HWDisplayId /*display*/,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline) {
+void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
+ hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline& timeline) {
Mutex::Autolock lock(mStateLock);
- if (sequenceId != getBE().mComposerSequenceId) {
- return;
- }
- mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
+ mScheduler->onNewVsyncPeriodChangeTimeline(timeline);
}
-void SurfaceFlinger::onSeamlessPossible(int32_t /*sequenceId*/, hal::HWDisplayId /*display*/) {
+void SurfaceFlinger::onComposerHalSeamlessPossible(hal::HWDisplayId) {
// TODO(b/142753666): use constraints when calling to setActiveModeWithConstraints and
// use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
}
-void SurfaceFlinger::onRefreshReceived(int sequenceId, hal::HWDisplayId /*hwcDisplayId*/) {
+void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) {
Mutex::Autolock lock(mStateLock);
- if (sequenceId != getBE().mComposerSequenceId) {
- return;
- }
repaintEverythingForHWC();
}
@@ -2011,6 +2067,7 @@
}
refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+ refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
mGeometryInvalid = false;
@@ -2217,11 +2274,10 @@
mTunnelModeEnabledReporter->updateTunnelModeStatus();
}
hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
- for (auto& [key, value] : mHdrLayerInfoListeners) {
- if (value && value->hasListeners()) {
- auto listenersDisplay = getDisplayById(key);
- if (listenersDisplay) {
- hdrInfoListeners.emplace_back(listenersDisplay->getCompositionDisplay(), value);
+ for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
+ if (reporter && reporter->hasListeners()) {
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
}
}
}
@@ -2647,10 +2703,10 @@
ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
status = state.surface->query(NATIVE_WINDOW_HEIGHT, &resolution.height);
ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
- int intPixelFormat;
- status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
+ int format;
+ status = state.surface->query(NATIVE_WINDOW_FORMAT, &format);
ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
- pixelFormat = static_cast<ui::PixelFormat>(intPixelFormat);
+ pixelFormat = static_cast<ui::PixelFormat>(format);
} else {
// Virtual displays without a surface are dormant:
// they have external state (layer stack, projection,
@@ -2660,17 +2716,18 @@
compositionengine::DisplayCreationArgsBuilder builder;
if (const auto& physical = state.physical) {
- builder.setPhysical({physical->id, physical->type});
+ builder.setId(physical->id);
+ builder.setConnectionType(physical->type);
+ } else {
+ builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.layerStack));
}
+
builder.setPixels(resolution);
- builder.setPixelFormat(pixelFormat);
builder.setIsSecure(state.isSecure);
builder.setLayerStackId(state.layerStack);
builder.setPowerAdvisor(&mPowerAdvisor);
- builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays);
- builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
builder.setName(state.displayName);
- const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+ auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled);
sp<compositionengine::DisplaySurface> displaySurface;
@@ -2679,33 +2736,30 @@
sp<IGraphicBufferConsumer> bqConsumer;
getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
- DisplayId displayId = compositionDisplay->getId();
-
if (state.isVirtual()) {
- const auto virtualId = VirtualDisplayId::tryCast(displayId);
- LOG_FATAL_IF(!virtualId);
- sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(getHwComposer(), *virtualId, state.surface, bqProducer,
- bqConsumer, state.displayName);
-
- displaySurface = vds;
- producer = vds;
+ const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());
+ LOG_FATAL_IF(!displayId);
+ auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,
+ bqProducer, bqConsumer, state.displayName);
+ displaySurface = surface;
+ producer = std::move(surface);
} else {
ALOGE_IF(state.surface != nullptr,
"adding a supported display, but rendering "
"surface is provided (%p), ignoring it",
state.surface.get());
- const auto physicalId = PhysicalDisplayId::tryCast(displayId);
- LOG_FATAL_IF(!physicalId);
- displaySurface = new FramebufferSurface(getHwComposer(), *physicalId, bqConsumer,
- state.physical->activeMode->getSize(),
- ui::Size(maxGraphicsWidth, maxGraphicsHeight));
+ const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());
+ LOG_FATAL_IF(!displayId);
+ displaySurface =
+ sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
+ state.physical->activeMode->getSize(),
+ ui::Size(maxGraphicsWidth, maxGraphicsHeight));
producer = bqProducer;
}
LOG_FATAL_IF(!displaySurface);
- const auto display = setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
- displaySurface, producer);
+ const auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay),
+ state, displaySurface, producer);
mDisplays.emplace(displayToken, display);
if (!state.isVirtual()) {
dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
@@ -2721,7 +2775,10 @@
auto display = getDisplayDeviceLocked(displayToken);
if (display) {
display->disconnect();
- if (!display->isVirtual()) {
+
+ if (display->isVirtual()) {
+ releaseVirtualDisplay(display->getVirtualId());
+ } else {
dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
}
}
@@ -2750,17 +2807,26 @@
const DisplayDeviceState& drawingState) {
const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
+
+ // Recreate the DisplayDevice if the surface or sequence ID changed.
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
- // changing the surface is like destroying and recreating the DisplayDevice
getRenderEngine().cleanFramebufferCache();
+
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
+ if (display->isVirtual()) {
+ releaseVirtualDisplay(display->getVirtualId());
+ }
}
+
mDisplays.erase(displayToken);
+
if (const auto& physical = currentState.physical) {
getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id);
}
+
processDisplayAdded(displayToken, currentState);
+
if (currentState.physical) {
const auto display = getDisplayDeviceLocked(displayToken);
setPowerModeInternal(display, hal::PowerMode::ON);
@@ -2770,7 +2836,10 @@
mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
currentState.physical->activeMode->getId());
mVsyncConfiguration->reset();
- updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate().getFps());
+ const Fps refreshRate = currentState.physical->activeMode->getFps();
+ updatePhaseConfiguration(refreshRate);
+ mRefreshRateStats->setRefreshRate(refreshRate);
+
if (mRefreshRateOverlay) {
mRefreshRateOverlay->reset();
}
@@ -2856,6 +2925,7 @@
// Commit layer transactions. This needs to happen after display transactions are
// committed because some geometry logic relies on display orientation.
if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
+ mForceTraversal = false;
mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags && !displayTransactionNeeded) return;
@@ -3953,7 +4023,7 @@
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
+ if (what & layer_state_t::eBackgroundBlurRadiusChanged && mSupportsBlur) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eBlurRegionsChanged) {
@@ -4018,6 +4088,13 @@
std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
+ auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1);
+ if (gameMode != -1) {
+ // The transaction will be received on the Task layer and needs to be applied to all
+ // child layers. Child layers that are added at a later point will obtain the game mode
+ // info through addChild().
+ layer->setGameModeForTree(gameMode);
+ }
if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -4429,6 +4506,11 @@
}
const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
if (currentMode == hal::PowerMode::OFF) {
+ // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
+ // We can merge the syscall later.
+ if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
+ ALOGW("Couldn't set uclamp.min on display on: %s\n", strerror(errno));
+ }
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
@@ -4447,6 +4529,9 @@
if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
}
+ if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
+ ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
+ }
if (display->isPrimary() && currentMode != hal::PowerMode::DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
@@ -4627,7 +4712,8 @@
StringAppendF(&result, " PRESENT_TIME_OFFSET=%" PRId64, dispSyncPresentTimeOffset);
StringAppendF(&result, " FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
- StringAppendF(&result, " MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
+ StringAppendF(&result, " MAX_VIRT_DISPLAY_DIM=%zu",
+ getHwComposer().getMaxVirtualDisplayDimension());
StringAppendF(&result, " RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
StringAppendF(&result, " NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
maxFrameBufferAcquiredBuffers);
@@ -5387,8 +5473,8 @@
return NO_ERROR;
}
case 1021: { // Disable HWC virtual displays
- n = data.readInt32();
- mUseHwcVirtualDisplays = !n;
+ const bool enable = data.readInt32() != 0;
+ static_cast<void>(schedule([this, enable] { enableHalVirtualDisplays(enable); }));
return NO_ERROR;
}
case 1022: { // Set saturation boost
@@ -5588,7 +5674,7 @@
Mutex::Autolock lock(mStateLock);
hwcId = getHwComposer().getInternalHwcDisplayId();
}
- onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
+ onComposerHalHotplug(*hwcId, hal::Connection::CONNECTED);
return NO_ERROR;
}
// Modify the max number of display frames stored within FrameTimeline
@@ -5795,35 +5881,45 @@
if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
return -errno;
}
+
return NO_ERROR;
}
-sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
- if (const sp<IBinder> displayToken =
- getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
- return getDisplayDeviceLocked(displayToken);
- }
- // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
- // may not have a displayId.
- return getDisplayByLayerStack(displayOrLayerStack);
-}
+status_t SurfaceFlinger::setSchedAttr(bool enabled) {
+ static const unsigned int kUclampMin =
+ base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min", 0U);
-sp<DisplayDevice> SurfaceFlinger::getDisplayById(DisplayId displayId) const {
- for (const auto& [token, display] : mDisplays) {
- if (display->getId() == displayId) {
- return display;
- }
+ if (!kUclampMin) {
+ // uclamp.min set to 0 (default), skip setting
+ return NO_ERROR;
}
- return nullptr;
-}
-sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
- for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == layerStack) {
- return display;
- }
+ // Currently, there is no wrapper in bionic: b/183240349.
+ struct sched_attr {
+ uint32_t size;
+ uint32_t sched_policy;
+ uint64_t sched_flags;
+ int32_t sched_nice;
+ uint32_t sched_priority;
+ uint64_t sched_runtime;
+ uint64_t sched_deadline;
+ uint64_t sched_period;
+ uint32_t sched_util_min;
+ uint32_t sched_util_max;
+ };
+
+ sched_attr attr = {};
+ attr.size = sizeof(attr);
+
+ attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP);
+ attr.sched_util_min = enabled ? kUclampMin : 0;
+ attr.sched_util_max = 1024;
+
+ if (syscall(__NR_sched_setattr, 0, &attr, 0)) {
+ return -errno;
}
- return nullptr;
+
+ return NO_ERROR;
}
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
@@ -5837,7 +5933,7 @@
if (!args.displayToken) return BAD_VALUE;
- wp<DisplayDevice> displayWeak;
+ wp<const DisplayDevice> displayWeak;
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
ui::Dataspace dataspace;
@@ -5878,18 +5974,26 @@
captureListener);
}
-status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
+status_t SurfaceFlinger::captureDisplay(uint64_t displayIdOrLayerStack,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
- wp<DisplayDevice> displayWeak;
+ wp<const DisplayDevice> displayWeak;
ui::Size size;
ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
- sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ auto display = getDisplayDeviceLocked(PhysicalDisplayId{displayIdOrLayerStack});
+
+ // Fall back to first display whose layer stack matches.
+ if (!display) {
+ const auto layerStack = static_cast<ui::LayerStack>(displayIdOrLayerStack);
+ display = findDisplay(WithLayerStack(layerStack));
+ }
+
if (!display) {
return NAME_NOT_FOUND;
}
+
layerStack = display->getLayerStack();
displayWeak = display;
@@ -5977,7 +6081,7 @@
}
}
- const auto display = getDisplayByLayerStack(parent->getLayerStack());
+ const auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
if (!display) {
return NAME_NOT_FOUND;
}
@@ -6200,9 +6304,12 @@
clearRegion,
layerStackSpaceRect,
clientCompositionDisplay.outputDataspace,
- true, /* realContentIsVisible */
+ true, /* realContentIsVisible */
false, /* clearContent */
- disableBlurs,
+ disableBlurs ? compositionengine::LayerFE::ClientCompositionTargetSettings::
+ BlurSetting::Disabled
+ : compositionengine::LayerFE::ClientCompositionTargetSettings::
+ BlurSetting::Enabled,
};
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c382005..a3fa8d6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -170,11 +170,6 @@
};
mutable Mutex mBufferingStatsMutex;
std::unordered_map<std::string, BufferingStats> mBufferingStats;
-
- // The composer sequence id is a monotonically increasing integer that we
- // use to differentiate callbacks from different hardware composer
- // instances. Each hardware composer instance gets a different sequence id.
- int32_t mComposerSequenceId = 0;
};
class SurfaceFlinger : public BnSurfaceComposer,
@@ -191,6 +186,9 @@
// set main thread scheduling policy
static status_t setSchedFifo(bool enabled) ANDROID_API;
+ // set main thread scheduling attributes
+ static status_t setSchedAttr(bool enabled);
+
static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
// This is the phase offset in nanoseconds of the software vsync event
@@ -229,10 +227,6 @@
// GL composition.
static bool useHwcForRgbToYuv;
- // Maximum dimension supported by HWC for virtual display.
- // Equal to min(max_height, max_width).
- static uint64_t maxVirtualDisplaySize;
-
// Controls the number of buffers SurfaceFlinger will allocate for use in
// FramebufferSurface
static int64_t maxFrameBufferAcquiredBuffers;
@@ -722,18 +716,14 @@
// Implements RefBase.
void onFirstRef() override;
- /*
- * HWC2::ComposerCallback / HWComposer::EventHandler interface
- */
- void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos> vsyncPeriod) override;
- void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) override;
- void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId) override;
- void onVsyncPeriodTimingChangedReceived(
- int32_t sequenceId, hal::HWDisplayId display,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline) override;
- void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override;
+ // HWC2::ComposerCallback overrides:
+ void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos>) override;
+ void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
+ void onComposerHalRefresh(hal::HWDisplayId) override;
+ void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
+ const hal::VsyncPeriodChangeTimeline&) override;
+ void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
/*
* ISchedulerCallback
@@ -924,10 +914,6 @@
bool canCaptureBlackoutContent, bool regionSampling,
bool grayscale, ScreenCaptureResults&);
- sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
- sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
- sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
-
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
@@ -953,6 +939,14 @@
return it == mDisplays.end() ? nullptr : it->second;
}
+ sp<const DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) const
+ REQUIRES(mStateLock) {
+ if (const auto token = getPhysicalDisplayTokenLocked(id)) {
+ return getDisplayDeviceLocked(token);
+ }
+ return nullptr;
+ }
+
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
}
@@ -969,6 +963,20 @@
return getDefaultDisplayDeviceLocked();
}
+ // Returns the first display that matches a `bool(const DisplayDevice&)` predicate.
+ template <typename Predicate>
+ sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
+ const auto it = std::find_if(mDisplays.begin(), mDisplays.end(),
+ [&](const auto& pair) { return p(*pair.second); });
+
+ return it == mDisplays.end() ? nullptr : it->second;
+ }
+
+ sp<const DisplayDevice> getDisplayDeviceLocked(DisplayId id) const REQUIRES(mStateLock) {
+ // TODO(b/182939859): Replace tokens with IDs for display lookup.
+ return findDisplay([id](const auto& display) { return display.getId() == id; });
+ }
+
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
@@ -1086,6 +1094,14 @@
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
+ // Toggles use of HAL/GPU virtual displays.
+ void enableHalVirtualDisplays(bool);
+
+ // Virtual display lifecycle for ID generation and HAL allocation.
+ VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, ui::LayerStack)
+ REQUIRES(mStateLock);
+ void releaseVirtualDisplay(VirtualDisplayId);
+
/*
* Debugging & dumpsys
*/
@@ -1167,6 +1183,8 @@
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
+ std::future<void> mRenderEnginePrimeCacheFuture;
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
@@ -1238,7 +1256,10 @@
std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
GUARDED_BY(mStateLock);
- RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
+ struct {
+ DisplayIdGenerator<GpuVirtualDisplayId> gpu;
+ std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
+ } mVirtualDisplayIdGenerators;
std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
@@ -1261,11 +1282,9 @@
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline;
- bool mUseHwcVirtualDisplays = false;
+
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
- // Disable blurs, for debugging
- std::atomic<bool> mDisableBlurs = false;
// If blurs are considered expensive and should require high GPU frequency.
bool mBlursAreExpensive = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index d6a0787..f1b153f 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -58,6 +58,21 @@
return histogramProto;
}
+SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(int32_t gameMode) {
+ switch (gameMode) {
+ case TimeStatsHelper::GameModeUnsupported:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSUPPORTED;
+ case TimeStatsHelper::GameModeStandard:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD;
+ case TimeStatsHelper::GameModePerformance:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
+ case TimeStatsHelper::GameModeBattery:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
+ default:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
+ }
+}
+
SurfaceflingerStatsLayerInfo_SetFrameRateVote frameRateVoteToProto(
const TimeStats::SetFrameRateVote& setFrameRateVote) {
using FrameRateCompatibilityEnum =
@@ -206,6 +221,7 @@
*atom->mutable_app_deadline_misses() =
histogramToProto(layer->deltas["appDeadlineDeltas"].hist,
mMaxPulledHistogramBuckets);
+ atom->set_game_mode(gameModeToProto(layer->gameMode));
}
// Always clear data.
@@ -437,7 +453,8 @@
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) {
+ SetFrameRateVote frameRateVote,
+ int32_t gameMode) {
ATRACE_CALL();
ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
@@ -464,12 +481,13 @@
TimeStatsHelper::TimelineStats& displayStats = mTimeStats.stats[timelineKey];
- TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+ TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName, gameMode};
if (!displayStats.stats.count(layerKey)) {
displayStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
displayStats.stats[layerKey].renderRateBucket = renderRateBucket;
displayStats.stats[layerKey].uid = uid;
displayStats.stats[layerKey].layerName = layerName;
+ displayStats.stats[layerKey].gameMode = gameMode;
}
if (frameRateVote.frameRate > 0.0f) {
displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
@@ -535,10 +553,11 @@
layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
}
-bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName,
+ int32_t gameMode) {
uint32_t layerRecords = 0;
for (const auto& record : mTimeStats.stats) {
- if (record.second.stats.count({uid, layerName}) > 0) {
+ if (record.second.stats.count({uid, layerName, gameMode}) > 0) {
return true;
}
@@ -549,7 +568,7 @@
}
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime) {
+ uid_t uid, nsecs_t postTime, int32_t gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -557,13 +576,14 @@
postTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!canAddNewAggregatedStats(uid, layerName)) {
+ if (!canAddNewAggregatedStats(uid, layerName, gameMode)) {
return;
}
if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
layerNameIsValid(layerName)) {
mTimeStatsTracker[layerId].uid = uid;
mTimeStatsTracker[layerId].layerName = layerName;
+ mTimeStatsTracker[layerId].gameMode = gameMode;
}
if (!mTimeStatsTracker.count(layerId)) return;
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
@@ -698,7 +718,7 @@
void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) {
+ SetFrameRateVote frameRateVote, int32_t gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -717,13 +737,14 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote,
+ gameMode);
}
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) {
+ SetFrameRateVote frameRateVote, int32_t gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -743,7 +764,8 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote,
+ gameMode);
}
static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
@@ -801,6 +823,7 @@
// the first jank record is not dropped.
static const std::string kDefaultLayerName = "none";
+ static constexpr int32_t kDefaultGameMode = TimeStatsHelper::GameModeUnsupported;
const int32_t refreshRateBucket =
clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -817,13 +840,14 @@
updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, info.reasons);
- TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName};
+ TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName, info.gameMode};
if (!timelineStats.stats.count(layerKey)) {
- layerKey = {info.uid, kDefaultLayerName};
+ layerKey = {info.uid, kDefaultLayerName, kDefaultGameMode};
timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
timelineStats.stats[layerKey].uid = info.uid;
- timelineStats.stats[layerKey].layerName = kDefaultLayerName;
+ timelineStats.stats[layerKey].layerName = kDefaultGameMode;
+ timelineStats.stats[layerKey].gameMode = info.gameMode;
}
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5b0f5bd..dd48950 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -87,7 +87,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime) = 0;
+ uid_t uid, nsecs_t postTime, int32_t gameMode) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -109,11 +109,11 @@
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) = 0;
+ SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) = 0;
+ SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -131,6 +131,7 @@
std::optional<Fps> renderRate;
uid_t uid = 0;
std::string layerName;
+ int32_t gameMode = 0;
int32_t reasons = 0;
nsecs_t displayDeadlineDelta = 0;
nsecs_t displayPresentJitter = 0;
@@ -141,8 +142,8 @@
((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
(renderRate != std::nullopt && o.renderRate != std::nullopt &&
Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
- uid == o.uid && layerName == o.layerName && reasons == o.reasons &&
- displayDeadlineDelta == o.displayDeadlineDelta &&
+ uid == o.uid && layerName == o.layerName && gameMode == o.gameMode &&
+ reasons == o.reasons && displayDeadlineDelta == o.displayDeadlineDelta &&
displayPresentJitter == o.displayPresentJitter &&
appDeadlineDelta == o.appDeadlineDelta;
}
@@ -199,6 +200,7 @@
struct LayerRecord {
uid_t uid;
std::string layerName;
+ int32_t gameMode = 0;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
@@ -251,7 +253,7 @@
const std::shared_ptr<FenceTime>& readyFence) override;
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
- nsecs_t postTime) override;
+ nsecs_t postTime, int32_t gameMode) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
void incrementBadDesiredPresent(int32_t layerId) override;
@@ -261,10 +263,11 @@
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) override;
+ SetFrameRateVote frameRateVote, int32_t gameMode) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
- std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
+ std::optional<Fps> renderRate, SetFrameRateVote frameRateVote,
+ int32_t gameMode) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
@@ -286,10 +289,10 @@
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote);
+ SetFrameRateVote frameRateVote, int32_t gameMode);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
- bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode);
void enable();
void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index 133a541..e45757d 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -166,6 +166,23 @@
// This is intended to be used as a dimension in collecting per-render rate
// jank statistics.
optional int32 render_rate_bucket = 23;
+
+ enum GameMode {
+ GAME_MODE_UNSPECIFIED = 0;
+ GAME_MODE_UNSUPPORTED = 1;
+ GAME_MODE_STANDARD = 2;
+ GAME_MODE_PERFORMANCE = 3;
+ GAME_MODE_BATTERY = 4;
+ }
+
+ // Game mode that the layer was running at. Used to track user engagement
+ // in different modes. The modes are defined in GameManager.java
+ // Game modes are used only for integrating with GameManager. All non-game
+ // layers will have this field set to UNSUPPORTED.
+ // Introduced in Android 12
+ // This is intended to be used as a dimension in collecting per-game mode
+ // fps and frame related metrics.
+ optional GameMode game_mode = 26;
// The layer for this set of metrics
// In many scenarios the package name is included in the layer name, e.g.,
// layers created by Window Manager. But this is not a guarantee - in the
@@ -271,7 +288,7 @@
// Introduced in Android 12.
optional FrameTimingHistogram app_deadline_misses = 25;
- // Next ID: 26
+ // Next ID: 27
}
/**
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index a7e7db2..ffb2f09 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -122,6 +122,20 @@
return result;
}
+std::string TimeStatsHelper::TimeStatsLayer::toString(int32_t gameMode) const {
+ switch (gameMode) {
+ case TimeStatsHelper::GameModeUnsupported:
+ return "GameModeUnsupported";
+ case TimeStatsHelper::GameModeStandard:
+ return "GameModeStandard";
+ case TimeStatsHelper::GameModePerformance:
+ return "GameModePerformance";
+ case TimeStatsHelper::GameModeBattery:
+ return "GameModeBattery";
+ default:
+ return "GameModeUnspecified";
+ }
+}
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -129,6 +143,7 @@
StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
+ StringAppendF(&result, "gameMode = %s\n", toString(gameMode).c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 2b37ffe..2afff8d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -77,6 +77,18 @@
std::string toString() const;
};
+ /**
+ * GameMode of the layer. GameModes are set by SysUI through WMShell.
+ * Actual game mode definitions are managed by GameManager.java
+ * The values defined here should always be in sync with the ones in GameManager.
+ */
+ enum GameMode {
+ GameModeUnsupported = 0,
+ GameModeStandard = 1,
+ GameModePerformance = 2,
+ GameModeBattery = 3,
+ };
+
class TimeStatsLayer {
public:
uid_t uid;
@@ -84,6 +96,7 @@
std::string packageName;
int32_t displayRefreshRateBucket = 0;
int32_t renderRateBucket = 0;
+ int32_t gameMode = 0;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
@@ -93,6 +106,7 @@
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
+ std::string toString(int32_t gameMode) const;
SFTimeStatsLayerProto toProto() const;
};
@@ -123,24 +137,19 @@
struct LayerStatsKey {
uid_t uid = 0;
std::string layerName;
+ int32_t gameMode = 0;
struct Hasher {
size_t operator()(const LayerStatsKey& key) const {
- size_t result = std::hash<uid_t>{}(key.uid);
- return HashCombine(result, std::hash<std::string>{}(key.layerName));
+ size_t uidHash = std::hash<uid_t>{}(key.uid);
+ size_t layerNameHash = std::hash<std::string>{}(key.layerName);
+ size_t gameModeHash = std::hash<int32_t>{}(key.gameMode);
+ return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
}
};
bool operator==(const LayerStatsKey& o) const {
- return uid == o.uid && layerName == o.layerName;
- }
- };
-
- struct LayerStatsHasher {
- size_t operator()(const std::pair<uid_t, std::string>& p) const {
- // Normally this isn't a very good hash function due to symmetry reasons,
- // but these are distinct types so this should be good enough
- return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+ return uid == o.uid && layerName == o.layerName && gameMode == o.gameMode;
}
};
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 9686523..673239d 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -89,6 +89,12 @@
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4);
+ // Set uclamp.min setting on all threads, maybe an overkill but we want
+ // to cover important threads like RenderEngine.
+ if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
+ ALOGW("Couldn't set uclamp.min: %s\n", strerror(errno));
+ }
+
// The binder threadpool we start will inherit sched policy and priority
// of (this) creating thread. We want the binder thread pool to have
// SCHED_FIFO policy and priority 1 (lowest RT priority)
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1b25a36..b5086fa 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -57,6 +57,7 @@
"FpsTest.cpp",
"FramebufferSurfaceTest.cpp",
"FrameTimelineTest.cpp",
+ "GameModeTest.cpp",
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
@@ -68,7 +69,7 @@
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
"SurfaceFlinger_HandleTransactionLockedTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
- "SurfaceFlinger_OnHotplugReceivedTest.cpp",
+ "SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_OnInitializeDisplaysTest.cpp",
"SurfaceFlinger_SetDisplayStateTest.cpp",
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 3042450..560f139 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -105,7 +105,9 @@
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
- setupComposer(0);
+
+ mComposer = new Hwc2::mock::Composer();
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
}
~CompositionTest() {
@@ -114,14 +116,6 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
- void setupComposer(int virtualDisplayCount) {
- mComposer = new Hwc2::mock::Composer();
- EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
- mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-
- Mock::VerifyAndClear(mComposer);
- }
-
void setupScheduler() {
auto eventThread = std::make_unique<mock::EventThread>();
auto sfEventThread = std::make_unique<mock::EventThread>();
@@ -289,16 +283,16 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
- auto ceDisplayArgs =
- compositionengine::DisplayCreationArgsBuilder()
- .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
- .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
- .setIsSecure(Derived::IS_SECURE)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setId(DEFAULT_DISPLAY_ID)
+ .setConnectionType(ui::DisplayConnectionType::Internal)
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setIsSecure(Derived::IS_SECURE)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
auto compositionDisplay =
compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
index 77a3e14..8d4a023 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
@@ -14,76 +14,68 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
#include <gtest/gtest.h>
+#include <ui/DisplayId.h>
+#include <algorithm>
+#include <iterator>
#include <vector>
-#include <ui/DisplayId.h>
#include "DisplayIdGenerator.h"
namespace android {
-template <typename T>
-void testNextId(DisplayIdGenerator<T>& generator) {
- constexpr int kNumIds = 5;
- std::vector<T> ids;
- for (int i = 0; i < kNumIds; i++) {
- const auto id = generator.nextId();
- ASSERT_TRUE(id);
- ids.push_back(*id);
- }
+template <typename Id>
+void testGenerateId() {
+ DisplayIdGenerator<Id> generator;
+
+ std::vector<std::optional<Id>> ids;
+ std::generate_n(std::back_inserter(ids), 10, [&] { return generator.generateId(); });
// All IDs should be different.
- for (size_t i = 0; i < kNumIds; i++) {
- for (size_t j = i + 1; j < kNumIds; j++) {
- EXPECT_NE(ids[i], ids[j]);
+ for (auto it = ids.begin(); it != ids.end(); ++it) {
+ EXPECT_TRUE(*it);
+
+ for (auto dup = it + 1; dup != ids.end(); ++dup) {
+ EXPECT_NE(*it, *dup);
}
}
}
-TEST(DisplayIdGeneratorTest, nextIdGpuVirtual) {
- RandomDisplayIdGenerator<GpuVirtualDisplayId> generator;
- testNextId(generator);
+TEST(DisplayIdGeneratorTest, generateGpuVirtualDisplayId) {
+ testGenerateId<GpuVirtualDisplayId>();
}
-TEST(DisplayIdGeneratorTest, nextIdHalVirtual) {
- RandomDisplayIdGenerator<HalVirtualDisplayId> generator;
- testNextId(generator);
+TEST(DisplayIdGeneratorTest, generateHalVirtualDisplayId) {
+ testGenerateId<HalVirtualDisplayId>();
}
-TEST(DisplayIdGeneratorTest, markUnused) {
+TEST(DisplayIdGeneratorTest, releaseId) {
constexpr size_t kMaxIdsCount = 5;
- RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+ DisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
- const auto id = generator.nextId();
+ const auto id = generator.generateId();
EXPECT_TRUE(id);
- for (int i = 1; i < kMaxIdsCount; i++) {
- EXPECT_TRUE(generator.nextId());
+ for (size_t i = 1; i < kMaxIdsCount; i++) {
+ EXPECT_TRUE(generator.generateId());
}
- EXPECT_FALSE(generator.nextId());
+ EXPECT_FALSE(generator.generateId());
- generator.markUnused(*id);
- EXPECT_TRUE(generator.nextId());
+ generator.releaseId(*id);
+ EXPECT_TRUE(generator.generateId());
}
TEST(DisplayIdGeneratorTest, maxIdsCount) {
constexpr size_t kMaxIdsCount = 5;
- RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+ DisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
- for (int i = 0; i < kMaxIdsCount; i++) {
- EXPECT_TRUE(generator.nextId());
+ for (size_t i = 0; i < kMaxIdsCount; i++) {
+ EXPECT_TRUE(generator.generateId());
}
- EXPECT_FALSE(generator.nextId());
+ EXPECT_FALSE(generator.generateId());
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index a3e8108..cc24323 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -41,9 +41,6 @@
mFlinger.mutableUseColorManagement() = false;
mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
- // Default to using HWC virtual displays
- mFlinger.mutableUseHwcVirtualDisplays() = true;
-
mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) {
ADD_FAILURE() << "Unexpected request to create a buffer queue.";
});
@@ -85,10 +82,17 @@
}
void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
+ if (mComposer) {
+ // If reinjecting, disable first to prevent the enable below from being a no-op.
+ mFlinger.enableHalVirtualDisplays(false);
+ }
+
mComposer = new Hwc2::mock::Composer();
- EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.enableHalVirtualDisplays(true);
+
Mock::VerifyAndClear(mComposer);
}
@@ -135,18 +139,21 @@
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
- auto compositionDisplay = compositionengine::impl::
- createDisplay(mFlinger.getCompositionEngine(),
- compositionengine::DisplayCreationArgsBuilder()
- .setPhysical(
- {DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal})
- .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
+ constexpr auto kConnectionType = ui::DisplayConnectionType::Internal;
+ constexpr bool kIsPrimary = true;
- auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
- ui::DisplayConnectionType::Internal,
- DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */);
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
+ compositionengine::DisplayCreationArgsBuilder()
+ .setId(DEFAULT_DISPLAY_ID)
+ .setConnectionType(kConnectionType)
+ .setPixels({DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT})
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
+
+ auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, kConnectionType,
+ DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
injector.setNativeWindow(mNativeWindow);
if (injectExtra) {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index d68fff6..6ce281d 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -263,40 +263,23 @@
static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
- if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
- ceDisplayArgs.setPhysical({*displayId, ui::DisplayConnectionType::Internal});
- } else {
- // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
- // from calling into HWComposer. This way all virtual displays will get
- // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
- // In this case we later override it by calling display.setDisplayIdForTesting().
- ceDisplayArgs.setUseHwcVirtualDisplays(false);
+ ceDisplayArgs.setId(DISPLAY_ID::get())
+ .setPixels({WIDTH, HEIGHT})
+ .setPowerAdvisor(&test->mPowerAdvisor);
- GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
- .value_or(GpuVirtualDisplayId(0));
-
- ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
- .WillByDefault(Return(desiredDisplayId));
-
- auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
- ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
+ const auto connectionType = CONNECTION_TYPE::value;
+ if (connectionType) {
+ ceDisplayArgs.setConnectionType(*connectionType);
}
- ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
auto compositionDisplay =
compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
ceDisplayArgs.build());
- if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
- // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
- // override it with the correct HalVirtualDisplayId.
- compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
- }
-
auto injector =
TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
compositionDisplay,
- CONNECTION_TYPE::value,
+ connectionType,
HWC_DISPLAY_ID_OPT::value,
static_cast<bool>(PRIMARY));
@@ -404,8 +387,8 @@
::testing::UnitTest::GetInstance()->current_test_info();
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPhysical({DisplayVariant::DISPLAY_ID::get(),
- PhysicalDisplay::CONNECTION_TYPE})
+ .setId(DisplayVariant::DISPLAY_ID::get())
+ .setConnectionType(PhysicalDisplay::CONNECTION_TYPE)
.setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
.setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
.setPowerAdvisor(&test->mPowerAdvisor)
@@ -558,17 +541,13 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
- ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
- .WillByDefault(Return(Base::DISPLAY_ID::get()));
-
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setId(Base::DISPLAY_ID::get())
.setPixels({Base::WIDTH, Base::HEIGHT})
.setIsSecure(static_cast<bool>(Base::SECURE))
.setPowerAdvisor(&test->mPowerAdvisor)
.setName(std::string("Injected display for ") +
test_info->test_case_name() + "." + test_info->name())
- .setGpuVirtualDisplayIdGenerator(
- test->mFlinger.gpuVirtualDisplayIdGenerator())
.build();
return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
@@ -610,35 +589,22 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
- // In order to prevent compostition engine calling into HWComposer, we
- // 1. turn off the use of HWC virtual displays,
- // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
- // 3. override the ID by calling setDisplayIdForTesting()
-
- ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
- .WillByDefault(Return(GpuVirtualDisplayId(0)));
-
+ const auto displayId = Base::DISPLAY_ID::get();
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(false)
+ .setId(displayId)
.setPixels({Base::WIDTH, Base::HEIGHT})
.setIsSecure(static_cast<bool>(Base::SECURE))
.setPowerAdvisor(&test->mPowerAdvisor)
.setName(std::string("Injected display for ") +
test_info->test_case_name() + "." + test_info->name())
- .setGpuVirtualDisplayIdGenerator(
- test->mFlinger.gpuVirtualDisplayIdGenerator())
.build();
auto compositionDisplay =
compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
ceDisplayArgs);
- compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
// Insert display data so that the HWC thinks it created the virtual display.
- if (const auto displayId = Base::DISPLAY_ID::get();
- HalVirtualDisplayId::tryCast(displayId)) {
- test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
- }
+ test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
return compositionDisplay;
}
@@ -649,8 +615,8 @@
}
static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
- .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
}
};
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index dec0ff5..010c675 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -76,11 +76,9 @@
static constexpr int32_t PRIORITY_UNSET = -1;
void setupScheduler();
- void setupComposer(uint32_t virtualDisplayCount);
sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
mock::FrameTimeline mFrameTimeline =
mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
@@ -103,7 +101,8 @@
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
setupScheduler();
- setupComposer(0);
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+
mFpsListener = new TestableFpsListener();
}
@@ -145,19 +144,11 @@
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();
- const constexpr int32_t kTaskId = 12;
+ constexpr int32_t kTaskId = 12;
LayerMetadata targetMetadata;
targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
mTarget = createBufferStateLayer(targetMetadata);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index c6a4115..0a8c748 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -175,6 +175,7 @@
static constexpr int32_t sInputEventId = 5;
static constexpr int32_t sLayerIdOne = 1;
static constexpr int32_t sLayerIdTwo = 2;
+static constexpr int32_t sGameMode = 0;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -194,11 +195,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
@@ -207,7 +208,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -217,7 +218,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -227,7 +228,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -239,7 +240,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -250,7 +251,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -278,11 +279,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdTwo, sLayerNameTwo,
- sLayerNameTwo, /*isBuffer*/ true);
+ sLayerNameTwo, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -325,7 +326,7 @@
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -347,7 +348,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -361,20 +362,20 @@
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue", /*isBuffer*/ true);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue",
+ /*isBuffer*/ true, sGameMode);
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue", /*isBuffer*/ true);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue",
+ /*isBuffer*/ true, sGameMode);
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -389,7 +390,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -406,7 +407,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -423,7 +424,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -443,7 +444,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -471,7 +472,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -486,7 +487,7 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
+ sLayerNameOne, sGameMode,
JankType::SurfaceFlingerCpuDeadlineMissed, 2, 10,
0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -496,7 +497,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -511,7 +512,7 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
+ sLayerNameOne, sGameMode,
JankType::SurfaceFlingerGpuDeadlineMissed, 4, 10,
0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -522,7 +523,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -537,8 +538,8 @@
Fps refreshRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne, JankType::DisplayHAL,
- -4, 0, 0}));
+ sLayerNameOne, sGameMode,
+ JankType::DisplayHAL, -4, 0, 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
@@ -547,7 +548,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setAcquireFenceTime(20);
@@ -561,7 +562,7 @@
Fps refreshRate = Fps(11.0);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
+ sLayerNameOne, sGameMode,
JankType::AppDeadlineMissed, -4, 0,
25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -571,7 +572,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(45);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -587,7 +588,7 @@
Fps refreshRate = Fps::fromPeriodNsecs(32);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
+ sLayerNameOne, sGameMode,
JankType::SurfaceFlingerScheduling,
-4, 0, -10}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -597,7 +598,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -613,7 +614,7 @@
Fps refreshRate = Fps::fromPeriodNsecs(16);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
+ sLayerNameOne, sGameMode,
JankType::PredictionError, -4, 5,
0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -623,7 +624,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -639,7 +640,7 @@
Fps refreshRate = Fps::fromPeriodNsecs(32);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
+ sLayerNameOne, sGameMode,
JankType::BufferStuffing, -4, 0,
0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -649,7 +650,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
@@ -666,9 +667,10 @@
Fps refreshRate = Fps::fromPeriodNsecs(11);
Fps renderRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
- JankType::AppDeadlineMissed, -4, 0, 25}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
+ sLayerNameOne, sGameMode,
+ JankType::AppDeadlineMissed, -4, 0,
+ 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
@@ -676,7 +678,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(45);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -696,6 +698,7 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+ sGameMode,
JankType::Unknown | JankType::AppDeadlineMissed,
0, 0, 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -705,7 +708,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
flushTokens();
@@ -742,7 +745,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -769,7 +772,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -815,7 +818,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -874,7 +877,7 @@
ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart(
int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName,
ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition,
- ProtoJankType jankType, ProtoPredictionType predictionType) {
+ ProtoJankType jankType, ProtoPredictionType predictionType, bool isBuffer) {
ProtoActualSurfaceFrameStart proto;
proto.set_cookie(cookie);
proto.set_token(token);
@@ -886,6 +889,7 @@
proto.set_gpu_composition(gpuComposition);
proto.set_jank_type(jankType);
proto.set_prediction_type(predictionType);
+ proto.set_is_buffer(isBuffer);
return proto;
}
@@ -975,6 +979,8 @@
EXPECT_EQ(received.jank_type(), source.jank_type());
ASSERT_TRUE(received.has_prediction_type());
EXPECT_EQ(received.prediction_type(), source.prediction_type());
+ ASSERT_TRUE(received.has_is_buffer());
+ EXPECT_EQ(received.is_buffer(), source.is_buffer());
}
void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
@@ -1128,11 +1134,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1151,7 +1157,7 @@
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
FrameTimelineEvent::JANK_NONE,
- FrameTimelineEvent::PREDICTION_VALID);
+ FrameTimelineEvent::PREDICTION_VALID, true);
auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
auto protoPresentedSurfaceFrameExpectedStart =
@@ -1163,7 +1169,7 @@
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_ON_TIME, true, false,
FrameTimelineEvent::JANK_NONE,
- FrameTimelineEvent::PREDICTION_VALID);
+ FrameTimelineEvent::PREDICTION_VALID, true);
auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
// Set up the display frame
@@ -1288,7 +1294,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
@@ -1306,7 +1312,7 @@
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
false, FrameTimelineEvent::JANK_UNKNOWN,
- FrameTimelineEvent::PREDICTION_EXPIRED);
+ FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
@@ -1364,7 +1370,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(22ms).count();
constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
@@ -1380,7 +1386,7 @@
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
FrameTimelineEvent::JANK_NONE,
- FrameTimelineEvent::PREDICTION_EXPIRED);
+ FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
@@ -1433,7 +1439,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1649,7 +1655,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1669,7 +1675,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1701,8 +1707,8 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
- sLayerNameOne, JankType::PredictionError, -3, 5,
- 0}));
+ sLayerNameOne, sGameMode,
+ JankType::PredictionError, -3, 5, 0}));
addEmptyDisplayFrame();
@@ -1729,7 +1735,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1749,7 +1755,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1781,8 +1787,8 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
- sLayerNameOne, JankType::PredictionError, -3, 5,
- 0}));
+ sLayerNameOne, sGameMode,
+ JankType::PredictionError, -3, 5, 0}));
addEmptyDisplayFrame();
@@ -1808,7 +1814,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1852,7 +1858,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1872,7 +1878,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1927,7 +1933,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1947,7 +1953,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -2005,7 +2011,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -2025,7 +2031,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne, /*isBuffer*/ true);
+ sLayerNameOne, /*isBuffer*/ true, sGameMode);
surfaceFrame2->setAcquireFenceTime(80);
mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
// Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
@@ -2081,7 +2087,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2097,7 +2103,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2107,7 +2113,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2123,7 +2129,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2133,7 +2139,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2149,7 +2155,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2159,7 +2165,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2178,7 +2184,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2188,7 +2194,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2198,7 +2204,7 @@
auto surfaceFrame3 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame3);
@@ -2208,7 +2214,7 @@
auto surfaceFrame4 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame4);
@@ -2218,7 +2224,7 @@
auto surfaceFrame5 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
sLayerIdOne, sLayerNameOne, sLayerNameOne,
- /*isBuffer*/ true);
+ /*isBuffer*/ true, sGameMode);
auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
// Dropped frames will be excluded from fps computation
surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
new file mode 100644
index 0000000..3fa1a2c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Mock;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class GameModeTest : public testing::Test {
+public:
+ GameModeTest() {
+ 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();
+ }
+
+ ~GameModeTest() {
+ 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> createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerMetadata());
+ return new BufferStateLayer(args);
+ }
+
+ void 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 setupComposer() {
+ mComposer = new Hwc2::mock::Composer();
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+ }
+
+ // Mocks the behavior of applying a transaction from WMShell
+ void setGameModeMetadata(sp<Layer> layer, int gameMode) {
+ mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode);
+ layer->setMetadata(mLayerMetadata);
+ layer->setGameModeForTree(gameMode);
+ }
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ client_cache_t mClientCache;
+ LayerMetadata mLayerMetadata;
+};
+
+TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
+ sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+ sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
+ sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+ rootLayer->addChild(childLayer1);
+ rootLayer->addChild(childLayer2);
+ rootLayer->setGameModeForTree(/*gameMode*/ 2);
+
+ EXPECT_EQ(rootLayer->getGameMode(), 2);
+ EXPECT_EQ(childLayer1->getGameMode(), 2);
+ EXPECT_EQ(childLayer2->getGameMode(), 2);
+}
+
+TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
+ sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+ sp<BufferStateLayer> childLayer = createBufferStateLayer();
+ rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->addChild(childLayer);
+
+ EXPECT_EQ(rootLayer->getGameMode(), 2);
+ EXPECT_EQ(childLayer->getGameMode(), 2);
+}
+
+TEST_F(GameModeTest, RemoveChildResetsGameMode) {
+ sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+ sp<BufferStateLayer> childLayer = createBufferStateLayer();
+ rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->addChild(childLayer);
+
+ EXPECT_EQ(rootLayer->getGameMode(), 2);
+ EXPECT_EQ(childLayer->getGameMode(), 2);
+
+ rootLayer->removeChild(childLayer);
+ EXPECT_EQ(childLayer->getGameMode(), 0);
+}
+
+TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
+ sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+ sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
+ sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+ rootLayer->setGameModeForTree(/*gameMode*/ 1);
+ rootLayer->addChild(childLayer1);
+
+ setGameModeMetadata(childLayer2, /*gameMode*/ 2);
+ rootLayer->addChild(childLayer2);
+
+ EXPECT_EQ(rootLayer->getGameMode(), 1);
+ EXPECT_EQ(childLayer1->getGameMode(), 1);
+ EXPECT_EQ(childLayer2->getGameMode(), 2);
+
+ rootLayer->removeChild(childLayer2);
+ EXPECT_EQ(childLayer2->getGameMode(), 2);
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index cbf8cc2..655baf8 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -58,28 +58,26 @@
using ::testing::StrictMock;
struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
- MOCK_METHOD3(onHotplugReceived, void(int32_t sequenceId, hal::HWDisplayId, hal::Connection));
- MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId));
- MOCK_METHOD4(onVsyncReceived,
- void(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos>));
- MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
- void(int32_t sequenceId, hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
- MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId));
+ MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
+ MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
+ MOCK_METHOD3(onComposerHalVsync,
+ void(hal::HWDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos>));
+ MOCK_METHOD2(onComposerHalVsyncPeriodTimingChanged,
+ void(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
+ MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId));
};
-struct HWComposerSetConfigurationTest : testing::Test {
+struct HWComposerSetCallbackTest : testing::Test {
Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
MockHWC2ComposerCallback mCallback;
};
-TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
+TEST_F(HWComposerSetCallbackTest, loadsLayerMetadataSupport) {
const std::string kMetadata1Name = "com.example.metadata.1";
constexpr bool kMetadata1Mandatory = false;
const std::string kMetadata2Name = "com.example.metadata.2";
constexpr bool kMetadata2Mandatory = true;
- EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
.WillOnce(DoAll(SetArgPointee<0>(std::vector<hal::LayerGenericMetadataKey>{
@@ -91,7 +89,7 @@
EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
- hwc.setConfiguration(&mCallback, 123);
+ hwc.setCallback(&mCallback);
const auto& supported = hwc.getSupportedLayerGenericMetadata();
EXPECT_EQ(2u, supported.size());
@@ -101,8 +99,7 @@
EXPECT_EQ(kMetadata2Mandatory, supported.find(kMetadata2Name)->second);
}
-TEST_F(HWComposerSetConfigurationTest, handlesUnsupportedCallToGetLayerGenericMetadataKeys) {
- EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+TEST_F(HWComposerSetCallbackTest, handlesUnsupportedCallToGetLayerGenericMetadataKeys) {
EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
.WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
@@ -110,7 +107,7 @@
EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
- hwc.setConfiguration(&mCallback, 123);
+ hwc.setCallback(&mCallback);
const auto& supported = hwc.getSupportedLayerGenericMetadata();
EXPECT_EQ(0u, supported.size());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7ace70a..d04a7d7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -45,9 +45,16 @@
class RefreshRateConfigsTest : public testing::Test {
protected:
+ using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;
+
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
+ RefreshRate createRefreshRate(DisplayModePtr displayMode) {
+ return {displayMode->getId(), displayMode, displayMode->getFps(),
+ RefreshRate::ConstructorTag(0)};
+ }
+
Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
}
@@ -71,6 +78,19 @@
return *refreshRateConfigs.mMaxSupportedRefreshRate;
}
+ void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
+ const GetBestRefreshRateInvocation& invocation) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
+ GetBestRefreshRateInvocation(invocation));
+ }
+
+ std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
+ const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return refreshRateConfigs.lastBestRefreshRateInvocation;
+ }
+
// Test config IDs
static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
@@ -1752,6 +1772,78 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
+ using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ setLastBestRefreshRateInvocation(*refreshRateConfigs,
+ GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+ LayerRequirement>(),
+ .globalSignals = {.touch = true,
+ .idle = true},
+ .outSignalsConsidered =
+ {.touch = true,
+ .idle = false},
+ .resultingBestRefreshRate =
+ createRefreshRate(
+ mConfig90)});
+
+ EXPECT_EQ(createRefreshRate(mConfig90),
+ refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+ {.touch = true, .idle = true}));
+
+ const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
+ setLastBestRefreshRateInvocation(*refreshRateConfigs,
+ GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+ LayerRequirement>(),
+ .globalSignals = {.touch = true,
+ .idle = true},
+ .outSignalsConsidered =
+ cachedSignalsConsidered,
+ .resultingBestRefreshRate =
+ createRefreshRate(
+ mConfig30)});
+
+ GlobalSignals signalsConsidered;
+ EXPECT_EQ(createRefreshRate(mConfig30),
+ refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+ {.touch = true, .idle = true},
+ &signalsConsidered));
+
+ EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
+ using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
+
+ GlobalSignals globalSignals{.touch = true, .idle = true};
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 0.5f}};
+ const auto lastResult =
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
+ /* outSignalsConsidered */ nullptr);
+
+ const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs);
+
+ ASSERT_TRUE(lastInvocation.has_value());
+ ASSERT_EQ(layers, lastInvocation->layerRequirements);
+ ASSERT_EQ(globalSignals, lastInvocation->globalSignals);
+ ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate);
+
+ // outSignalsConsidered needs to be populated even tho earlier we gave nullptr
+ // to getBestRefreshRate()
+ GlobalSignals detaultSignals;
+ ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered);
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 9c6ad06..fd3e564 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -60,7 +60,6 @@
static constexpr int32_t PRIORITY_UNSET = -1;
void setupScheduler();
- void setupComposer(uint32_t virtualDisplayCount);
sp<BufferQueueLayer> createBufferQueueLayer();
sp<BufferStateLayer> createBufferStateLayer();
sp<EffectLayer> createEffectLayer();
@@ -69,7 +68,6 @@
void commitTransaction(Layer* layer);
TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
sp<Client> mClient;
sp<Layer> mParent;
@@ -83,7 +81,7 @@
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
setupScheduler();
- setupComposer(0);
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
}
RefreshRateSelectionTest::~RefreshRateSelectionTest() {
@@ -147,14 +145,6 @@
std::move(eventThread), std::move(sfEventThread));
}
-void RefreshRateSelectionTest::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 cases
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 38e503f..423d0cc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -51,9 +51,18 @@
SchedulerTest();
- const scheduler::RefreshRateConfigs
- mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setGroup(0).build()},
- DisplayModeId(0)};
+ const DisplayModePtr mode60 = DisplayMode::Builder(0)
+ .setId(DisplayModeId(0))
+ .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
+ .setGroup(0)
+ .build();
+ const DisplayModePtr mode120 = DisplayMode::Builder(1)
+ .setId(DisplayModeId(1))
+ .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
+ .setGroup(0)
+ .build();
+
+ scheduler::RefreshRateConfigs mConfigs{{mode60}, mode60->getId()};
mock::SchedulerCallback mSchedulerCallback;
@@ -149,15 +158,14 @@
EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
-TEST_F(SchedulerTest, noLayerHistory) {
- // Layer history should not be created if there is a single config.
- ASSERT_FALSE(mScheduler->hasLayerHistory());
-
+TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
+ // The layer is registered at creation time and deregistered at destruction time.
sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
- // Content detection should be no-op.
- mScheduler->registerLayer(layer.get());
+ // recordLayerHistory should be a noop
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
constexpr bool kPowerStateNormal = true;
mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -169,6 +177,18 @@
mScheduler->chooseRefreshRateForContent();
}
+TEST_F(SchedulerTest, updateDisplayModes) {
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+
+ mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+}
+
TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
// If the optional fields are cleared, the function should return before
// onModeChange is called.
@@ -200,4 +220,25 @@
EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
}
+MATCHER(Is120Hz, "") {
+ return arg.getFps().equalsWithMargin(Fps(120.f));
+}
+
+TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
+ mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler->setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
+ mScheduler->chooseRefreshRateForContent();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index c088ddc..46ef750 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -118,14 +118,12 @@
SetFrameRateTest();
void setupScheduler();
- void setupComposer(uint32_t virtualDisplayCount);
void addChild(sp<Layer> layer, sp<Layer> child);
void removeChild(sp<Layer> layer, sp<Layer> child);
void commitTransaction();
TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
std::vector<sp<Layer>> mLayers;
@@ -139,10 +137,11 @@
mFlinger.mutableUseFrameRateApi() = true;
setupScheduler();
- setupComposer(0);
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.mutableEventQueue().reset(mMessageQueue);
}
+
void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
layer.get()->addChild(child.get());
}
@@ -184,14 +183,6 @@
/*hasMultipleModes*/ true);
}
-void SetFrameRateTest::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 cases
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
similarity index 62%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 42f4cf3..bd89397 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -22,19 +22,15 @@
namespace android {
namespace {
-class OnHotplugReceivedTest : public DisplayTransactionTest {};
+class HotplugTest : public DisplayTransactionTest {};
-TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) {
- constexpr int currentSequenceId = 123;
+TEST_F(HotplugTest, enqueuesEventsForDisplayTransaction) {
constexpr HWDisplayId hwcDisplayId1 = 456;
constexpr HWDisplayId hwcDisplayId2 = 654;
// --------------------------------------------------------------------
// Preconditions
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
// Set the main thread id so that the current thread does not appear to be
// the main thread.
mFlinger.mutableMainThreadId() = std::thread::id();
@@ -50,8 +46,8 @@
// Invocation
// Simulate two hotplug events (a connect and a disconnect)
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
+ mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED);
+ mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED);
// --------------------------------------------------------------------
// Postconditions
@@ -68,52 +64,14 @@
EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
}
-TEST_F(OnHotplugReceivedTest, hotplugDiscardsUnexpectedEvents) {
- constexpr int currentSequenceId = 123;
- constexpr int otherSequenceId = 321;
- constexpr HWDisplayId displayId = 456;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We do not expect any calls to invalidate().
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Call with an unexpected sequence id
- mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should not be set
- EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no pending events
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
- constexpr int currentSequenceId = 123;
+TEST_F(HotplugTest, processesEnqueuedEventsIfCalledOnMainThread) {
constexpr HWDisplayId displayId1 = 456;
// --------------------------------------------------------------------
// Note:
// --------------------------------------------------------------------
// This test case is a bit tricky. We want to verify that
- // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
+ // onComposerHalHotplug() calls processDisplayHotplugEventsLocked(), but we
// don't really want to provide coverage for everything the later function
// does as there are specific tests for it.
// --------------------------------------------------------------------
@@ -121,9 +79,6 @@
// --------------------------------------------------------------------
// Preconditions
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
// Set the main thread id so that the current thread does appear to be the
// main thread.
mFlinger.mutableMainThreadId() = std::this_thread::get_id();
@@ -139,9 +94,9 @@
// Invocation
// Simulate a disconnect on a display id that is not connected. This should
- // be enqueued by onHotplugReceived(), and dequeued by
+ // be enqueued by onComposerHalHotplug(), and dequeued by
// processDisplayHotplugEventsLocked(), but then ignored as invalid.
- mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
+ mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED);
// --------------------------------------------------------------------
// Postconditions
@@ -155,4 +110,4 @@
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f9dd01..41fd6e3 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -64,6 +64,11 @@
return mutableLayerHistory()->mLayerInfos.size();
}
+ size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mutableLayerHistory()->mActiveLayersEnd;
+ }
+
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
mTouchTimer.reset();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a551248..d78f36c 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -42,7 +42,6 @@
#include "SurfaceInterceptor.h"
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDisplayIdGenerator.h"
#include "mock/MockFrameTimeline.h"
#include "mock/MockFrameTracer.h"
@@ -185,9 +184,6 @@
SurfaceFlinger* flinger() { return mFlinger.get(); }
TestableScheduler* scheduler() { return mScheduler; }
- mock::DisplayIdGenerator<GpuVirtualDisplayId>& gpuVirtualDisplayIdGenerator() {
- return mGpuVirtualDisplayIdGenerator;
- }
// Extend this as needed for accessing SurfaceFlinger private (and public)
// functions.
@@ -309,6 +305,8 @@
return mFlinger->destroyDisplay(displayToken);
}
+ void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
+
auto setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -324,9 +322,8 @@
return mFlinger->handleTransactionLocked(transactionFlags);
}
- auto onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
- hal::Connection connection) {
- return mFlinger->onHotplugReceived(sequenceId, display, connection);
+ void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
+ mFlinger->onComposerHalHotplug(hwcDisplayId, connection);
}
auto setDisplayStateLocked(const DisplayState& s) {
@@ -437,11 +434,9 @@
auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; }
auto& mutableTexturePool() { return mFlinger->mTexturePool; }
auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
- auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
- auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
@@ -776,7 +771,6 @@
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
- mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 3e4e130..317cdf1 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -54,6 +54,9 @@
using testing::UnorderedElementsAre;
using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode;
+using SurfaceflingerStatsLayerInfo = android::surfaceflinger::SurfaceflingerStatsLayerInfo;
+using SurfaceflingerStatsLayerInfoWrapper =
+ android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper;
// clang-format off
#define FMT_PROTO true
@@ -71,6 +74,7 @@
const constexpr Fps kRefreshRate0 = Fps(static_cast<float>(REFRESH_RATE_0));
const constexpr Fps kRenderRate0 = Fps(static_cast<float>(RENDER_RATE_0));
+static constexpr int32_t kGameMode = TimeStatsHelper::GameModeUnsupported;
enum InputCommand : int32_t {
ENABLE = 0,
@@ -143,15 +147,16 @@
std::string inputCommand(InputCommand cmd, bool useProto);
void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
- TimeStats::SetFrameRateVote frameRateVote);
+ TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode);
int32_t genRandomInt32(int32_t begin, int32_t end);
template <size_t N>
void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
- nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
+ nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {},
+ int32_t gameMode = kGameMode) {
for (size_t i = 0; i < N; i++, ts += 1000000) {
- setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
+ setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote, gameMode);
}
}
@@ -200,11 +205,11 @@
}
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
- TimeStats::SetFrameRateVote frameRateVote) {
+ TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode) {
switch (type) {
case TimeStamp::POST:
- ASSERT_NO_FATAL_FAILURE(
- mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id),
+ UID_0, ts, gameMode));
break;
case TimeStamp::ACQUIRE:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
@@ -221,12 +226,14 @@
break;
case TimeStamp::PRESENT:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
- kRenderRate0, frameRateVote));
+ kRenderRate0, frameRateVote,
+ gameMode));
break;
case TimeStamp::PRESENT_FENCE:
- ASSERT_NO_FATAL_FAILURE(
- mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
- kRefreshRate0, kRenderRate0, frameRateVote));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber,
+ std::make_shared<FenceTime>(ts),
+ kRefreshRate0, kRenderRate0,
+ frameRateVote, gameMode));
break;
default:
ALOGD("Invalid timestamp type");
@@ -319,22 +326,24 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
+ kGameMode, JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2,
3});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, 1, 2, 3});
+ kGameMode, JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2,
+ 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::DisplayHAL, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::AppDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::PredictionError, 1, 2, 3});
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), kGameMode,
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::None, 1, 2, 3});
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
std::string expectedResult =
@@ -872,22 +881,24 @@
std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count()));
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, 1, 2, 3});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
+ kGameMode, JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2,
3});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, 1, 2, 3});
+ kGameMode, JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2,
+ 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::DisplayHAL, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::AppDeadlineMissed, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::PredictionError, 1, 2, 3});
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), kGameMode,
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, 3});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::None, 1, 2, 3});
EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
@@ -1039,34 +1050,36 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed,
+ kGameMode, JankType::SurfaceFlingerCpuDeadlineMissed,
DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed,
+ kGameMode, JankType::SurfaceFlingerGpuDeadlineMissed,
DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+ kGameMode, JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing,
+ kGameMode, JankType::AppDeadlineMissed,
DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+ kGameMode, JankType::SurfaceFlingerScheduling,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), kGameMode,
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, DISPLAY_DEADLINE_DELTA,
+ kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
std::string pulledData;
@@ -1157,7 +1170,8 @@
constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
+ TimeStatsHelper::GameModeStandard);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
}
@@ -1170,43 +1184,50 @@
TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
.seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
};
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60,
+ TimeStatsHelper::GameModeStandard);
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerCpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed,
+ TimeStatsHelper::GameModeStandard, JankType::DisplayHAL,
DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
- APP_DEADLINE_DELTA_3MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ TimeStatsHelper::GameModeStandard,
JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ TimeStatsHelper::GameModeStandard,
JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
+ TimeStatsHelper::GameModeStandard, JankType::PredictionError,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ TimeStatsHelper::GameModeStandard,
JankType::AppDeadlineMissed | JankType::BufferStuffing,
DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
+ TimeStatsHelper::GameModeStandard, JankType::None,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
ASSERT_EQ(atomList.atom_size(), 1);
- const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ const SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
EXPECT_EQ(atom.layer_name(), genLayerName(LAYER_ID_0));
EXPECT_EQ(atom.total_frames(), 1);
@@ -1236,6 +1257,7 @@
(int)frameRate60.frameRateCompatibility);
EXPECT_EQ((int)atom.set_frame_rate_vote().seamlessness(), (int)frameRate60.seamlessness);
EXPECT_THAT(atom.app_deadline_misses(), HistogramEq(buildExpectedHistogram({3, 2}, {4, 3})));
+ EXPECT_EQ(atom.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -1268,6 +1290,92 @@
EXPECT_THAT(result, Not(HasSubstr(expectedMissing)));
}
+TEST_F(TimeStatsTest, layerStatsCallback_multipleGameModes) {
+ constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+ constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
+ TimeStatsHelper::GameModeStandard);
+ for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+ mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+ }
+ for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+ mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+ }
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {},
+ TimeStatsHelper::GameModeStandard);
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {},
+ TimeStatsHelper::GameModePerformance);
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, TimeStatsHelper::GameModeBattery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, TimeStatsHelper::GameModeBattery);
+
+ std::string pulledData;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+
+ SurfaceflingerStatsLayerInfoWrapper atomList;
+ ASSERT_TRUE(atomList.ParseFromString(pulledData));
+ // The first time record is never uploaded to stats.
+ ASSERT_EQ(atomList.atom_size(), 3);
+ // Layers are ordered based on the hash in LayerStatsKey. For this test, the order happens to
+ // be: 0 - Battery 1 - Performance 2 - Standard
+ const SurfaceflingerStatsLayerInfo& atom0 = atomList.atom(0);
+
+ EXPECT_EQ(atom0.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom0.total_frames(), 2);
+ EXPECT_EQ(atom0.dropped_frames(), 0);
+ EXPECT_THAT(atom0.present_to_present(), HistogramEq(buildExpectedHistogram({0, 1}, {1, 1})));
+ EXPECT_THAT(atom0.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {2})));
+ EXPECT_THAT(atom0.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {2})));
+ EXPECT_THAT(atom0.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {2})));
+ EXPECT_THAT(atom0.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {2})));
+ EXPECT_THAT(atom0.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {2})));
+ EXPECT_EQ(atom0.late_acquire_frames(), 0);
+ EXPECT_EQ(atom0.bad_desired_present_frames(), 0);
+ EXPECT_EQ(atom0.uid(), UID_0);
+ EXPECT_EQ(atom0.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom0.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_EQ(atom0.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY);
+
+ const SurfaceflingerStatsLayerInfo& atom1 = atomList.atom(1);
+
+ EXPECT_EQ(atom1.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom1.total_frames(), 1);
+ EXPECT_EQ(atom1.dropped_frames(), 0);
+ EXPECT_THAT(atom1.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom1.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+ EXPECT_THAT(atom1.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+ EXPECT_THAT(atom1.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom1.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom1.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_EQ(atom1.late_acquire_frames(), 0);
+ EXPECT_EQ(atom1.bad_desired_present_frames(), 0);
+ EXPECT_EQ(atom1.uid(), UID_0);
+ EXPECT_EQ(atom1.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom1.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+
+ const SurfaceflingerStatsLayerInfo& atom2 = atomList.atom(2);
+
+ EXPECT_EQ(atom2.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom2.total_frames(), 1);
+ EXPECT_EQ(atom2.dropped_frames(), 0);
+ EXPECT_THAT(atom2.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom2.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+ EXPECT_THAT(atom2.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+ EXPECT_THAT(atom2.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom2.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom2.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_EQ(atom2.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+ EXPECT_EQ(atom2.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom2.uid(), UID_0);
+ EXPECT_EQ(atom2.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom2.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
+}
+
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -1279,7 +1387,7 @@
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
ASSERT_EQ(atomList.atom_size(), 2);
std::vector<std::string> actualLayerNames = {atomList.atom(0).layer_name(),
@@ -1304,10 +1412,10 @@
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
ASSERT_EQ(atomList.atom_size(), 1);
- const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ const SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1, 2}, {2, 1})));
}
@@ -1323,10 +1431,10 @@
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
ASSERT_EQ(atomList.atom_size(), 1);
- const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+ const SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {2})));
}
@@ -1343,7 +1451,7 @@
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
- android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+ SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
ASSERT_EQ(atomList.atom_size(), 1);
EXPECT_EQ(atomList.atom(0).layer_name(), genLayerName(LAYER_ID_1));
@@ -1372,7 +1480,7 @@
TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
const int32_t ts = genRandomInt32(1, 1000000000);
ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
- setTimeStamp(type, layerId, frameNumber, ts, {});
+ setTimeStamp(type, layerId, frameNumber, ts, {}, kGameMode);
}
}
@@ -1383,8 +1491,8 @@
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
- mTimeStats->incrementJankyFrames(
- {fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0), JankType::None, 0, 0, 0});
+ mTimeStats->incrementJankyFrames({fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0),
+ kGameMode, JankType::None, 0, 0, 0});
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
std::string expectedResult = "displayRefreshRate = " + std::to_string(bucket) + " fps";
EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 25001d3..546bc4a 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -45,7 +45,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
setupScheduler();
- setupComposer(0);
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
}
~TransactionFrameTracerTest() {
@@ -91,17 +91,9 @@
std::move(eventThread), std::move(sfEventThread));
}
- void 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);
- }
-
TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine mRenderEngine;
+
FenceToFenceTimeMap fenceFactory;
client_cache_t mClientCache;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index b7917aa..c1123cd 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -45,7 +45,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
setupScheduler();
- setupComposer(0);
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
}
~TransactionSurfaceFrameTest() {
@@ -91,17 +91,9 @@
std::move(eventThread), std::move(sfEventThread));
}
- void 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);
- }
-
TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine mRenderEngine;
+
FenceToFenceTimeMap fenceFactory;
client_cache_t mClientCache;
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 2a658dd..ae936e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -478,7 +478,7 @@
}
}
-TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) {
+TEST_F(VSyncPredictorTest, inconsistentVsyncValueIsFlushedEventually) {
EXPECT_TRUE(tracker.addVsyncTimestamp(600));
EXPECT_TRUE(tracker.needsMoreSamples());
@@ -492,6 +492,24 @@
EXPECT_FALSE(tracker.needsMoreSamples());
}
+TEST_F(VSyncPredictorTest, knownVsyncIsUpdated) {
+ EXPECT_TRUE(tracker.addVsyncTimestamp(600));
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ EXPECT_EQ(600, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+
+ EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += mPeriod));
+ EXPECT_EQ(mNow + 1000, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += mPeriod));
+ EXPECT_EQ(mNow + 1000, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+ }
+
+ EXPECT_FALSE(tracker.needsMoreSamples());
+ EXPECT_EQ(mNow + 1000, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 1ba3c0f..cb3bd73 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -54,7 +54,8 @@
MOCK_METHOD0(resetCommands, void());
MOCK_METHOD0(executeCommands, Error());
MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
- MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
+ MOCK_METHOD5(createVirtualDisplay,
+ Error(uint32_t, uint32_t, PixelFormat*, std::optional<Display>, Display*));
MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
MOCK_METHOD1(acceptDisplayChanges, Error(Display));
MOCK_METHOD2(createLayer, Error(Display, Layer* outLayer));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
deleted file mode 100644
index cfc37ea..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "DisplayIdGenerator.h"
-
-namespace android::mock {
-
-template <typename T>
-class DisplayIdGenerator : public android::DisplayIdGenerator<T> {
-public:
- // Explicit default instantiation is recommended.
- DisplayIdGenerator() = default;
- virtual ~DisplayIdGenerator() = default;
-
- MOCK_METHOD0(nextId, std::optional<T>());
- MOCK_METHOD1(markUnused, void(T));
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 526a847..5aebd2f 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,18 +41,19 @@
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t));
+ MOCK_METHOD6(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t, int32_t));
MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD6(setPresentTime,
- void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
- MOCK_METHOD6(setPresentFence,
+ MOCK_METHOD7(setPresentTime,
+ void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote,
+ int32_t));
+ MOCK_METHOD7(setPresentFence,
void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
- SetFrameRateVote));
+ SetFrameRateVote, int32_t));
MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 1010aa5..f15a963 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -95,7 +95,10 @@
}
HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
+ // the same as the operation being unsupported by this HAL. Should not retry.
return HalResult<void>::unsupported();
}
if (status.isOk()) {
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 8720d9d..87bc34e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -42,7 +42,10 @@
static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
static HalResult<T> fromStatus(binder::Status status, T data) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
+ // the same as the operation being unsupported by this HAL. Should not retry.
return HalResult<T>::unsupported();
}
if (status.isOk()) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index af0cdb8..7813303 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -189,8 +189,7 @@
.WillRepeatedly(vibrator::TriggerSchedulerCallback());
EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -228,8 +227,7 @@
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f)))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -265,8 +263,7 @@
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
.Times(Exactly(1))
@@ -397,8 +394,7 @@
Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
@@ -411,8 +407,7 @@
Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
.Times(Exactly(1))
.WillRepeatedly(
@@ -451,8 +446,7 @@
DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -549,8 +543,7 @@
.WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index 548d028..1593cb1 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -308,8 +308,7 @@
Return(Status())));
EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
.Times(Exactly(3))
- .WillOnce(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
.WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
.WillRepeatedly(DoAll(TriggerCallback(), Return(Status())));
}
@@ -345,8 +344,7 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) {
EXPECT_CALL(*mMockHal.get(), cancelSynced())
.Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
.WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
.WillRepeatedly(Return(Status()));