diff options
150 files changed, 3886 insertions, 1874 deletions
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl index a1d8ce5962..d6ca3db5d7 100644 --- a/aidl/gui/android/view/LayerMetadataKey.aidl +++ b/aidl/gui/android/view/LayerMetadataKey.aidl @@ -26,4 +26,5 @@ enum LayerMetadataKey { 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 818804ac14..0595322f92 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -342,7 +342,8 @@ static int restorecon_app_data_lazy(const std::string& path, const std::string& // 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 204953cd07..cc0434d9e4 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -292,8 +292,8 @@ static void SetDex2OatScheduling(bool set_to_bg) { } } -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 @@ static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t f 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 @@ static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t fla // 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 @@ static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t fla 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 @@ static unique_fd open_current_profile(uid_t uid, userid_t user, const std::strin 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 UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::st 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 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, 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 e27202597c..216347e616 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -919,7 +919,7 @@ class ProfileTest : public DexoptTest { 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 @@ class ProfileTest : public DexoptTest { 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 adfd6e297b..cc0ee825a8 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 d4defa8269..e8678d27c3 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 @@ enum { 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 1fec08027f..1955104a22 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -257,13 +257,9 @@ public: 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 @@ extern std::string getInputDeviceConfigurationFilePathByName( 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 be260e856d..91cd90d0ab 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -68,6 +68,8 @@ libbinder_device_interface_sources = [ 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 0000000000..9ca14bcec2 --- /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 3b3fd08cdc..39201298c3 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -24,7 +24,6 @@ use std::convert::TryFrom; 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 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { /// 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 ec231b2345..fb84f04fde 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -156,6 +156,10 @@ cc_test { ], 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 a2868c6143..b9a293f4c3 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -302,23 +302,25 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence // 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 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { 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 f74f91e034..63d07ba1fb 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -251,10 +251,11 @@ public: 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 634d8b7df0..189d51a4c1 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -136,6 +136,8 @@ std::string LayerMetadata::itemToString(uint32_t key, const char* separator) con 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 1057a51086..660c5bd97d 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -327,7 +327,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener callback(surfaceStats.previousBufferId, surfaceStats.previousReleaseFence ? surfaceStats.previousReleaseFence - : Fence::NO_FENCE); + : Fence::NO_FENCE, + surfaceStats.transformHint); } } } @@ -364,7 +365,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener } 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 @@ void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId, 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 c4ca399e37..3ab1ee103d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -89,7 +89,8 @@ public: 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 2d71194f70..3bfeef1922 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -158,7 +158,8 @@ public: 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 41982c28a2..de14b3d785 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -29,7 +29,8 @@ enum { 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 8a4c5a55f7..5aa132cd92 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -82,7 +82,8 @@ using TransactionCompletedCallback = 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 @@ public: // 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 61d72adb33..30c42a3daa 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -247,36 +247,22 @@ void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) { 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); - } - return ids; -} - -const InputDeviceLightInfo* InputDeviceInfo::getLightInfo(int32_t id) { - auto it = mLights.find(id); - if (it == mLights.end()) { - return nullptr; + infos.push_back(info); } - return &it->second; + return infos; } } // namespace android diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 4b90844490..3038d9daa5 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -40,4 +40,18 @@ interface IInputConstants * 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 ada689ac42..75f2385174 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -213,6 +213,7 @@ int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery what, in 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 3243a6b5f4..0eeca5469e 100644 --- a/libs/permission/Android.bp +++ b/libs/permission/Android.bp @@ -11,19 +11,24 @@ aidl_interface { 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 @@ cc_library_shared { 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 b6e54bf153..ed1b37dc0b 100644 --- a/libs/permission/aidl/android/content/AttributionSourceState.aidl +++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl @@ -23,8 +23,10 @@ package android.content; * {@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 @@ parcelable AttributionSourceState { /** 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 1f0e32d248..d3a331e1e2 100644 --- a/libs/permission/aidl/android/permission/IPermissionChecker.aidl +++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl @@ -28,9 +28,10 @@ interface IPermissionChecker { 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 a8083ee410..66526f90ea 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 @@ PermissionChecker::PermissionChecker() { } -sp<IPermissionChecker> PermissionChecker::getService() +sp<android::permission::IPermissionChecker> PermissionChecker::getService() { static String16 permission_checker("permission_checker"); @@ -59,56 +59,74 @@ sp<IPermissionChecker> PermissionChecker::getService() 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 checkPermission(permission, attributionSource, message, /*forDataDelivery*/ true, + /*startDataDelivery*/ true, /*fromDatasource*/ true, attributedOpCode); +} + +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 static_cast<PermissionResult>(checkPermission(permission, attributionSource, message, - /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true)); + return checkPermission(permission, attributionSource, message, /*forDataDelivery*/ false, + /*startDataDelivery*/ false, /*fromDatasource*/ true, attributedOpCode); } -void PermissionChecker::finishDataDelivery(const String16& op, - AttributionSourceState& attributionSource) +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 20ab51fc8a..21515e3bbd 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 @@ public: * 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 @@ public: * @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 @@ public: * @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 3c582383ca..b5dd8ac580 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -515,9 +515,10 @@ Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { 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 @@ void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) { 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 @@ bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { // 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 e7ed9c01fa..915dba364f 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -57,7 +57,7 @@ public: 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 @@ public: 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 @@ protected: 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 ddaa7c7783..ac0affb2ee 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ protected: // 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 27dbd1ecf3..0175af3a85 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -35,7 +35,7 @@ public: 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 @@ public: 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 8ae69de7f3..5c122d4154 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -29,8 +29,8 @@ namespace renderengine { 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 @@ AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer 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 @@ void AutoBackendTexture::unref(bool releaseLocalResources) { mUsageCount--; if (mUsageCount <= 0) { - if (mBackendTexture.isValid()) { - mDeleteProc(mImageCtx); - mBackendTexture = {}; - } - delete this; + mCleanupMgr.add(this); } } @@ -87,8 +90,15 @@ sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaTyp 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 3133de60b5..00b901be11 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 @@ namespace skia { */ 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 @@ public: return mTexture->getOrCreateSurface(dataspace, context); } + SkColorType colorType() const { return mTexture->mColorType; } + DISALLOW_COPY_AND_ASSIGN(LocalRef); private: @@ -73,10 +115,11 @@ public: 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 @@ private: 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 77e01f4967..b3975b04c0 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -37,6 +37,10 @@ const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f, 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 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin // 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 @@ static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySetting 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 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings // 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 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti .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 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { 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 47c330ffb1..d28d623b7f 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 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( 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 @@ SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGL 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 @@ void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffe 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 @@ void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffe 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 @@ void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buf 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 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, std::make_shared<AutoBackendTexture::LocalRef>(grContext, buffer->getBuffer() ->toAHardwareBuffer(), - true); + true, mTextureCleanupMgr); } const ui::Dataspace dstDataspace = @@ -792,7 +823,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, } 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 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ void SkiaGLRenderEngine::dump(std::string& result) { 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 97d3b72347..b30355bb67 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -53,13 +53,14 @@ public: 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 @@ protected: 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 @@ private: // 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 81f0b6f970..29175a227e 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 308c5ffa9f..31ad63e9ce 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -36,10 +36,10 @@ class BlurFilter; 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 @@ public: 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 40f5cf299d..856fff4b01 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.cpp +++ b/libs/renderengine/skia/debug/SkiaCapture.cpp @@ -34,7 +34,7 @@ namespace renderengine { 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 @@ void SkiaCapture::writeToFile() { // 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 @@ void SkiaCapture::writeToFile() { 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 @@ bool SkiaCapture::setupMultiFrameCapture() { 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 5e18e60f93..f1946290ca 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.h +++ b/libs/renderengine/skia/debug/SkiaCapture.h @@ -85,6 +85,8 @@ private: // 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 25c8cefd7d..e99b7ae390 100755 --- a/libs/renderengine/skia/debug/record.sh +++ b/libs/renderengine/skia/debug/record.sh @@ -16,14 +16,22 @@ elif [ "$1" == "rootandsetup" ]; then # 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 @@ sleep $(($1 / 1000 + 4)); # 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 @@ spin() { sleep $1 } -printf "MSKP captured, Waiting for file serialization to finish.\n" - -local_path=~/Downloads/$name +local_path=~/Downloads/ -# wait for the file size to stop changing +get_filename() { + adb shell getprop debug.renderengine.capture_filename +} -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_cmd="adb pull $remote_path $local_path" +echo $adb_pull_cmd +$adb_pull_cmd -adb pull "$remote_path" "$local_path" -if ! [ -f "$local_path" ] ; then - printf "something went wrong with `adb pull`." - exit 4 -fi 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 1ca7a16fe0..e2587419d2 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -43,6 +43,7 @@ constexpr int DEFAULT_DISPLAY_OFFSET = 64; constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false; namespace android { +namespace renderengine { class RenderEngineFactory { public: @@ -54,6 +55,7 @@ public: virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() { return nullptr; } + virtual bool useColorManagement() const = 0; }; class GLESRenderEngineFactory : public RenderEngineFactory { @@ -82,6 +84,8 @@ public: .build(); return renderengine::gl::GLESRenderEngine::create(reCreationArgs); } + + bool useColorManagement() const override { return false; } }; class GLESCMRenderEngineFactory : public RenderEngineFactory { @@ -110,6 +114,8 @@ public: .build(); return renderengine::gl::GLESRenderEngine::create(reCreationArgs); } + + bool useColorManagement() const override { return true; } }; class SkiaGLESRenderEngineFactory : public RenderEngineFactory { @@ -130,9 +136,16 @@ public: .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 @@ public: .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 @@ public: 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, drawLayers_fillShadow_translucentCasterWithAlpha) { } 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 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { 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 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { 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 e3917cce09..c65e731230 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -130,22 +130,22 @@ TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) { 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 9009ce46d3..ea3871f235 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(CreateInstanceFactory factory, Render } 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 @@ void RenderEngineThreaded::waitUntilInitialized() const { 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 @@ bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { 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 eb6098ed8d..9b523b2140 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -42,7 +42,7 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: // 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 91d2d58df7..3f958ba68f 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -128,8 +128,9 @@ status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, } // 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 @@ status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, 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 f57666077e..de36a7aea6 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1453,7 +1453,9 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri 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 7e069c80d0..d2b8739f96 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3787,7 +3787,7 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { 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 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { 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 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( 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 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( 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 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( 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 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( } 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 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( } 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 19abfd9adf..7fdbbfdac4 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -113,9 +113,9 @@ public: /* 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 e3e6c12485..b19b4195d1 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -67,6 +67,8 @@ static const char* DEVICE_PATH = "/dev/input"; // 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::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& vide 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 ad503fde99..7af014cb34 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -89,8 +89,7 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { } 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 @@ void InputDevice::updateExternalStylusState(const StylusState& state) { 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 e91f84e876..10c04f606c 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -406,9 +406,7 @@ void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& o 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 @@ std::vector<InputDeviceInfo> InputReader::getInputDevicesLocked() const { 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 @@ std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) { 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 c075078528..c7c8e28419 100644 --- a/services/inputflinger/reader/TouchVideoDevice.cpp +++ b/services/inputflinger/reader/TouchVideoDevice.cpp @@ -169,8 +169,9 @@ size_t TouchVideoDevice::readAndQueueFrames() { 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 291f1059c5..2f2eba78b1 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -80,7 +80,7 @@ public: 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 bc79ccf652..a00c5af4dc 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -99,9 +99,9 @@ public: 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 @@ protected: // 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 93aa6aca6d..d51acce4b2 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -473,6 +473,7 @@ protected: 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 @@ public: 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 @@ TEST_F(InputDispatcherTest, NonPointerMotionEvent_NotTransformed) { 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 @@ TEST_F(InputFilterTest, KeyEvent_InputFilter) { 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 7a11ca7396..73198bc5a1 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1524,20 +1524,6 @@ protected: } }; -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 @@ TEST_F(InputReaderTest, PolicyGetInputDevices) { 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 @@ TEST_F(InputReaderTest, GetMergedInputDevices) { 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 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { 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 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { 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 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe 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 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { 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 @@ TEST_F(LightControllerTest, MonoLight) { 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 @@ TEST_F(LightControllerTest, RGBLight) { 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 @@ TEST_F(LightControllerTest, MultiColorRGBLight) { 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 @@ TEST_F(LightControllerTest, PlayerIdLight) { 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 73a6db5128..560834f5f3 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 @@ VirtualSensor::VirtualSensor() : } // --------------------------------------------------------------------------- + +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 b5375cb9ed..ea181c9877 100644 --- a/services/sensorservice/SensorInterface.h +++ b/services/sensorservice/SensorInterface.h @@ -26,6 +26,7 @@ namespace android { // --------------------------------------------------------------------------- class SensorDevice; class SensorFusion; +class SensorService; class SensorInterface : public VirtualLightRefBase { public: @@ -43,6 +44,9 @@ public: 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 @@ public: 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 @@ protected: 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 617ceefd0e..049ae7c855 100644 --- a/services/sensorservice/SensorList.h +++ b/services/sensorservice/SensorList.h @@ -36,6 +36,15 @@ namespace SensorServiceUtil { 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 @@ public: 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 @@ private: 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 f9491969e2..228172140b 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 @@ Mutex SensorService::sPackageTargetVersionLock; 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 @@ static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSOR 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 @@ void SensorService::onFirstRef() { (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 @@ void SensorService::onFirstRef() { 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 @@ void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) { 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 @@ void SensorService::enableAllSensorsLocked(ConnectionSafeAutolock* connLock) { } 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 @@ status_t SensorService::resetToNormalModeLocked() { 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 @@ void SensorService::cleanupConnection(SensorDirectConnection* c) { 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 @@ bool SensorService::isRateCappedBasedOnPermission(const String16& opPackageName) 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 a563a60607..def661177c 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -89,9 +89,23 @@ public: 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 @@ public: 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 @@ private: }; 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 @@ private: 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 @@ private: 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 @@ private: 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 cacad52410..23779be73a 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -425,7 +425,8 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, 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 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, 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 54daa102ba..d68a0e0b47 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -43,11 +43,13 @@ namespace android { 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 @@ BufferStateLayer::~BufferStateLayer() { // 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 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex // 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 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex 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 @@ void BufferStateLayer::bufferMayChange(const sp<GraphicBuffer>& newBuffer) { // 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 08147edcfc..d738ccdb90 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -57,6 +57,7 @@ cc_library { "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 @@ cc_test { "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 289cb119ca..29937fb089 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -82,6 +82,9 @@ struct CompositionRefreshArgs { // 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 633668e1c2..14eddb1861 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 @@ class CompositionEngine; * 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 @@ struct DisplayCreationArgs { // 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,29 +64,18 @@ class DisplayCreationArgsBuilder { 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& setPixels(ui::Size pixels) { - mArgs.pixels = pixels; + DisplayCreationArgsBuilder& setConnectionType(ui::DisplayConnectionType connectionType) { + mArgs.connectionType = connectionType; 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; + DisplayCreationArgsBuilder& setPixels(ui::Size pixels) { + mArgs.pixels = pixels; return *this; } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 791e7db75c..e51019ae99 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -78,6 +78,30 @@ public: 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 @@ public: // 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 @@ static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& setti 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 257974f4e9..1416b1e3c0 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -286,7 +286,7 @@ protected: 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 a8d372c562..4110346aa3 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 @@ struct RenderSurfaceCreationArgs { // 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 @@ public: 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 @@ private: 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 54e91ae6be..bb540ea7ee 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -80,19 +80,13 @@ public: // 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 f10ff2598d..f832084f39 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -93,7 +93,7 @@ public: 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 2ffd47293c..244f8ab88b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -73,10 +73,11 @@ private: 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 3f670a1b32..7564c54218 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -103,6 +103,13 @@ struct OutputLayerCompositionState { // 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 fdcd6abe65..7cb0f6b9f2 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: // 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 @@ private: // 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 213c55e0ce..7534548d4a 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 Predictor; 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; + + 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; } + 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 fef0dfb700..a20d7b3ce7 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 @@ public: 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 @@ public: 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 4365b93bb9..be34153d31 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 @@ namespace compositionengine::impl::planner { // 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 @@ public: 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 0000000000..fb53ee04cd --- /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 4b4d37539e..8e777e3c9f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -109,7 +109,7 @@ public: 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 953eb76b63..ae1336ec9d 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -50,36 +50,14 @@ std::shared_ptr<Display> createDisplay( 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 @@ std::optional<DisplayId> Display::getDisplayId() const { 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 @@ void Display::applyClientTargetRequests(const ClientTargetProperty& clientTarget 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 088a400877..67bb1496e5 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -129,7 +129,7 @@ void Output::setLayerCachingEnabled(bool enabled) { } 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 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg devOptRepaintFlash(refreshArgs); finishFrame(refreshArgs); postFramebuffer(); - renderCachedSets(); + renderCachedSets(refreshArgs); } void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs, @@ -1188,19 +1188,6 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( !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 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( 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 @@ void Output::postFramebuffer() { 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 cd143272e9..e4e46a7cc9 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -349,7 +349,7 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t } writeOutputDependentPerFrameStateToHWC(hwcLayer.get()); - writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState); + writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer); writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough, skipLayer); @@ -471,7 +471,8 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) } 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 @@ void OutputLayer::writeOutputIndependentPerFrameStateToHWC( 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::writeSidebandStateToHWC(HWC2::Layer* hwcLayer, } 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 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, 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 b4c314c8d4..cfa740e251 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -78,6 +78,8 @@ void OutputLayerCompositionState::dump(std::string& out) const { 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 b61daeb7cd..acc7ed218e 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::hasBufferUpdate() const { } 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::updateAge(std::chrono::steady_clock::time_point now) { } } -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 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, 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 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, .dataspace = outputDataspace, .realContentIsVisible = true, .clearContent = false, - .disableBlurs = false, + .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; std::vector<renderengine::LayerSettings> layerSettings; @@ -201,6 +199,24 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, 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 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, // 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 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, 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 @@ bool CachedSet::requiresHolePunch() const { 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::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, boo } } +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 @@ void CachedSet::dump(std::string& result) const { 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 2def99d649..2bcaf60f3f 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 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, } // 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 @@ NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& lay 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; + } + + // Ensure that a cached set has a valid buffer first + if (mNewCachedSet->hasRenderedBuffer()) { + ATRACE_NAME("mNewCachedSet->hasRenderedBuffer()"); return; } - mNewCachedSet->render(renderEngine, outputState); + 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 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers 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 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers 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 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers 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 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers skipCount -= layerCount; } + priorBlurLayer = mNewCachedSet->getBlurLayer(); merged.emplace_back(std::move(*mNewCachedSet)); mNewCachedSet = std::nullopt; continue; @@ -295,17 +356,20 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers 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 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers // 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 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { 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 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { } } 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 @@ void Flattener::buildCachedSets(time_point now) { 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 @@ void Flattener::buildCachedSets(time_point now) { ++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 297c0b2f7e..be2510faba 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 @@ void Planner::reportFinalPlan( 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 0000000000..e3772a22d2 --- /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 e12cb57feb..db9437b9a8 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::Sequence; 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 @@ struct DisplayTestCommon : public testing::Test { 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 @@ struct DisplayTestCommon : public testing::Test { 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 @@ struct DisplayTestCommon : public testing::Test { 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 @@ TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) { 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 @@ TEST_F(DisplayCreationTest, createNonHwcVirtualDisplay) { 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, configuresInternalSecurePhysicalDisplay) { } 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,52 +290,17 @@ TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) { 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)); - - 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_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, 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)); - - 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())); +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()); + + EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId()); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); @@ -356,20 +308,17 @@ TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAll 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())); +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()); + + 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, setsModeUnlessNoChange) { 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 @@ using DisplayCreateRenderSurfaceTest = PartialMockDisplayTestCommon; 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 @@ TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) { 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 @@ TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) { 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 @@ TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) { 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 @@ TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) { 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 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { 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 @@ TEST_F(DisplayApplyLayerRequestsToLayersTest, appliesDeviceLayerRequests) { } /* + * 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 @@ TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) { 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, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) { } 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 @@ struct DisplayFunctionalTest : public testing::Test { 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 @@ TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) { } // 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 cd2d09e3db..64cbea91a3 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -39,14 +39,17 @@ public: 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 5bd12160cc..e9ecf3ecc5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -699,6 +699,7 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { 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 @@ TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPres /*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 6677f408ad..742b155c9f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -143,6 +143,7 @@ struct OutputTest : public testing::Test { 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 @@ struct OutputTest : public testing::Test { 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 @@ struct OutputPresentTest : public testing::Test { 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 @@ TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) { 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositi 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu kDisplayDataspace, false /* realContentIsVisible */, true /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -3879,7 +3921,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings; @@ -3923,7 +3965,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -3991,7 +4033,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4003,7 +4045,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ @@ -4048,7 +4090,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4060,7 +4102,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -4103,7 +4145,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4115,7 +4157,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), @@ -4156,7 +4198,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), @@ -4168,7 +4210,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq kOutputDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); @@ -4324,7 +4366,7 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq kOutputDataspace, true /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); @@ -4358,7 +4400,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace, false /* realContentIsVisible */, false /* clearContent */, - false /* disabledBlurs */, + compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; LayerFE::LayerSettings mShadowSettings; @@ -4404,7 +4446,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, 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 9aeb290eb5..5090bb280f 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 @@ public: 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 @@ TEST_F(RenderSurfaceTest, flipForwardsSignal) { 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 8f44677676..0acc31765a 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 testing::SetArgPointee; 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 @@ protected: 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 @@ TEST_F(CachedSetTest, incrementAge) { 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 @@ TEST_F(CachedSetTest, append) { 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 @@ TEST_F(CachedSetTest, append) { 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 @@ TEST_F(CachedSetTest, render) { 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 @@ TEST_F(CachedSetTest, render) { 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 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { 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 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { 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 @@ TEST_F(CachedSetTest, holePunch_requiresSingleLayer) { 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 @@ TEST_F(CachedSetTest, addHolePunch) { }; 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 @@ TEST_F(CachedSetTest, hasBlurBehind) { 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 7ec2c98bc1..334b855c22 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 @@ using testing::SetArgPointee; 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 @@ protected: }; 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 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la 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 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState* 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 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState* 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 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) { 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 @@ TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) { 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 @@ TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { 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 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { 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 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { // 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 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { 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 @@ TEST_F(FlattenerTest, flattenLayers_pip) { // 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 @@ TEST_F(FlattenerTest, flattenLayers_pip) { 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 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { 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 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { 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 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { 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 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { 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 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { 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 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { 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 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { 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 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // 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 0000000000..b802e51234 --- /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 0f18235751..ca4b6abc03 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -70,13 +70,14 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) 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 bf249cdb25..7e4d92308c 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -103,15 +103,21 @@ public: 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; + } + + VirtualDisplayId getVirtualId() const { + const auto id = VirtualDisplayId::tryCast(getId()); + LOG_FATAL_IF(!id); + return *id; } - DisplayId getId() const; const wp<IBinder>& getDisplayToken() const { return mDisplayToken; } int32_t getSequenceId() const { return mSequenceId; } @@ -285,4 +291,16 @@ struct DisplayDeviceCreationArgs { 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 1cbcf592db..caf0294a56 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -211,9 +211,8 @@ uint32_t Composer::getMaxVirtualDisplayCount() 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 0619b8c444..b525e63c66 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 @@ public: 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 @@ public: 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 fae95e79c3..871465d717 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -56,20 +56,16 @@ namespace hal = android::hardware::graphics::composer::hal; // 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 36876dc7f1..32f04e56bc 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 @@ using android::HWC2::ComposerCallback; class ComposerCallbackBridge : public hal::IComposerCallback { public: - ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId, - bool vsyncSwitchingSupported) - : mCallback(callback), - mSequenceId(sequenceId), - mVsyncSwitchingSupported(vsyncSwitchingSupported) {} - - Return<void> onHotplug(hal::HWDisplayId display, hal::Connection conn) override { - mCallback->onHotplugReceived(mSequenceId, display, conn); + ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported) + : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} + + 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 @@ public: 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 @@ public: } 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 @@ namespace impl { 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 @@ HWComposer::~HWComposer() { mDisplayData.clear(); } -void HWComposer::setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) { +void HWComposer::setCallback(HWC2::ComposerCallback* callback) { loadCapabilities(); loadLayerMetadataSupport(); @@ -164,10 +158,9 @@ void HWComposer::setConfiguration(HWC2::ComposerCallback* callback, int32_t sequ 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 @@ bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { 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; - 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 {}; + 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, 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 @@ status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& trans 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 @@ void HWComposer::loadLayerMetadataSupport() { } } -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 d0c0c1105a..cd6f9f516f 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 @@ public: 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 @@ public: 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 @@ public: ~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 @@ public: 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 @@ private: void loadCapabilities(); void loadLayerMetadataSupport(); - uint32_t getMaxVirtualDisplayCount() const; std::unordered_map<HalDisplayId, DisplayData> mDisplayData; @@ -416,8 +423,7 @@ private: 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 e7c69a8094..9791a2504a 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 @@ public: 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 @@ public: 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 fd18c3bf31..0cc5f33d73 100644 --- a/services/surfaceflinger/EffectLayer.cpp +++ b/services/surfaceflinger/EffectLayer.cpp @@ -66,6 +66,7 @@ std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClien layerSettings->source.solidColor = getColor().rgb; results.push_back(*layerSettings); } else if (hasBlur() || drawShadows()) { + layerSettings->skipContentDraw = true; results.push_back(*layerSettings); } @@ -126,7 +127,7 @@ const compositionengine::LayerFECompositionState* EffectLayer::getCompositionSta 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::fillsColor() const { } 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 f19e2a7863..c294ff2695 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -136,6 +136,10 @@ std::string jankTypeBitmaskToString(int32_t jankType) { 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 @@ SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t own 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 @@ SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t own 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 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, 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 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken) const { 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 @@ void FrameTimeline::registerDataSource() { 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::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( 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 42be55ae2c..15ecf130e7 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -154,7 +154,7 @@ public: 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 @@ private: // 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 @@ public: // 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 @@ public: 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 a7c870483b..2bf5602595 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -574,7 +574,8 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom 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 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom 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 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac 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 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( 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 @@ void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, 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 @@ size_t Layer::getChildrenCount() const { 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 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { const auto removeResult = mCurrentChildren.remove(layer); updateTreeHasFrameRateVote(); + layer->setGameModeForTree(0); layer->updateTreeHasFrameRateVote(); return removeResult; @@ -2353,6 +2382,16 @@ void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhy 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 @@ InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { // 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 66d70185a7..58731036d7 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -847,6 +847,13 @@ public: */ 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 @@ private: // 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 @@ private: // 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 b062acd948..9746076040 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -190,6 +190,45 @@ struct RefreshRateScore { 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 @@ RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequir } }; - std::lock_guard lock(mLock); - int noVoteLayers = 0; int minVoteLayers = 0; int maxVoteLayers = 0; @@ -592,6 +629,11 @@ const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() con 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 @@ RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId 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 @@ status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { 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 @@ status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& poli 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 ee89149fd9..342fde0e2a 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -250,6 +250,10 @@ public: 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 @@ private: 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 @@ private: 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 80f4665150..208a7678ce 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -61,6 +61,7 @@ public: 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 fac2c654cb..4b8cbfb621 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -194,8 +194,6 @@ Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) 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::setIgnorePresentFences(bool ignore) { } void Scheduler::registerLayer(Layer* layer) { - if (!mLayerHistory) return; - scheduler::LayerHistory::LayerVoteType voteType; if (!mOptions.useContentDetection || @@ -600,26 +596,22 @@ void Scheduler::registerLayer(Layer* layer) { } 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 @@ void Scheduler::chooseRefreshRateForContent() { bool frameRateOverridesChanged; { std::lock_guard<std::mutex> lock(mFeatureStateLock); - if (mFeatures.contentRequirements == summary) { - return; - } mFeatures.contentRequirements = summary; newModeId = calculateRefreshRateModeId(&consideredSignals); @@ -691,9 +680,7 @@ void Scheduler::setDisplayPowerState(bool normal) { // 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 @@ void Scheduler::touchTimerCallback(TimerState state) { // 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::onDisplayRefreshed(nsecs_t timestamp) { } 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 49d3d93f36..30a32537ad 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -268,7 +268,7 @@ private: 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 028f7a68c9..329e4a0eb4 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -86,6 +86,9 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // 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 881ee5b8f4..c1d28b1746 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 @@ const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled // --------------------------------------------------------------------------- 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 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI 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 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI 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 @@ void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) { 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 @@ void SurfaceFlinger::bootFinished() { 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 @@ void SurfaceFlinger::init() { ? 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 @@ void SurfaceFlinger::init() { 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 @@ void SurfaceFlinger::readPersistentProperties() { 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 @@ void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) { 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 @@ void SurfaceFlinger::setActiveModeInternal() { // 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 @@ void SurfaceFlinger::setActiveModeInternal() { mRefreshRateStats->setRefreshRate(refreshRate); - if (!refreshRate.equalsWithMargin(oldRefreshRate)) { - mTimeStats->incrementRefreshRateSwitches(); - } updatePhaseConfiguration(refreshRate); ATRACE_INT("ActiveConfigFPS", refreshRate.getValue()); @@ -1195,6 +1264,10 @@ void SurfaceFlinger::performSetActiveMode() { } 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 @@ nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { 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 @@ void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate, 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 @@ void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcD 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 @@ void SurfaceFlinger::onMessageRefresh() { } refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime); + refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate(); mGeometryInvalid = false; @@ -2217,11 +2274,10 @@ void SurfaceFlinger::postComposition() { 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 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, 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 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, 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 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, 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 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { 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 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, 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 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, 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 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { // 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 @@ uint32_t SurfaceFlinger::setClientStateLocked( 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 @@ uint32_t SurfaceFlinger::setClientStateLocked( 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 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } 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 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: 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 @@ void SurfaceFlinger::appendSfConfigString(std::string& result) const { 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 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r 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 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r 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 @@ status_t SurfaceFlinger::setSchedFifo(bool enabled) { 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 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, 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 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, 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 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } } - const auto display = getDisplayByLayerStack(parent->getLayerStack()); + const auto display = findDisplay(WithLayerStack(parent->getLayerStack())); if (!display) { return NAME_NOT_FOUND; } @@ -6200,9 +6304,12 @@ status_t SurfaceFlinger::renderScreenImplLocked( 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 c3820053c1..a3fa8d654f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -170,11 +170,6 @@ struct SurfaceFlingerBE { }; 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 @@ public: // 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 @@ public: // 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 @@ private: // 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 d6a0787ddc..f1b153fe7e 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -58,6 +58,21 @@ FrameTimingHistogram histogramToProto(const std::unordered_map<int32_t, int32_t> 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 @@ bool TimeStats::populateLayerAtom(std::string* pulledData) { *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 @@ static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) { 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 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR 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 @@ static bool layerNameIsValid(const std::string& layerName) { 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 @@ bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName } 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 @@ void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::st 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::setAcquireFence(int32_t layerId, uint64_t frameNumber, 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 @@ void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t pr 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 @@ void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, 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 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { // 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 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { 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 5b0f5bd13d..dd48950fd0 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -87,7 +87,7 @@ public: 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 @@ public: // 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 @@ public: 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 @@ public: ((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 @@ class TimeStats : public android::TimeStats { 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 @@ public: 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 @@ public: 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 @@ private: 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 133a5419b5..e45757ddfd 100644 --- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto +++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto @@ -166,6 +166,23 @@ message SurfaceflingerStatsLayerInfo { // 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 @@ message SurfaceflingerStatsLayerInfo { // 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 a7e7db25d7..ffb2f0921d 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -122,6 +122,20 @@ std::string TimeStatsHelper::SetFrameRateVote::toString() const { 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 @@ std::string TimeStatsHelper::TimeStatsLayer::toString() const { 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 2b37ffef30..2afff8d313 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -77,6 +77,18 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 9686523525..673239dfa9 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -89,6 +89,12 @@ int main(int, char**) { // 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 1b25a3684e..b5086fafb7 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -57,6 +57,7 @@ cc_test { "FpsTest.cpp", "FramebufferSurfaceTest.cpp", "FrameTimelineTest.cpp", + "GameModeTest.cpp", "HWComposerTest.cpp", "OneShotTimerTest.cpp", "LayerHistoryTest.cpp", @@ -68,7 +69,7 @@ cc_test { "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 3042450f29..560f139719 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -105,7 +105,9 @@ public: 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 @@ public: 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 @@ struct BaseDisplayVariant { 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 77a3e1449f..8d4a023615 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 a3e810871c..cc24323c98 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -41,9 +41,6 @@ DisplayTransactionTest::DisplayTransactionTest() { 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::injectMockScheduler() { } 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 @@ sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( 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()); - - auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, - DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */); + constexpr auto kConnectionType = ui::DisplayConnectionType::Internal; + constexpr bool kIsPrimary = true; + + 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 d68fff6345..6ce281d403 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -263,40 +263,23 @@ struct DisplayVariant { 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); - - GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get()) - .value_or(GpuVirtualDisplayId(0)); - - ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId()) - .WillByDefault(Return(desiredDisplayId)); + ceDisplayArgs.setId(DISPLAY_ID::get()) + .setPixels({WIDTH, HEIGHT}) + .setPowerAdvisor(&test->mPowerAdvisor); - 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 @@ struct HwcDisplayVariant { ::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 @@ struct NonHwcVirtualDisplayVariant 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 @@ struct HwcVirtualDisplayVariant 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 @@ struct HwcVirtualDisplayVariant } 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 dec0ff5df2..010c675574 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -76,11 +76,9 @@ protected: 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 @@ FpsReporterTest::FpsReporterTest() { 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 @@ void FpsReporterTest::setupScheduler() { 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 c6a41159c1..0a8c7486f7 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -175,6 +175,7 @@ static constexpr pid_t sPidTwo = 20; 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 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPi 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 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) { auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, sLayerNameOne, - /*isBuffer*/ true); + /*isBuffer*/ true, sGameMode); EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None); } @@ -217,7 +218,7 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) { 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 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) { 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 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { 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 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { 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 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { 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, displayFramesSlidingWindowMovesAfterLimit) { } 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 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { 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 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { 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 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_doesNotReportForInvalidTokens) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres 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 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres 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 @@ TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) { 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 @@ TEST_F(FrameTimelineTest, tracing_sanityTest) { 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) 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 @@ ProtoExpectedSurfaceFrameStart createProtoExpectedSurfaceFrameStart(int64_t cook 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 @@ ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart( 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 @@ void validateTraceEvent(const ProtoActualSurfaceFrameStart& received, 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpecte 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpecte 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced 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 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced 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 @@ TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) { 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) 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 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) 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 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli 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 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli 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 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin 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 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin 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 @@ TEST_F(FrameTimelineTest, computeFps_singleDisplayFrame_returnsZero) { 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 @@ TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) { 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 @@ TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) { 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 @@ TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) { 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 @@ TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) { 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 @@ TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) { 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 @@ TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) { 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 @@ TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) { 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 @@ TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) { 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 @@ TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) { 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 @@ TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) { 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 @@ TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) { 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 0000000000..3fa1a2c2f5 --- /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 cbf8cc21bd..655baf8a77 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -58,28 +58,26 @@ using ::testing::SetArgPointee; 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 @@ TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) { 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 @@ TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) { 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 @@ TEST_F(HWComposerSetConfigurationTest, handlesUnsupportedCallToGetLayerGenericMe 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 7ace70aeef..d04a7d73c7 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -45,9 +45,16 @@ using LayerRequirement = RefreshRateConfigs::LayerRequirement; 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 @@ protected: 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 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOv 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 9c6ad06e1d..fd3e564cd9 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp @@ -60,7 +60,6 @@ protected: 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 @@ protected: void commitTransaction(Layer* layer); TestableSurfaceFlinger mFlinger; - Hwc2::mock::Composer* mComposer = nullptr; sp<Client> mClient; sp<Layer> mParent; @@ -83,7 +81,7 @@ RefreshRateSelectionTest::RefreshRateSelectionTest() { 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 @@ void RefreshRateSelectionTest::setupScheduler() { 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 38e503fc81..423d0ccbff 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -51,9 +51,18 @@ protected: 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 @@ TEST_F(SchedulerTest, validConnectionHandle) { 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 @@ TEST_F(SchedulerTest, noLayerHistory) { 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 @@ TEST_F(SchedulerTest, calculateExtraBufferCount) { 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 c088ddc971..46ef75091e 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -118,14 +118,12 @@ protected: 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 @@ SetFrameRateTest::SetFrameRateTest() { 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 @@ void SetFrameRateTest::setupScheduler() { /*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 index 42f4cf31ba..bd89397ef6 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 @@ TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) { // 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 @@ TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) { 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 @@ TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread // -------------------------------------------------------------------- // 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 @@ TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread // 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 @@ TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread } } // 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 3f9dd01588..41fd6e316f 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -64,6 +64,11 @@ public: 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 a551248dd6..d78f36cd27 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 3e4e130a14..317cdf1592 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -54,6 +54,9 @@ using testing::StrEq; 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 @@ using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode 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 @@ public: 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 @@ static std::string genLayerName(int32_t layerId) { } 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 @@ void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumbe 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 @@ TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) { 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}); + kGameMode, JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, + 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::DisplayHAL, 1, 2, 3}); + kGameMode, JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, + 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed, 1, 2, 3}); + kGameMode, JankType::DisplayHAL, 1, 2, 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerScheduling, 1, 2, 3}); + kGameMode, JankType::AppDeadlineMissed, 1, 2, 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::PredictionError, 1, 2, 3}); + kGameMode, JankType::SurfaceFlingerScheduling, 1, 2, 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, - 3}); + 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), - JankType::None, 1, 2, 3}); + kGameMode, JankType::None, 1, 2, 3}); const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); std::string expectedResult = @@ -872,22 +881,24 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { 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}); + kGameMode, JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, + 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::DisplayHAL, 1, 2, 3}); + kGameMode, JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, + 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed, 1, 2, 3}); + kGameMode, JankType::DisplayHAL, 1, 2, 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerScheduling, 1, 2, 3}); + kGameMode, JankType::AppDeadlineMissed, 1, 2, 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::PredictionError, 1, 2, 3}); + kGameMode, JankType::SurfaceFlingerScheduling, 1, 2, 3}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, - 3}); + 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), - JankType::None, 1, 2, 3}); + kGameMode, JankType::None, 1, 2, 3}); EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty()); @@ -1039,34 +1050,36 @@ TEST_F(TimeStatsTest, globalStatsCallback) { 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, - 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, + kGameMode, JankType::DisplayHAL, 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}); + kGameMode, JankType::AppDeadlineMissed, + 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::SurfaceFlingerScheduling, 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::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 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { 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 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple, .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired, }; - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60); - - mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerCpuDeadlineMissed, - DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, - APP_DEADLINE_DELTA_3MS}); + 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::SurfaceFlingerGpuDeadlineMissed, + 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::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 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { (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 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { 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 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) { 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 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) { 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 @@ TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) { 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 @@ TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) { 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 @@ TEST_F(TimeStatsTest, canSurviveMonkey) { 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 @@ TEST_F(TimeStatsTest, refreshRateIsClampedToNearestBucket) { 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 25001d3890..546bc4a17b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -45,7 +45,7 @@ public: ::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 @@ public: 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 b7917aa00e..c1123cd6e8 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -45,7 +45,7 @@ public: ::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 @@ public: 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 2a658dd8ea..ae936e490d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -478,7 +478,7 @@ TEST_F(VSyncPredictorTest, isVSyncInPhase) { } } -TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) { +TEST_F(VSyncPredictorTest, inconsistentVsyncValueIsFlushedEventually) { EXPECT_TRUE(tracker.addVsyncTimestamp(600)); EXPECT_TRUE(tracker.needsMoreSamples()); @@ -492,6 +492,24 @@ TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) { 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 1ba3c0f56e..cb3bd73920 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -54,7 +54,8 @@ public: 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 cfc37ea8d9..0000000000 --- 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 526a847614..5aebd2f20e 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -41,18 +41,19 @@ public: 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 1010aa5195..f15a963e44 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -95,7 +95,10 @@ HalResult<void> HalResult<void>::fromStatus(status_t status) { } 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 8720d9da27..87bc34e62a 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -42,7 +42,10 @@ public: 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 af0cdb85b1..78133031f6 100644 --- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -189,8 +189,7 @@ TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) { .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 @@ TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) { 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 @@ TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) { 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 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { 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 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { 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 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { 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 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { .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 548d02817a..1593cb18ec 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -308,8 +308,7 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestTriggerSyncedWithCallbackSupport) 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, TestTriggerSyncedWithoutCallbackSuppor 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())); |