Merge changes from topic "libbinder.stable.abi" into sc-dev
* changes:
[libbinder] Enumerate symbols exported from libbinder
[libbinder] Disable Sampling PGO for x86, x86_64
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 204953c..cc0434d 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -292,8 +292,8 @@
}
}
-static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600)));
+static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags, mode_t mode) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, mode)));
if (fd.get() < 0) {
if (errno != EEXIST) {
PLOG(ERROR) << "Failed to create profile " << profile;
@@ -310,7 +310,7 @@
return fd;
}
-static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) {
+static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags, mode_t mode) {
// Do not follow symlinks when opening a profile:
// - primary profiles should not contain symlinks in their paths
// - secondary dex paths should have been already resolved and validated
@@ -320,7 +320,7 @@
// Reference profiles and snapshots are created on the fly; so they might not exist beforehand.
unique_fd fd;
if ((flags & O_CREAT) != 0) {
- fd = create_profile(uid, profile, flags);
+ fd = create_profile(uid, profile, flags, mode);
} else {
fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
}
@@ -336,6 +336,16 @@
PLOG(ERROR) << "Failed to open profile " << profile;
}
return invalid_unique_fd();
+ } else {
+ // If we just create the file we need to set its mode because on Android
+ // open has a mask that only allows owner access.
+ if ((flags & O_CREAT) != 0) {
+ if (fchmod(fd.get(), mode) != 0) {
+ PLOG(ERROR) << "Could not set mode " << std::hex << mode << std::dec
+ << " on profile" << profile;
+ // Not a terminal failure.
+ }
+ }
}
return fd;
@@ -345,20 +355,29 @@
const std::string& location, bool is_secondary_dex) {
std::string profile = create_current_profile_path(user, package_name, location,
is_secondary_dex);
- return open_profile(uid, profile, O_RDONLY);
+ return open_profile(uid, profile, O_RDONLY, /*mode=*/ 0);
}
static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
- return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+ return open_profile(
+ uid,
+ profile,
+ read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
}
static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile_path = create_reference_profile_path(package_name, location,
is_secondary_dex);
- unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+ unique_fd ufd = open_profile(
+ uid,
+ profile_path,
+ read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
+
return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
clear_profile(path);
});
@@ -367,7 +386,7 @@
static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
const std::string& location) {
std::string profile = create_snapshot_profile_path(package_name, location);
- return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);
+ return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
}
static void open_profile_files(uid_t uid, const std::string& package_name,
@@ -2484,7 +2503,7 @@
for (size_t i = 0; i < profiles.size(); ) {
std::vector<unique_fd> profiles_fd;
for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
- unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
+ unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY, /*mode=*/ 0);
if (fd.get() >= 0) {
profiles_fd.push_back(std::move(fd));
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index e272025..216347e 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -919,7 +919,7 @@
return;
}
- // Check that the snapshot was created witht he expected acess flags.
+ // Check that the snapshot was created with the expected access flags.
CheckFileAccess(snap_profile_, kSystemUid, kSystemGid, 0600 | S_IFREG);
// The snapshot should be equivalent to the merge of profiles.
@@ -962,8 +962,8 @@
return;
}
- // Check that the snapshot was created witht he expected acess flags.
- CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+ // Check that the snapshot was created with the expected access flags.
+ CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0640 | S_IFREG);
// The snapshot should be equivalent to the merge of profiles.
std::string ref_profile_content = ref_profile_ + ".expected";
diff --git a/include/input/Input.h b/include/input/Input.h
index d4defa8..e8678d2 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -24,6 +24,9 @@
*/
#include <android/input.h>
+#ifdef __linux__
+#include <android/os/IInputConstants.h>
+#endif
#include <math.h>
#include <stdint.h>
#include <ui/Transform.h>
@@ -219,7 +222,16 @@
POLICY_FLAG_GESTURE = 0x00000008,
POLICY_FLAG_RAW_MASK = 0x0000ffff,
+#ifdef __linux__
+ POLICY_FLAG_INPUTFILTER_TRUSTED = android::os::IInputConstants::POLICY_FLAG_INPUTFILTER_TRUSTED,
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
+ android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+#else
+ POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000,
+
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000,
+#endif
/* These flags are set by the input dispatcher. */
// Indicates that the input event was injected.
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7f0324a..1955104 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -318,6 +318,8 @@
const std::string& name, InputDeviceConfigurationFileType type);
enum ReservedInputDeviceId : int32_t {
+ // Device id assigned to input events generated inside accessibility service
+ ACCESSIBILITY_DEVICE_ID = -2,
// Device id of a special "virtual" keyboard that is always present.
VIRTUAL_KEYBOARD_ID = -1,
// Device id of the "built-in" keyboard if there is one.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 3b3fd08..3920129 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -24,7 +24,6 @@
use std::ffi::{c_void, CString};
use std::mem::ManuallyDrop;
use std::ops::Deref;
-use std::ptr;
/// Rust wrapper around Binder remotable objects.
///
@@ -273,7 +272,7 @@
/// Must be called with a valid pointer to a `T` object. After this call,
/// the pointer will be invalid and should not be dereferenced.
unsafe extern "C" fn on_destroy(object: *mut c_void) {
- ptr::drop_in_place(object as *mut T)
+ Box::from_raw(object as *mut T);
}
/// Called whenever a new, local `AIBinder` object is needed of a specific
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9610437..660c5bd 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -182,12 +182,12 @@
void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
sp<SurfaceControl> surfaceControl) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
mJankListeners.insert({surfaceControl->getHandle(), listener});
}
void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
if (it->second == listener) {
it = mJankListeners.erase(it);
@@ -210,13 +210,13 @@
void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
mSurfaceStatsListeners.insert({surfaceControl->getHandle(),
SurfaceStatsCallbackEntry(context, cookie, listener)});
}
void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
for (auto it = mSurfaceStatsListeners.begin(); it != mSurfaceStatsListeners.end();) {
auto [itContext, itCookie, itListener] = it->second;
if (itContext == context && itCookie == cookie) {
@@ -242,8 +242,6 @@
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
- std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
- std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -259,8 +257,6 @@
* sp<SurfaceControl> that could possibly exist for the callbacks.
*/
callbacksMap = mCallbacks;
- jankListenersMap = mJankListeners;
- surfaceListeners = mSurfaceStatsListeners;
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto& callbackId : transactionStats.callbackIds) {
mCallbacks.erase(callbackId);
@@ -341,15 +337,26 @@
surfaceControlStats);
}
for (const auto& surfaceStats : transactionStats.surfaceStats) {
- auto listenerRange = surfaceListeners.equal_range(surfaceStats.surfaceControl);
- for (auto it = listenerRange.first; it != listenerRange.second; it++) {
- auto entry = it->second;
- entry.callback(entry.context, transactionStats.latchTime,
- transactionStats.presentFence, surfaceStats);
+ {
+ // Acquire surface stats listener lock such that we guarantee that after calling
+ // unregister, there won't be any further callback.
+ std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
+ auto listenerRange = mSurfaceStatsListeners.equal_range(
+ surfaceStats.surfaceControl);
+ for (auto it = listenerRange.first; it != listenerRange.second; it++) {
+ auto entry = it->second;
+ entry.callback(entry.context, transactionStats.latchTime,
+ transactionStats.presentFence, surfaceStats);
+ }
}
if (surfaceStats.jankData.empty()) continue;
- auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
+
+ // Acquire jank listener lock such that we guarantee that after calling unregister,
+ // there won't be any further callback.
+ std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+ auto copy = mJankListeners;
+ auto jankRange = copy.equal_range(surfaceStats.surfaceControl);
for (auto it = jankRange.first; it != jankRange.second; it++) {
it->second->onJankDataAvailable(surfaceStats.jankData);
}
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 62a782f..5aa132c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -652,6 +652,12 @@
std::mutex mMutex;
+ // This lock needs to be recursive so we can unregister a callback from within that callback.
+ std::recursive_mutex mJankListenerMutex;
+
+ // This lock needs to be recursive so we can unregister a callback from within that callback.
+ std::recursive_mutex mSurfaceStatsListenerMutex;
+
bool mListening GUARDED_BY(mMutex) = false;
int64_t mCallbackIdCounter GUARDED_BY(mMutex) = 1;
@@ -674,11 +680,16 @@
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
GUARDED_BY(mMutex);
- std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+
+ // This is protected by mJankListenerMutex, but GUARDED_BY isn't supported for
+ // std::recursive_mutex
+ std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners;
std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
mReleaseBufferCallbacks GUARDED_BY(mMutex);
- std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry>
- mSurfaceStatsListeners GUARDED_BY(mMutex);
+
+ // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
+ // std::recursive_mutex
+ std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
public:
static sp<TransactionCompletedListener> getInstance();
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 4b90844..3038d9d 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -40,4 +40,18 @@
* available.
*/
const int INVALID_INPUT_EVENT_ID = 0;
+
+ /**
+ * The injected event was originally sent from InputDispatcher. Most likely, the journey of the
+ * event looked as follows:
+ * InputDispatcherPolicyInterface::filterInputEvent -> InputFilter.java::onInputEvent ->
+ * InputFilter.java::sendInputEvent -> InputDispatcher::injectInputEvent, without being modified
+ * along the way.
+ */
+ const int POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000;
+
+ /**
+ * The input event was injected from accessibility
+ */
+ const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 3c58238..b5dd8ac 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,9 +515,10 @@
return mDrawingBuffer.get();
}
-void GLESRenderEngine::primeCache() {
+std::future<void> GLESRenderEngine::primeCache() {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
mUseColorManagement, mPrecacheToneMapperShaderOnly);
+ return {};
}
base::unique_fd GLESRenderEngine::flush() {
@@ -969,37 +970,17 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
+bool GLESRenderEngine::canSkipPostRenderCleanup() const {
+ return mPriorResourcesCleaned ||
+ (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled);
+}
+
+void GLESRenderEngine::cleanupPostRender() {
ATRACE_CALL();
- if (mPriorResourcesCleaned ||
- (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
+ if (canSkipPostRenderCleanup()) {
// If we don't have a prior frame needing cleanup, then don't do anything.
- return false;
- }
-
- // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
- // not need to keep memory around if we don't need to do so.
- if (mode == CleanupMode::CLEAN_ALL) {
- // TODO: SurfaceFlinger memory utilization may benefit from resetting
- // texture bindings as well. Assess if it does and there's no performance regression
- // when rebinding the same image data to the same texture, and if so then its mode
- // behavior can be tweaked.
- if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
- for (auto [textureName, bufferId] : mTextureView) {
- if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
- static_cast<GLeglImageOES>(mPlaceholderImage));
- mTextureView[textureName] = std::nullopt;
- checkErrors();
- }
- }
- }
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- mImageCache.clear();
- }
+ return;
}
// Bind the texture to placeholder so that backing image data can be freed.
@@ -1010,7 +991,6 @@
// we could no-op repeated calls of this method instead.
mLastDrawFence = nullptr;
mPriorResourcesCleaned = true;
- return true;
}
void GLESRenderEngine::cleanFramebufferCache() {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index e7ed9c0..915dba3 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() override;
+ std::future<void> primeCache() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
bool isProtected() const override { return mInProtectedContext; }
@@ -68,7 +68,7 @@
const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) override;
- bool cleanupPostRender(CleanupMode mode) override;
+ void cleanupPostRender() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
void onPrimaryDisplaySizeChanged(ui::Size size) override {}
@@ -106,6 +106,7 @@
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
EXCLUDES(mRenderingMutex);
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+ bool canSkipPostRenderCleanup() const override;
private:
friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index d1bbcc5..ac0affb 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -29,6 +29,7 @@
#include <ui/GraphicTypes.h>
#include <ui/Transform.h>
+#include <future>
#include <memory>
/**
@@ -48,6 +49,11 @@
*/
#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 {
@@ -94,17 +100,13 @@
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
- RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
-
- RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
-
virtual ~RenderEngine() = 0;
// ----- BEGIN DEPRECATED INTERFACE -----
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual void primeCache() = 0;
+ virtual std::future<void> primeCache() = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
@@ -112,25 +114,6 @@
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
- enum class CleanupMode {
- CLEAN_OUTPUT_RESOURCES,
- CLEAN_ALL,
- };
- // Clean-up method that should be called on the main thread after the
- // drawFence returned by drawLayers fires. This method will free up
- // resources used by the most recently drawn frame. If the frame is still
- // being drawn, then this call is silently ignored.
- //
- // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
- // output framebuffer are cleaned up, including the sibling texture.
- //
- // If mode is CLEAN_ALL, then we also cleanup resources related to any input
- // buffers.
- //
- // Returns true if resources were cleaned up, and false if we didn't need to
- // do any work.
- virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
-
// queries that are required to be thread safe
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
@@ -184,6 +167,13 @@
const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) = 0;
+
+ // Clean-up method that should be called on the main thread after the
+ // drawFence returned by drawLayers fires. This method will free up
+ // resources used by the most recently drawn frame. If the frame is still
+ // being drawn, then the implementation is free to silently ignore this call.
+ virtual void cleanupPostRender() = 0;
+
virtual void cleanFramebufferCache() = 0;
// Returns the priority this context was actually created with. Note: this may not be
// the same as specified at context creation time, due to implementation limits on the
@@ -204,6 +194,10 @@
static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
protected:
+ RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+
+ RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
+
// Maps GPU resources for this buffer.
// Note that work may be deferred to an additional thread, i.e. this call
// is made asynchronously, but the caller can expect that map/unmap calls
@@ -228,8 +222,15 @@
// that's conflict serializable, i.e. unmap a buffer should never occur before binding the
// buffer if the caller called mapExternalTextureBuffer before calling unmap.
virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+
+ // A thread safe query to determine if any post rendering cleanup is necessary. Returning true
+ // is a signal that calling the postRenderCleanup method would be a no-op and that callers can
+ // avoid any thread synchronization that may be required by directly calling postRenderCleanup.
+ virtual bool canSkipPostRenderCleanup() const = 0;
+
friend class ExternalTexture;
friend class threaded::RenderEngineThreaded;
+ friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
const RenderEngineType mRenderEngineType;
};
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 27dbd1e..0175af3 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD0(primeCache, void());
+ MOCK_METHOD0(primeCache, std::future<void>());
MOCK_METHOD1(dump, void(std::string&));
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
@@ -45,7 +45,8 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
- MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
+ MOCK_METHOD0(cleanupPostRender, void());
+ MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 8356005..5c122d4 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -29,8 +29,8 @@
namespace skia {
AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
- bool isOutputBuffer)
- : mIsOutputBuffer(isOutputBuffer) {
+ bool isOutputBuffer, CleanupManager& cleanupMgr)
+ : mCleanupMgr(cleanupMgr), mIsOutputBuffer(isOutputBuffer) {
ATRACE_CALL();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
@@ -49,6 +49,13 @@
this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
}
+AutoBackendTexture::~AutoBackendTexture() {
+ if (mBackendTexture.isValid()) {
+ mDeleteProc(mImageCtx);
+ mBackendTexture = {};
+ }
+}
+
void AutoBackendTexture::unref(bool releaseLocalResources) {
if (releaseLocalResources) {
mSurface = nullptr;
@@ -57,11 +64,7 @@
mUsageCount--;
if (mUsageCount <= 0) {
- if (mBackendTexture.isValid()) {
- mDeleteProc(mImageCtx);
- mBackendTexture = {};
- }
- delete this;
+ mCleanupMgr.add(this);
}
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index a9e8430..00b901b 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -25,6 +25,9 @@
#include "android-base/macros.h"
+#include <mutex>
+#include <vector>
+
namespace android {
namespace renderengine {
namespace skia {
@@ -36,13 +39,50 @@
*/
class AutoBackendTexture {
public:
+ // Manager class that is responsible for the immediate or deferred cleanup
+ // of AutoBackendTextures. Clients of AutoBackendTexture are responsible for
+ // ensuring that access to this class is thread safe. Clients also control when
+ // the resources are reclaimed by setting the manager into deferred mode.
+ class CleanupManager {
+ public:
+ CleanupManager() = default;
+ void add(AutoBackendTexture* abt) {
+ if (mDeferCleanup) {
+ mCleanupList.push_back(abt);
+ } else {
+ delete abt;
+ }
+ }
+
+ void setDeferredStatus(bool enabled) { mDeferCleanup = enabled; }
+
+ bool isEmpty() const { return mCleanupList.empty(); }
+
+ // If any AutoBackedTextures were added while in deferred mode this method
+ // will ensure they are deleted before returning. It must only be called
+ // on the thread where the GPU context that created the AutoBackedTexture
+ // is active.
+ void cleanup() {
+ for (auto abt : mCleanupList) {
+ delete abt;
+ }
+ mCleanupList.clear();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CleanupManager);
+ bool mDeferCleanup = false;
+ std::vector<AutoBackendTexture*> mCleanupList;
+ };
+
// Local reference that supports RAII-style management of an AutoBackendTexture
// AutoBackendTexture by itself can't be managed in a similar fashion because
// of shared ownership with Skia objects, so we wrap it here instead.
class LocalRef {
public:
- LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer) {
- mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer);
+ LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+ CleanupManager& cleanupMgr) {
+ mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr);
mTexture->ref();
}
@@ -75,10 +115,11 @@
private:
// Creates a GrBackendTexture whose contents come from the provided buffer.
- AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer);
+ AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+ CleanupManager& cleanupMgr);
// The only way to invoke dtor is with unref, when mUsageCount is 0.
- ~AutoBackendTexture() {}
+ ~AutoBackendTexture();
void ref() { mUsageCount++; }
@@ -100,6 +141,8 @@
GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+ CleanupManager& mCleanupMgr;
+
static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
static void releaseImageProc(SkImage::ReleaseContext releaseContext);
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 8a41395..b3975b0 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -292,7 +292,11 @@
drawSolidLayers(renderengine, display, dstTexture);
drawShadowLayers(renderengine, display, srcTexture);
- drawBlurLayers(renderengine, display, dstTexture);
+
+ if (renderengine->supportsBackgroundBlur()) {
+ drawBlurLayers(renderengine, display, dstTexture);
+ }
+
// The majority of shaders are related to sampling images.
drawImageLayers(renderengine, display, dstTexture, srcTexture);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index d4b139c..d28d623 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -235,8 +235,9 @@
return engine;
}
-void SkiaGLRenderEngine::primeCache() {
+std::future<void> SkiaGLRenderEngine::primeCache() {
Cache::primeShaderCache(this);
+ return {};
}
EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
@@ -316,6 +317,7 @@
GrContextOptions options;
options.fDisableDriverCorrectnessWorkarounds = true;
options.fDisableDistanceFieldPaths = true;
+ options.fReducedShaderVariations = true;
options.fPersistentCache = &mSkSLCacheMonitor;
mGrContext = GrDirectContext::MakeGL(glInterface, options);
if (useProtectedContext(true)) {
@@ -507,17 +509,18 @@
return;
}
// We currently don't attempt to map a buffer if the buffer contains protected content
- // or we are using a protected context because GPU resources for protected buffers is
- // much more limited.
+ // because GPU resources for protected buffers is much more limited.
const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
- if (isProtectedBuffer || mInProtectedContext) {
+ if (isProtectedBuffer) {
return;
}
ATRACE_CALL();
- // If we were to support caching protected buffers then we will need to switch the currently
- // bound context if we are not already using the protected context (and subsequently switch
- // back after the buffer is cached).
+ // If we were to support caching protected buffers then we will need to switch the
+ // currently bound context if we are not already using the protected context (and subsequently
+ // switch back after the buffer is cached). However, for non-protected content we can bind
+ // the texture in either GL context because they are initialized with the same share_context
+ // which allows the texture state to be shared between them.
auto grContext = getActiveGrContext();
auto& cache = mTextureCache;
@@ -528,7 +531,7 @@
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
- isRenderable);
+ isRenderable, mTextureCleanupMgr);
cache.insert({buffer->getId(), imageTextureRef});
}
}
@@ -551,12 +554,36 @@
if (iter->second == 0) {
mTextureCache.erase(buffer->getId());
- mProtectedTextureCache.erase(buffer->getId());
mGraphicBufferExternalRefs.erase(buffer->getId());
}
}
}
+bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return mTextureCleanupMgr.isEmpty();
+}
+
+void SkiaGLRenderEngine::cleanupPostRender() {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mTextureCleanupMgr.cleanup();
+}
+
+// Helper class intended to be used on the stack to ensure that texture cleanup
+// is deferred until after this class goes out of scope.
+class DeferTextureCleanup final {
+public:
+ DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
+ mMgr.setDeferredStatus(true);
+ }
+ ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
+ AutoBackendTexture::CleanupManager& mMgr;
+};
+
sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
sk_sp<SkShader> shader,
const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
@@ -703,7 +730,10 @@
validateOutputBufferUsage(buffer->getBuffer());
auto grContext = getActiveGrContext();
- auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+ auto& cache = mTextureCache;
+
+ // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
+ DeferTextureCleanup dtc(mTextureCleanupMgr);
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
@@ -713,7 +743,7 @@
std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->getBuffer()
->toAHardwareBuffer(),
- true);
+ true, mTextureCleanupMgr);
}
const ui::Dataspace dstDataspace =
@@ -969,7 +999,7 @@
imageTextureRef = std::make_shared<
AutoBackendTexture::LocalRef>(grContext,
item.buffer->getBuffer()->toAHardwareBuffer(),
- false);
+ false, mTextureCleanupMgr);
}
// isOpaque means we need to ignore the alpha in the image,
@@ -1446,12 +1476,6 @@
StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
gpuProtectedReporter.logOutput(result, true);
- StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
- mProtectedTextureCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, unused] : mProtectedTextureCache) {
- StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
- }
StringAppendF(&result, "\n");
StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
for (const auto& [linearEffect, unused] : mRuntimeEffects) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 97d3b72..b30355b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -53,13 +53,14 @@
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() override;
+ std::future<void> primeCache() override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) override;
- void cleanFramebufferCache() override {}
+ void cleanupPostRender() override;
+ void cleanFramebufferCache() override{};
int getContextPriority() override;
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
@@ -75,6 +76,7 @@
size_t getMaxViewportDims() const override;
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ bool canSkipPostRenderCleanup() const override;
private:
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -126,19 +128,18 @@
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
GUARDED_BY(mRenderingMutex);
- // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+ // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
- mProtectedTextureCache GUARDED_BY(mRenderingMutex);
std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+ AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
// multiple threads is guaranteed thread-safe.
- std::mutex mRenderingMutex;
+ mutable std::mutex mRenderingMutex;
sp<Fence> mLastDrawFence;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 81f0b6f..29175a2 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -14,13 +14,22 @@
* limitations under the License.
*/
-//#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "SkiaRenderEngine.h"
+
+#include <android-base/properties.h>
+#include <src/core/SkTraceEventCommon.h>
+
namespace android {
namespace renderengine {
-namespace skia {} // namespace skia
+namespace skia {
+SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {
+ SkAndroidFrameworkTraceUtil::setEnableTracing(
+ base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
+}
+} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 308c5ff..31ad63e 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -36,10 +36,10 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
+ SkiaRenderEngine(RenderEngineType type);
~SkiaRenderEngine() override {}
- virtual void primeCache() override{};
+ virtual std::future<void> primeCache() override { return {}; };
virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
virtual bool isProtected() const override { return false; } // mInProtectedContext; }
@@ -53,15 +53,14 @@
base::unique_fd* /*drawFence*/) override {
return 0;
};
- virtual bool cleanupPostRender(CleanupMode) override { return true; };
virtual int getContextPriority() override { return 0; }
virtual void assertShadersCompiled(int numShaders) {}
virtual int reportShadersCompiled() { return 0; }
protected:
virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
- bool /*isRenderable*/) override;
- virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override;
+ bool /*isRenderable*/) override = 0;
+ virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
};
} // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index dfab6e8..e258741 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -43,6 +43,7 @@
constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
namespace android {
+namespace renderengine {
class RenderEngineFactory {
public:
@@ -1779,13 +1780,6 @@
}
TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
- const auto& renderEngineFactory = GetParam();
-
- if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
- // GLES-specific test
- return;
- }
-
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -1810,53 +1804,9 @@
sync_wait(fd, -1);
}
// Only cleanup the first time.
- EXPECT_TRUE(mRE->cleanupPostRender(
- renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
- EXPECT_FALSE(mRE->cleanupPostRender(
- renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
-}
-
-TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
- const auto& renderEngineFactory = GetParam();
-
- if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
- // GLES-specific test
- return;
- }
-
- initializeRenderEngine();
-
- renderengine::DisplaySettings settings;
- settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- settings.physicalDisplay = fullscreenRect();
- settings.clip = fullscreenRect();
-
- std::vector<const renderengine::LayerSettings*> layers;
- renderengine::LayerSettings layer;
- layer.geometry.boundaries = fullscreenRect().toFloatRect();
- BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layer.alpha = 1.0;
- layers.push_back(&layer);
-
- base::unique_fd fence;
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
-
- const int fd = fence.get();
- if (fd >= 0) {
- sync_wait(fd, -1);
- }
-
- uint64_t bufferId = layer.source.buffer.buffer->getBuffer()->getId();
- uint32_t texName = layer.source.buffer.textureName;
- EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
-
- EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
-
- // Now check that our view of memory is good.
- EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
- EXPECT_EQ(std::nullopt, mGLESRE->getBufferIdForTextureNameForTesting(bufferId));
- EXPECT_TRUE(mGLESRE->isTextureNameKnownForTesting(texName));
+ EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
+ mRE->cleanupPostRender();
+ EXPECT_TRUE(mRE->canSkipPostRenderCleanup());
}
TEST_P(RenderEngineTest, testRoundedCornersCrop) {
@@ -2080,6 +2030,7 @@
expectBufferColor(rect, 0, 255, 0, 255);
}
}
+} // namespace renderengine
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index e3917cc..c65e731 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -130,22 +130,22 @@
ASSERT_EQ(true, result);
}
-TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
- EXPECT_CALL(*mRenderEngine,
- cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
- .WillOnce(Return(false));
- status_t result =
- mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
- ASSERT_EQ(false, result);
+TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) {
+ EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0);
+ mThreadedRE->cleanupPostRender();
+
+ // call ANY synchronous function to ensure that cleanupPostRender has completed.
+ mThreadedRE->getContextPriority();
}
-TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
- EXPECT_CALL(*mRenderEngine,
- cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
- .WillOnce(Return(true));
- status_t result =
- mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
- ASSERT_EQ(true, result);
+TEST_F(RenderEngineThreadedTest, PostRenderCleanup_notSkipped) {
+ EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return());
+ mThreadedRE->cleanupPostRender();
+
+ // call ANY synchronous function to ensure that cleanupPostRender has completed.
+ mThreadedRE->getContextPriority();
}
TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsFalse) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 9009ce4..ea3871f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -24,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <private/gui/SyncFeatures.h>
+#include <processgroup/processgroup.h>
#include <utils/Trace.h>
#include "gl/GLESRenderEngine.h"
@@ -48,44 +49,74 @@
}
RenderEngineThreaded::~RenderEngineThreaded() {
- {
- std::lock_guard lock(mThreadMutex);
- mRunning = false;
- mCondition.notify_one();
- }
+ mRunning = false;
+ mCondition.notify_one();
if (mThread.joinable()) {
mThread.join();
}
}
+status_t RenderEngineThreaded::setSchedFifo(bool enabled) {
+ static constexpr int kFifoPriority = 2;
+ static constexpr int kOtherPriority = 0;
+
+ struct sched_param param = {0};
+ int sched_policy;
+ if (enabled) {
+ sched_policy = SCHED_FIFO;
+ param.sched_priority = kFifoPriority;
+ } else {
+ sched_policy = SCHED_OTHER;
+ param.sched_priority = kOtherPriority;
+ }
+
+ if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
+ return -errno;
+ }
+ return NO_ERROR;
+}
+
// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO");
+ if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
+ ALOGW("Failed to set render-engine task profile!");
+ }
+
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO");
}
mRenderEngine = factory();
- std::unique_lock<std::mutex> lock(mThreadMutex);
pthread_setname_np(pthread_self(), mThreadName);
{
- std::unique_lock<std::mutex> lock(mInitializedMutex);
+ std::scoped_lock lock(mInitializedMutex);
mIsInitialized = true;
}
mInitializedCondition.notify_all();
while (mRunning) {
- if (!mFunctionCalls.empty()) {
- auto task = mFunctionCalls.front();
- mFunctionCalls.pop();
- task(*mRenderEngine);
+ const auto getNextTask = [this]() -> std::optional<Work> {
+ std::scoped_lock lock(mThreadMutex);
+ if (!mFunctionCalls.empty()) {
+ Work task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ return std::make_optional<Work>(task);
+ }
+ return std::nullopt;
+ };
+
+ const auto task = getNextTask();
+
+ if (task) {
+ (*task)(*mRenderEngine);
}
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
return !mRunning || !mFunctionCalls.empty();
});
@@ -100,18 +131,31 @@
mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
}
-void RenderEngineThreaded::primeCache() {
+std::future<void> RenderEngineThreaded::primeCache() {
+ const auto resultPromise = std::make_shared<std::promise<void>>();
+ std::future<void> resultFuture = resultPromise->get_future();
ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::primeCache");
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_OTHER for primeCache");
+ }
+
instance.primeCache();
+ resultPromise->set_value();
+
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO for primeCache");
+ }
});
}
mCondition.notify_one();
+
+ return resultFuture;
}
void RenderEngineThreaded::dump(std::string& result) {
@@ -231,19 +275,26 @@
return resultFuture.get();
}
-bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
- std::promise<bool> resultPromise;
- std::future<bool> resultFuture = resultPromise.get_future();
+void RenderEngineThreaded::cleanupPostRender() {
+ if (canSkipPostRenderCleanup()) {
+ return;
+ }
+
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::cleanupPostRender");
- bool returnValue = instance.cleanupPostRender(mode);
- resultPromise.set_value(returnValue);
+ mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ instance.cleanupPostRender();
});
}
mCondition.notify_one();
- return resultFuture.get();
+}
+
+bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
+ waitUntilInitialized();
+ return mRenderEngine->canSkipPostRenderCleanup();
}
status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index eb6098e..9b523b2 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
- void primeCache() override;
+ std::future<void> primeCache() override;
void dump(std::string& result) override;
@@ -54,7 +54,7 @@
bool isProtected() const override;
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
- bool cleanupPostRender(CleanupMode mode) override;
+ void cleanupPostRender() override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
@@ -70,10 +70,12 @@
protected:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ bool canSkipPostRenderCleanup() const override;
private:
void threadMain(CreateInstanceFactory factory);
void waitUntilInitialized() const;
+ static status_t setSchedFifo(bool enabled);
/* ------------------------------------------------------------------------
* Threading
@@ -82,9 +84,10 @@
// Protects the creation and destruction of mThread.
mutable std::mutex mThreadMutex;
std::thread mThread GUARDED_BY(mThreadMutex);
- bool mRunning GUARDED_BY(mThreadMutex) = true;
- mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
- GUARDED_BY(mThreadMutex);
+ std::atomic<bool> mRunning = true;
+
+ using Work = std::function<void(renderengine::RenderEngine&)>;
+ mutable std::queue<Work> mFunctionCalls GUARDED_BY(mThreadMutex);
mutable std::condition_variable mCondition;
// Used to allow select thread safe methods to be accessed without requiring the
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 847b351..efbe613 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -82,9 +82,13 @@
void egl_surface_t::disconnect() {
if (win != nullptr && connected) {
- native_window_set_buffers_format(win, 0);
- if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
- ALOGW("EGLNativeWindowType %p disconnect failed", win);
+ // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
+ // native_window_* calls, so don't do them here.
+ if (!cnx->useAngle) {
+ native_window_set_buffers_format(win, 0);
+ if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
+ ALOGW("EGLNativeWindowType %p disconnect failed", win);
+ }
}
connected = false;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index cf433c0..d2b8739 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3787,7 +3787,7 @@
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
- policyFlags |= POLICY_FLAG_FILTERED;
+ policyFlags |= POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
@@ -4009,6 +4009,19 @@
policyFlags |= POLICY_FLAG_TRUSTED;
}
+ // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events
+ // that have gone through the InputFilter. If the event passed through the InputFilter,
+ // but did not get modified, assign the provided device id. If the InputFilter modifies the
+ // events in any way, it is responsible for removing this flag.
+ // If the injected event originated from accessibility, assign the accessibility device id,
+ // so that it can be distinguished from regular injected events.
+ int32_t resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
+ if (policyFlags & POLICY_FLAG_INPUTFILTER_TRUSTED) {
+ resolvedDeviceId = event->getDeviceId();
+ } else if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+ resolvedDeviceId = ACCESSIBILITY_DEVICE_ID;
+ }
+
std::queue<std::unique_ptr<EventEntry>> injectedEntries;
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -4021,10 +4034,10 @@
int32_t flags = incomingKey.getFlags();
int32_t keyCode = incomingKey.getKeyCode();
int32_t metaState = incomingKey.getMetaState();
- accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action,
+ accelerateMetaShortcuts(resolvedDeviceId, action,
/*byref*/ keyCode, /*byref*/ metaState);
KeyEvent keyEvent;
- keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+ keyEvent.initialize(incomingKey.getId(), resolvedDeviceId, incomingKey.getSource(),
incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
incomingKey.getDownTime(), incomingKey.getEventTime());
@@ -4045,7 +4058,7 @@
mLock.lock();
std::unique_ptr<KeyEntry> injectedEntry =
std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
- VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+ resolvedDeviceId, incomingKey.getSource(),
incomingKey.getDisplayId(), policyFlags, action,
flags, keyCode, incomingKey.getScanCode(), metaState,
incomingKey.getRepeatCount(),
@@ -4055,18 +4068,18 @@
}
case AINPUT_EVENT_TYPE_MOTION: {
- const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
- int32_t action = motionEvent->getAction();
- size_t pointerCount = motionEvent->getPointerCount();
- const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
- int32_t actionButton = motionEvent->getActionButton();
- int32_t displayId = motionEvent->getDisplayId();
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ int32_t action = motionEvent.getAction();
+ size_t pointerCount = motionEvent.getPointerCount();
+ const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
+ int32_t actionButton = motionEvent.getActionButton();
+ int32_t displayId = motionEvent.getDisplayId();
if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
return InputEventInjectionResult::FAILED;
}
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
- nsecs_t eventTime = motionEvent->getEventTime();
+ nsecs_t eventTime = motionEvent.getEventTime();
android::base::Timer t;
mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
@@ -4076,47 +4089,46 @@
}
mLock.lock();
- const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
- const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+ const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
+ const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
std::unique_ptr<MotionEntry> injectedEntry =
- std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
- VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags, action,
- actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(),
- motionEvent->getButtonState(),
- motionEvent->getClassification(),
- motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(),
- motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(),
- uint32_t(pointerCount), pointerProperties,
- samplePointerCoords, motionEvent->getXOffset(),
- motionEvent->getYOffset());
+ std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
+ resolvedDeviceId, motionEvent.getSource(),
+ motionEvent.getDisplayId(), policyFlags, action,
+ actionButton, motionEvent.getFlags(),
+ motionEvent.getMetaState(),
+ motionEvent.getButtonState(),
+ motionEvent.getClassification(),
+ motionEvent.getEdgeFlags(),
+ motionEvent.getXPrecision(),
+ motionEvent.getYPrecision(),
+ motionEvent.getRawXCursorPosition(),
+ motionEvent.getRawYCursorPosition(),
+ motionEvent.getDownTime(), uint32_t(pointerCount),
+ pointerProperties, samplePointerCoords,
+ motionEvent.getXOffset(),
+ motionEvent.getYOffset());
injectedEntries.push(std::move(injectedEntry));
- for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+ for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
samplePointerCoords += pointerCount;
std::unique_ptr<MotionEntry> nextInjectedEntry =
- std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
- VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags,
- action, actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(),
- motionEvent->getButtonState(),
- motionEvent->getClassification(),
- motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(),
- motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(),
+ std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
+ resolvedDeviceId, motionEvent.getSource(),
+ motionEvent.getDisplayId(), policyFlags,
+ action, actionButton, motionEvent.getFlags(),
+ motionEvent.getMetaState(),
+ motionEvent.getButtonState(),
+ motionEvent.getClassification(),
+ motionEvent.getEdgeFlags(),
+ motionEvent.getXPrecision(),
+ motionEvent.getYPrecision(),
+ motionEvent.getRawXCursorPosition(),
+ motionEvent.getRawYCursorPosition(),
+ motionEvent.getDownTime(),
uint32_t(pointerCount), pointerProperties,
- samplePointerCoords,
- motionEvent->getXOffset(),
- motionEvent->getYOffset());
+ samplePointerCoords, motionEvent.getXOffset(),
+ motionEvent.getYOffset());
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
index c075078..c7c8e28 100644
--- a/services/inputflinger/reader/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -169,8 +169,9 @@
mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
std::make_move_iterator(frames.end()));
if (mFrames.size() > MAX_QUEUE_SIZE) {
- ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
- mFrames.size() - MAX_QUEUE_SIZE);
+ // A user-space grip suppression process may be processing the video frames, and holding
+ // back the input events. This could result in video frames being produced without the
+ // matching input events. Drop the oldest frame here to prepare for the next input event.
mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
}
return numFrames;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 93aa6ac..d51acce 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -473,6 +473,7 @@
const sp<InputWindowHandle>& focusedWindow = nullptr) {
FocusRequest request;
request.token = window->getToken();
+ request.windowName = window->getName();
if (focusedWindow) {
request.focusedToken = focusedWindow->getToken();
}
@@ -1085,6 +1086,20 @@
return mInputReceiver->consume();
}
+ MotionEvent* consumeMotion() {
+ InputEvent* event = consume();
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed : no event";
+ return nullptr;
+ }
+ if (event->getType() != AINPUT_EVENT_TYPE_MOTION) {
+ ADD_FAILURE() << "Instead of motion event, got "
+ << inputEventTypeToString(event->getType());
+ return nullptr;
+ }
+ return static_cast<MotionEvent*>(event);
+ }
+
void assertNoEvents() {
if (mInputReceiver == nullptr &&
mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
@@ -2446,13 +2461,10 @@
generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&motionArgs);
- InputEvent* event = window->consume();
+ MotionEvent* event = window->consumeMotion();
ASSERT_NE(event, nullptr);
- ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
- << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
- << " event, got " << inputEventTypeToString(event->getType()) << " event";
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ const MotionEvent& motionEvent = *event;
EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction());
EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
@@ -3118,6 +3130,70 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+class InputFilterInjectionPolicyTest : public InputDispatcherTest {
+protected:
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ /**
+ * We don't need to enable input filter to test the injected event policy, but we enabled it
+ * here to make the tests more realistic, since this policy only matters when inputfilter is
+ * on.
+ */
+ mDispatcher->setInputFilterEnabled(true);
+
+ std::shared_ptr<InputApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+ mWindow =
+ new FakeWindowHandle(application, mDispatcher, "Test Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mWindow->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ setFocusedWindow(mWindow);
+ mWindow->consumeFocusEvent(true);
+ }
+
+ void testInjectedKey(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId) {
+ KeyEvent event;
+
+ const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ event.initialize(InputEvent::nextId(), injectedDeviceId, AINPUT_SOURCE_KEYBOARD,
+ ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A,
+ KEY_A, AMETA_NONE, 0 /*repeatCount*/, eventTime, eventTime);
+ const int32_t additionalPolicyFlags =
+ POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+ policyFlags | additionalPolicyFlags));
+
+ InputEvent* received = mWindow->consume();
+ ASSERT_NE(nullptr, received);
+ ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
+ }
+
+private:
+ sp<FakeWindowHandle> mWindow;
+};
+
+TEST_F(InputFilterInjectionPolicyTest, TrustedFilteredEvents_KeepOriginalDeviceId) {
+ // We don't need POLICY_FLAG_FILTERED here, but it will be set in practice, so keep it to make
+ // the test more closely resemble the real usage
+ testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED, 3 /*injectedDeviceId*/,
+ 3 /*resolvedDeviceId*/);
+}
+
+TEST_F(InputFilterInjectionPolicyTest, EventsInjectedFromAccessibility_HaveAccessibilityDeviceId) {
+ testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+ 3 /*injectedDeviceId*/, ACCESSIBILITY_DEVICE_ID /*resolvedDeviceId*/);
+}
+
+TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDeviceId) {
+ testInjectedKey(0 /*policyFlags*/, 3 /*injectedDeviceId*/,
+ VIRTUAL_KEYBOARD_ID /*resolvedDeviceId*/);
+}
+
class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 9df020d..2281721 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1626,19 +1626,17 @@
}
void SensorService::notifyProximityStateLocked(
- const std::vector<sp<ProximityActiveListener>>& listnrs) {
- std::async(
- std::launch::async,
- [](uint64_t mySeq, bool isActive, std::vector<sp<ProximityActiveListener>> listeners) {
- while (completedCallbackSeq.load() != mySeq - 1)
- std::this_thread::sleep_for(1ms);
- for (auto& listener : listeners)
- listener->onProximityActive(isActive);
- completedCallbackSeq++;
- },
- ++curProxCallbackSeq, mProximityActiveCount > 0,
- listnrs /* (this is meant to be a copy) */
- );
+ 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) {
@@ -2172,6 +2170,13 @@
return true;
}
+/**
+ * Checks if a sensor should be capped according to HIGH_SAMPLING_RATE_SENSORS
+ * permission.
+ *
+ * This needs to be kept in sync with the list defined on the Java side
+ * in frameworks/base/core/java/android/hardware/SystemSensorManager.java
+ */
bool SensorService::isSensorInCappedSet(int sensorType) {
return (sensorType == SENSOR_TYPE_ACCELEROMETER
|| sensorType == SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index d68a0e0..6213a8a 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -33,6 +33,7 @@
#include <gui/BufferQueue.h>
#include <private/gui/SyncFeatures.h>
#include <renderengine/Image.h>
+#include "TunnelModeEnabledReporter.h"
#include "EffectLayer.h"
#include "FrameTracer/FrameTracer.h"
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 289cb11..29937fb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -82,6 +82,9 @@
// The earliest time to send the present command to the HAL
std::chrono::steady_clock::time_point earliestPresentTime;
+
+ // The predicted next invalidation time
+ std::optional<std::chrono::steady_clock::time_point> nextInvalidateTime;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 257974f..1416b1e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -286,7 +286,7 @@
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
virtual void postFramebuffer() = 0;
- virtual void renderCachedSets() = 0;
+ virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual void chooseCompositionStrategy() = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index f10ff25..f832084 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -93,7 +93,7 @@
std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
void postFramebuffer() override;
- void renderCachedSets() override;
+ void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
// Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index a4356c5..7cb0f6b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -98,6 +98,7 @@
mDrawFence = nullptr;
mBlurLayer = nullptr;
mHolePunchLayer = nullptr;
+ mSkipCount = 0;
mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
Region boundingRegion;
@@ -107,6 +108,8 @@
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, TexturePool& texturePool,
@@ -155,6 +158,7 @@
Rect mBounds = Rect::EMPTY_RECT;
Region mVisibleRegion;
size_t mAge = 0;
+ size_t mSkipCount = 0;
// 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.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 94a169e..7534548 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -20,6 +20,7 @@
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>
+#include <chrono>
#include <numeric>
#include <vector>
@@ -37,7 +38,35 @@
class Flattener {
public:
- Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false);
+ 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;
@@ -48,16 +77,14 @@
std::chrono::steady_clock::time_point now);
// Renders the newest cached sets with the supplied output composition state
- void renderCachedSets(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; }
-protected:
- std::optional<CachedSet> mNewCachedSet;
-
private:
size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
@@ -149,9 +176,15 @@
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;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index fd1ddfc..be34153 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -58,8 +58,11 @@
void reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
- // The planner will call to the Flattener to render any pending cached set
- void renderCachedSets(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/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4b4d375..8e777e3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -109,7 +109,7 @@
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
- MOCK_METHOD0(renderCachedSets, void());
+ MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index cd2f742..67bb149 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -434,7 +434,7 @@
devOptRepaintFlash(refreshArgs);
finishFrame(refreshArgs);
postFramebuffer();
- renderCachedSets();
+ renderCachedSets(refreshArgs);
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -1312,9 +1312,9 @@
mReleasedLayers.clear();
}
-void Output::renderCachedSets() {
+void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
if (mPlanner) {
- mPlanner->renderCachedSets(getState());
+ mPlanner->renderCachedSets(getState(), refreshArgs.nextInvalidateTime);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 68b6231..acc7ed2 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -277,6 +277,7 @@
mOutputSpace.orientation = outputState.framebufferSpace.orientation;
mOutputDataspace = outputDataspace;
mOrientation = orientation;
+ mSkipCount = 0;
} else {
mTexture.reset();
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 192c411..2bcaf60 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -23,7 +23,7 @@
#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;
@@ -60,9 +60,12 @@
} // namespace
-Flattener::Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch)
+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);
@@ -105,12 +108,45 @@
return hash;
}
-void Flattener::renderCachedSets(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;
+ }
+
+ 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);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 711a634..be2510f 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -26,16 +26,43 @@
#include <compositionengine/impl/planner/Planner.h>
#include <utils/Trace.h>
+#include <chrono>
namespace android::compositionengine::impl::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(renderEngine,
- base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) {
+ base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true),
+ buildFlattenerTuneables()) {
mPredictorEnabled =
base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
}
@@ -161,9 +188,11 @@
finalPlan);
}
-void Planner::renderCachedSets(const OutputCompositionState& outputState) {
+void Planner::renderCachedSets(
+ const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
ATRACE_CALL();
- mFlattener.renderCachedSets(outputState);
+ mFlattener.renderCachedSets(outputState, renderDeadline);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c381081..742b155 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1767,7 +1767,7 @@
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(postFramebuffer, void());
- MOCK_METHOD0(renderCachedSets, void());
+ MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
};
StrictMock<OutputPartialMock> mOutput;
@@ -1787,7 +1787,7 @@
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(Ref(args)));
EXPECT_CALL(mOutput, postFramebuffer());
- EXPECT_CALL(mOutput, renderCachedSets());
+ EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 4a76a95..0acc317 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -223,6 +223,16 @@
EXPECT_EQ(2u, cachedSet.getAge());
}
+TEST_F(CachedSetTest, incrementSkipCount) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet cachedSet(layer);
+ EXPECT_EQ(0u, cachedSet.getSkipCount());
+ cachedSet.incrementSkipCount();
+ EXPECT_EQ(1u, cachedSet.getSkipCount());
+ cachedSet.incrementSkipCount();
+ EXPECT_EQ(2u, cachedSet.getSkipCount());
+}
+
TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) {
CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
@@ -257,6 +267,8 @@
CachedSet cachedSet1(layer1);
CachedSet cachedSet2(layer2);
cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
+ cachedSet1.incrementSkipCount();
+ EXPECT_EQ(1u, cachedSet1.getSkipCount());
cachedSet1.append(cachedSet2);
EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
@@ -268,6 +280,8 @@
EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
EXPECT_EQ(3u, cachedSet1.getLayerCount());
EXPECT_EQ(0u, cachedSet1.getAge());
+ EXPECT_EQ(0u, cachedSet1.getSkipCount());
+
expectNoBuffer(cachedSet1);
// TODO(b/181192080): check that getNonBufferHash returns the correct hash value
// EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index e176c98..334b855 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -24,6 +24,7 @@
#include <renderengine/ExternalTexture.h>
#include <renderengine/LayerSettings.h>
#include <renderengine/mock/RenderEngine.h>
+#include <chrono>
namespace android::compositionengine {
using namespace std::chrono_literals;
@@ -46,22 +47,28 @@
class TestableFlattener : public Flattener {
public:
- TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch)
- : Flattener(renderEngine, enableHolePunch) {}
+ 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<TestableFlattener>(mRenderEngine, 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 is held as a reference in 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<TestableFlattener> mFlattener;
@@ -148,13 +155,13 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(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(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
}
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -164,7 +171,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -174,7 +181,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
EXPECT_NE(nullptr, buffer);
@@ -209,7 +216,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
}
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -255,7 +262,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -360,7 +367,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -397,7 +404,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -406,7 +413,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -419,7 +426,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -428,7 +435,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -470,7 +477,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -484,7 +491,7 @@
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -497,7 +504,7 @@
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -512,7 +519,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -524,7 +531,7 @@
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -563,7 +570,7 @@
// This will render a CachedSet.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -576,7 +583,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -625,7 +632,7 @@
// This will render a CachedSet.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -638,7 +645,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -682,7 +689,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -692,7 +699,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
EXPECT_EQ(nullptr, overrideBuffer3);
@@ -726,7 +733,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -737,7 +744,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
}
@@ -778,7 +785,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -788,7 +795,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, blurOverrideBuffer);
EXPECT_NE(nullptr, overrideBuffer3);
@@ -825,7 +832,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
ASSERT_NE(std::nullopt, cachedSet);
@@ -839,7 +846,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer2, overrideBuffer1);
EXPECT_EQ(nullptr, blurOverrideBuffer);
@@ -866,7 +873,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -874,16 +881,61 @@
// Simulate attempting to render prior to merging the new cached set with the layer stack.
// Here we should not try to re-render.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
- mFlattener->renderCachedSets(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(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/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 3523b56..c294ff2 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -693,6 +693,7 @@
actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
+ actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer);
});
// Actual timeline end
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e50087f..4beb526 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -847,11 +847,13 @@
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
- return mTransactionFlags.fetch_and(~flags) & flags;
+ auto ret = mTransactionFlags & flags;
+ mTransactionFlags &= ~flags;
+ return ret;
}
uint32_t Layer::setTransactionFlags(uint32_t flags) {
- return mTransactionFlags.fetch_or(flags);
+ return mTransactionFlags |= flags;
}
bool Layer::setPosition(float x, float y) {
@@ -2018,7 +2020,7 @@
const std::vector<BlurRegion> Layer::getBlurRegions() const {
auto regionsCopy(getDrawingState().blurRegions);
- int layerAlpha = getAlpha();
+ float layerAlpha = getAlpha();
for (auto& region : regionsCopy) {
region.alpha = region.alpha * layerAlpha;
}
@@ -2382,6 +2384,16 @@
info.touchableRegion = inputTransform.transform(info.touchableRegion);
}
+void Layer::fillTouchOcclusionMode(InputWindowInfo& info) {
+ sp<Layer> p = this;
+ while (p != nullptr && !p->hasInputInfo()) {
+ p = p->mDrawingParent.promote();
+ }
+ if (p != nullptr) {
+ info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
+ }
+}
+
InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
@@ -2419,6 +2431,7 @@
// anything.
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
info.alpha = getAlpha();
+ fillTouchOcclusionMode(info);
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index dde0031..5669049 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -960,7 +960,7 @@
// these are protected by an external lock (mStateLock)
State mCurrentState;
- std::atomic<uint32_t> mTransactionFlags{0};
+ uint32_t mTransactionFlags{0};
// Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
@@ -1057,6 +1057,10 @@
// null.
sp<Layer> getRootLayer();
+ // Fills in the touch occlusion mode of the first parent (including this layer) that
+ // hasInputInfo() or no-op if no such parent is found.
+ void fillTouchOcclusionMode(InputWindowInfo& info);
+
// Fills in the frame and transform info for the InputWindowInfo
void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b062acd..9746076 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -190,6 +190,45 @@
RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
const GlobalSignals& globalSignals,
GlobalSignals* outSignalsConsidered) const {
+ std::lock_guard lock(mLock);
+
+ if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) {
+ return *cached;
+ }
+
+ GlobalSignals signalsConsidered;
+ RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered);
+ lastBestRefreshRateInvocation.emplace(
+ GetBestRefreshRateInvocation{.layerRequirements = layers,
+ .globalSignals = globalSignals,
+ .outSignalsConsidered = signalsConsidered,
+ .resultingBestRefreshRate = result});
+ if (outSignalsConsidered) {
+ *outSignalsConsidered = signalsConsidered;
+ }
+ return result;
+}
+
+std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
+ const bool sameAsLastCall = lastBestRefreshRateInvocation &&
+ lastBestRefreshRateInvocation->layerRequirements == layers &&
+ lastBestRefreshRateInvocation->globalSignals == globalSignals;
+
+ if (sameAsLastCall) {
+ if (outSignalsConsidered) {
+ *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered;
+ }
+ return lastBestRefreshRateInvocation->resultingBestRefreshRate;
+ }
+
+ return {};
+}
+
+RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -206,8 +245,6 @@
}
};
- std::lock_guard lock(mLock);
-
int noVoteLayers = 0;
int minVoteLayers = 0;
int maxVoteLayers = 0;
@@ -592,6 +629,11 @@
void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
std::lock_guard lock(mLock);
+
+ // Invalidate the cached invocation to getBestRefreshRate. This forces
+ // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+ lastBestRefreshRateInvocation.reset();
+
mCurrentRefreshRate = mRefreshRates.at(modeId).get();
}
@@ -605,11 +647,16 @@
void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
DisplayModeId currentModeId) {
std::lock_guard lock(mLock);
+
// The current mode should be supported
LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
return mode->getId() == currentModeId;
}));
+ // Invalidate the cached invocation to getBestRefreshRate. This forces
+ // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+ lastBestRefreshRateInvocation.reset();
+
mRefreshRates.clear();
for (const auto& mode : modes) {
const auto modeId = mode->getId();
@@ -666,6 +713,7 @@
ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
+ lastBestRefreshRateInvocation.reset();
Policy previousPolicy = *getCurrentPolicyLocked();
mDisplayManagerPolicy = policy;
if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -680,6 +728,7 @@
if (policy && !isPolicyValidLocked(*policy)) {
return BAD_VALUE;
}
+ lastBestRefreshRateInvocation.reset();
Policy previousPolicy = *getCurrentPolicyLocked();
mOverridePolicy = policy;
if (*getCurrentPolicyLocked() == previousPolicy) {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ee89149..342fde0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -250,6 +250,10 @@
bool touch = false;
// True if the system hasn't seen any buffers posted to layers recently.
bool idle = false;
+
+ bool operator==(const GlobalSignals& other) const {
+ return touch == other.touch && idle == other.idle;
+ }
};
// Returns the refresh rate that fits best to the given layers.
@@ -350,6 +354,15 @@
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
+ std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const
+ REQUIRES(mLock);
+
+ RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
+
// Returns the refresh rate with the highest score in the collection specified from begin
// to end. If there are more than one with the same highest refresh rate, the first one is
// returned.
@@ -414,6 +427,15 @@
const bool mEnableFrameRateOverride;
bool mSupportsFrameRateOverride;
+
+ struct GetBestRefreshRateInvocation {
+ std::vector<LayerRequirement> layerRequirements;
+ GlobalSignals globalSignals;
+ GlobalSignals outSignalsConsidered;
+ RefreshRate resultingBestRefreshRate;
+ };
+ mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
+ GUARDED_BY(mLock);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 80f4665..208a767 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -61,6 +61,7 @@
if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
return;
}
+ mTimeStats.incrementRefreshRateSwitches();
flushTime();
mCurrentRefreshRate = currRefreshRate;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index fac2c65..4b8cbfb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -194,8 +194,6 @@
std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
const scheduler::RefreshRateConfigs& configs) {
- if (!configs.canSwitch()) return nullptr;
-
return std::make_unique<scheduler::LayerHistory>(configs);
}
@@ -579,8 +577,6 @@
}
void Scheduler::registerLayer(Layer* layer) {
- if (!mLayerHistory) return;
-
scheduler::LayerHistory::LayerVoteType voteType;
if (!mOptions.useContentDetection ||
@@ -600,26 +596,22 @@
}
void Scheduler::deregisterLayer(Layer* layer) {
- if (mLayerHistory) {
- mLayerHistory->deregisterLayer(layer);
- }
+ mLayerHistory->deregisterLayer(layer);
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
- if (mLayerHistory) {
+ if (mRefreshRateConfigs.canSwitch()) {
mLayerHistory->record(layer, presentTime, systemTime(), updateType);
}
}
void Scheduler::setModeChangePending(bool pending) {
- if (mLayerHistory) {
- mLayerHistory->setModeChangePending(pending);
- }
+ mLayerHistory->setModeChangePending(pending);
}
void Scheduler::chooseRefreshRateForContent() {
- if (!mLayerHistory) return;
+ if (!mRefreshRateConfigs.canSwitch()) return;
ATRACE_CALL();
@@ -630,9 +622,6 @@
bool frameRateOverridesChanged;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRequirements == summary) {
- return;
- }
mFeatures.contentRequirements = summary;
newModeId = calculateRefreshRateModeId(&consideredSignals);
@@ -691,9 +680,7 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- if (mLayerHistory) {
- mLayerHistory->clear();
- }
+ mLayerHistory->clear();
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -732,9 +719,7 @@
// NOTE: Instead of checking all the layers, we should be checking the layer
// that is currently on top. b/142507166 will give us this capability.
if (handleTimerStateChanged(&mFeatures.touch, touch)) {
- if (mLayerHistory) {
- mLayerHistory->clear();
- }
+ mLayerHistory->clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
@@ -908,9 +893,7 @@
}
void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
- if (mLayerHistory) {
- mLayerHistory->setDisplayArea(displayArea);
- }
+ mLayerHistory->setDisplayArea(displayArea);
}
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 49d3d93..30a3253 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -268,7 +268,7 @@
VsyncSchedule mVsyncSchedule;
// Used to choose refresh rate if content detection is enabled.
- const std::unique_ptr<LayerHistory> mLayerHistory;
+ std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 028f7a6..e9bd92a 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -69,7 +69,21 @@
auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
- return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
+ if (percent >= kOutlierTolerancePercent &&
+ percent <= (kMaxPercent - kOutlierTolerancePercent)) {
+ return false;
+ }
+
+ const auto iter = std::min_element(mTimestamps.begin(), mTimestamps.end(),
+ [timestamp](nsecs_t a, nsecs_t b) {
+ return std::abs(timestamp - a) < std::abs(timestamp - b);
+ });
+ const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / mIdealPeriod;
+ if (distancePercent < kOutlierTolerancePercent) {
+ // duplicate timestamp
+ return false;
+ }
+ return true;
}
nsecs_t VSyncPredictor::currentPeriod() const {
@@ -86,6 +100,9 @@
// in the learning phase we should just clear all timestamps and start
// over.
if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+ // Add the timestamp to mTimestamps before clearing it so we could
+ // update mKnownTimestamp based on the new timestamp.
+ mTimestamps.push_back(timestamp);
clearTimestamps();
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a4b6fef..4fcec16 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>
@@ -680,6 +681,10 @@
if (mStartPropertySetThread->join() != NO_ERROR) {
ALOGE("Join StartPropertySetThread failed!");
}
+
+ if (mRenderEnginePrimeCacheFuture.valid()) {
+ mRenderEnginePrimeCacheFuture.get();
+ }
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -781,6 +786,12 @@
? 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().setCallback(this);
@@ -807,7 +818,15 @@
char primeShaderCache[PROPERTY_VALUE_MAX];
property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
if (atoi(primeShaderCache)) {
- getRenderEngine().primeCache();
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Can't set SCHED_OTHER for primeCache");
+ }
+
+ mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache();
+
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Can't set SCHED_OTHER for primeCache");
+ }
}
getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
@@ -1139,7 +1158,18 @@
// have been already updated with the upcoming active mode.
return;
}
- const Fps oldRefreshRate = display->getActiveMode()->getFps();
+
+ if (display->getActiveMode()->getSize() != upcomingMode->getSize()) {
+ auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
+ // We need to generate new sequenceId in order to recreate the display (and this
+ // way the framebuffer).
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.physical->activeMode = upcomingMode;
+ processDisplayChangesLocked();
+
+ // processDisplayChangesLocked will update all necessary components so we're done here.
+ return;
+ }
std::lock_guard<std::mutex> lock(mActiveModeLock);
mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
@@ -1149,9 +1179,6 @@
mRefreshRateStats->setRefreshRate(refreshRate);
- if (!refreshRate.equalsWithMargin(oldRefreshRate)) {
- mTimeStats->incrementRefreshRateSwitches();
- }
updatePhaseConfiguration(refreshRate);
ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
@@ -2040,6 +2067,7 @@
}
refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+ refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
mGeometryInvalid = false;
@@ -2225,14 +2253,14 @@
compositorTiming = getBE().mCompositorTiming;
}
- mDrawingState.traverse([&](Layer* layer) {
+ for (const auto& layer: mLayersWithQueuedFrames) {
const bool frameLatched =
layer->onPostComposition(display, glCompositionDoneFenceTime,
mPreviousPresentFences[0].fenceTime, compositorTiming);
if (frameLatched) {
recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
}
- });
+ }
std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
hdrInfoListeners;
@@ -2808,7 +2836,10 @@
mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
currentState.physical->activeMode->getId());
mVsyncConfiguration->reset();
- updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate().getFps());
+ const Fps refreshRate = currentState.physical->activeMode->getFps();
+ updatePhaseConfiguration(refreshRate);
+ mRefreshRateStats->setRefreshRate(refreshRate);
+
if (mRefreshRateOverlay) {
mRefreshRateOverlay->reset();
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 519a5ab..a3fa8d6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1183,6 +1183,8 @@
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
+ std::future<void> mRenderEnginePrimeCacheFuture;
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index f1b153f..7c1f21f 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -846,8 +846,8 @@
timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
timelineStats.stats[layerKey].uid = info.uid;
- timelineStats.stats[layerKey].layerName = kDefaultGameMode;
- timelineStats.stats[layerKey].gameMode = info.gameMode;
+ timelineStats.stats[layerKey].layerName = kDefaultLayerName;
+ timelineStats.stats[layerKey].gameMode = kDefaultGameMode;
}
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index dd48950..9e70684 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -16,18 +16,10 @@
#pragma once
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
#include <cstdint>
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <../Fps.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <gui/JankInfo.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 71a567a..0a8c748 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -877,7 +877,7 @@
ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart(
int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName,
ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition,
- ProtoJankType jankType, ProtoPredictionType predictionType) {
+ ProtoJankType jankType, ProtoPredictionType predictionType, bool isBuffer) {
ProtoActualSurfaceFrameStart proto;
proto.set_cookie(cookie);
proto.set_token(token);
@@ -889,6 +889,7 @@
proto.set_gpu_composition(gpuComposition);
proto.set_jank_type(jankType);
proto.set_prediction_type(predictionType);
+ proto.set_is_buffer(isBuffer);
return proto;
}
@@ -978,6 +979,8 @@
EXPECT_EQ(received.jank_type(), source.jank_type());
ASSERT_TRUE(received.has_prediction_type());
EXPECT_EQ(received.prediction_type(), source.prediction_type());
+ ASSERT_TRUE(received.has_is_buffer());
+ EXPECT_EQ(received.is_buffer(), source.is_buffer());
}
void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
@@ -1154,7 +1157,7 @@
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
FrameTimelineEvent::JANK_NONE,
- FrameTimelineEvent::PREDICTION_VALID);
+ FrameTimelineEvent::PREDICTION_VALID, true);
auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
auto protoPresentedSurfaceFrameExpectedStart =
@@ -1166,7 +1169,7 @@
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_ON_TIME, true, false,
FrameTimelineEvent::JANK_NONE,
- FrameTimelineEvent::PREDICTION_VALID);
+ FrameTimelineEvent::PREDICTION_VALID, true);
auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
// Set up the display frame
@@ -1309,7 +1312,7 @@
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
false, FrameTimelineEvent::JANK_UNKNOWN,
- FrameTimelineEvent::PREDICTION_EXPIRED);
+ FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
@@ -1383,7 +1386,7 @@
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
FrameTimelineEvent::JANK_NONE,
- FrameTimelineEvent::PREDICTION_EXPIRED);
+ FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7ace70a..d04a7d7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -45,9 +45,16 @@
class RefreshRateConfigsTest : public testing::Test {
protected:
+ using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;
+
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
+ RefreshRate createRefreshRate(DisplayModePtr displayMode) {
+ return {displayMode->getId(), displayMode, displayMode->getFps(),
+ RefreshRate::ConstructorTag(0)};
+ }
+
Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
}
@@ -71,6 +78,19 @@
return *refreshRateConfigs.mMaxSupportedRefreshRate;
}
+ void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
+ const GetBestRefreshRateInvocation& invocation) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
+ GetBestRefreshRateInvocation(invocation));
+ }
+
+ std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
+ const RefreshRateConfigs& refreshRateConfigs) {
+ std::lock_guard lock(refreshRateConfigs.mLock);
+ return refreshRateConfigs.lastBestRefreshRateInvocation;
+ }
+
// Test config IDs
static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
@@ -1752,6 +1772,78 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
+ using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ setLastBestRefreshRateInvocation(*refreshRateConfigs,
+ GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+ LayerRequirement>(),
+ .globalSignals = {.touch = true,
+ .idle = true},
+ .outSignalsConsidered =
+ {.touch = true,
+ .idle = false},
+ .resultingBestRefreshRate =
+ createRefreshRate(
+ mConfig90)});
+
+ EXPECT_EQ(createRefreshRate(mConfig90),
+ refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+ {.touch = true, .idle = true}));
+
+ const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
+ setLastBestRefreshRateInvocation(*refreshRateConfigs,
+ GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+ LayerRequirement>(),
+ .globalSignals = {.touch = true,
+ .idle = true},
+ .outSignalsConsidered =
+ cachedSignalsConsidered,
+ .resultingBestRefreshRate =
+ createRefreshRate(
+ mConfig30)});
+
+ GlobalSignals signalsConsidered;
+ EXPECT_EQ(createRefreshRate(mConfig30),
+ refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+ {.touch = true, .idle = true},
+ &signalsConsidered));
+
+ EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
+ using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
+
+ GlobalSignals globalSignals{.touch = true, .idle = true};
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 0.5f}};
+ const auto lastResult =
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
+ /* outSignalsConsidered */ nullptr);
+
+ const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs);
+
+ ASSERT_TRUE(lastInvocation.has_value());
+ ASSERT_EQ(layers, lastInvocation->layerRequirements);
+ ASSERT_EQ(globalSignals, lastInvocation->globalSignals);
+ ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate);
+
+ // outSignalsConsidered needs to be populated even tho earlier we gave nullptr
+ // to getBestRefreshRate()
+ GlobalSignals detaultSignals;
+ ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered);
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 38e503f..423d0cc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -51,9 +51,18 @@
SchedulerTest();
- const scheduler::RefreshRateConfigs
- mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setGroup(0).build()},
- DisplayModeId(0)};
+ const DisplayModePtr mode60 = DisplayMode::Builder(0)
+ .setId(DisplayModeId(0))
+ .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
+ .setGroup(0)
+ .build();
+ const DisplayModePtr mode120 = DisplayMode::Builder(1)
+ .setId(DisplayModeId(1))
+ .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
+ .setGroup(0)
+ .build();
+
+ scheduler::RefreshRateConfigs mConfigs{{mode60}, mode60->getId()};
mock::SchedulerCallback mSchedulerCallback;
@@ -149,15 +158,14 @@
EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
-TEST_F(SchedulerTest, noLayerHistory) {
- // Layer history should not be created if there is a single config.
- ASSERT_FALSE(mScheduler->hasLayerHistory());
-
+TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
+ // The layer is registered at creation time and deregistered at destruction time.
sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
- // Content detection should be no-op.
- mScheduler->registerLayer(layer.get());
+ // recordLayerHistory should be a noop
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
constexpr bool kPowerStateNormal = true;
mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -169,6 +177,18 @@
mScheduler->chooseRefreshRateForContent();
}
+TEST_F(SchedulerTest, updateDisplayModes) {
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+
+ mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+ ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+}
+
TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
// If the optional fields are cleared, the function should return before
// onModeChange is called.
@@ -200,4 +220,25 @@
EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
}
+MATCHER(Is120Hz, "") {
+ return arg.getFps().equalsWithMargin(Fps(120.f));
+}
+
+TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
+ mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler->setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
+ mScheduler->chooseRefreshRateForContent();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f9dd01..41fd6e3 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -64,6 +64,11 @@
return mutableLayerHistory()->mLayerInfos.size();
}
+ size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mutableLayerHistory()->mActiveLayersEnd;
+ }
+
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
mTouchTimer.reset();
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 2a658dd..37ecd7c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -478,7 +478,7 @@
}
}
-TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) {
+TEST_F(VSyncPredictorTest, inconsistentVsyncValueIsFlushedEventually) {
EXPECT_TRUE(tracker.addVsyncTimestamp(600));
EXPECT_TRUE(tracker.needsMoreSamples());
@@ -492,6 +492,46 @@
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));
+}
+
+TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) {
+ // these are real vsync timestamps from b/190331974 which caused vsync predictor
+ // period to spike to 18ms due to very close timestamps
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 198353408177, 198370074844, 198371400000, 198374274000, 198390941000, 198407565000,
+ 198540887994, 198607538588, 198624218276, 198657655939, 198674224176, 198690880955,
+ 198724204319, 198740988133, 198758166681, 198790869196, 198824205052, 198840871678,
+ 198857715631, 198890885797, 198924199640, 198940873834, 198974204401,
+ };
+ auto constexpr idealPeriod = 16'666'666;
+ auto constexpr expectedPeriod = 16'644'742;
+ auto constexpr expectedIntercept = 125'626;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 1010aa5..f15a963 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -95,7 +95,10 @@
}
HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
+ // the same as the operation being unsupported by this HAL. Should not retry.
return HalResult<void>::unsupported();
}
if (status.isOk()) {
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 8720d9d..87bc34e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -42,7 +42,10 @@
static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
static HalResult<T> fromStatus(binder::Status status, T data) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
+ // the same as the operation being unsupported by this HAL. Should not retry.
return HalResult<T>::unsupported();
}
if (status.isOk()) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index af0cdb8..7813303 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -189,8 +189,7 @@
.WillRepeatedly(vibrator::TriggerSchedulerCallback());
EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -228,8 +227,7 @@
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f)))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -265,8 +263,7 @@
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
.Times(Exactly(1))
@@ -397,8 +394,7 @@
Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
@@ -411,8 +407,7 @@
Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
.Times(Exactly(1))
.WillRepeatedly(
@@ -451,8 +446,7 @@
DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -549,8 +543,7 @@
.WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index 548d028..1593cb1 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -308,8 +308,7 @@
Return(Status())));
EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
.Times(Exactly(3))
- .WillOnce(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
.WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
.WillRepeatedly(DoAll(TriggerCallback(), Return(Status())));
}
@@ -345,8 +344,7 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) {
EXPECT_CALL(*mMockHal.get(), cancelSynced())
.Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
.WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
.WillRepeatedly(Return(Status()));