Merge "Add a better getLastQueuedBuffer" into sc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8ac4ff8..25e6dc9 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1696,6 +1696,12 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh);
+ // The dump mechanism in connectivity is refactored due to modularization work. Connectivity can
+ // only register with a default priority(NORMAL priority). Dumpstate has to call connectivity
+ // dump with priority parameters to dump high priority information.
+ RunDumpsys("SERVICE HIGH connectivity", {"connectivity", "--dump-priority", "HIGH"},
+ CommandOptions::WithTimeout(10).Build());
+
RunCommand("SYSTEM PROPERTIES", {"getprop"});
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 18b77e6..ef7fd44 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,45 +366,19 @@
pid_t IPCThreadState::getCallingPid() const
{
- checkContextIsBinderForUse(__func__);
return mCallingPid;
}
const char* IPCThreadState::getCallingSid() const
{
- checkContextIsBinderForUse(__func__);
return mCallingSid;
}
uid_t IPCThreadState::getCallingUid() const
{
- checkContextIsBinderForUse(__func__);
return mCallingUid;
}
-IPCThreadState::SpGuard* IPCThreadState::pushGetCallingSpGuard(SpGuard* guard) {
- SpGuard* orig = mServingStackPointerGuard;
- mServingStackPointerGuard = guard;
- return orig;
-}
-
-void IPCThreadState::restoreGetCallingSpGuard(SpGuard* guard) {
- mServingStackPointerGuard = guard;
-}
-
-void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
- if (mServingStackPointerGuard == nullptr) return;
-
- if (!mServingStackPointer || mServingStackPointerGuard < mServingStackPointer) {
- LOG_ALWAYS_FATAL("In context %s, %s does not make sense.",
- mServingStackPointerGuard->context, use);
- }
-
- // in the case mServingStackPointer is deeper in the stack than the guard,
- // we must be serving a binder transaction (maybe nested). This is a binder
- // context, so we don't abort
-}
-
int64_t IPCThreadState::clearCallingIdentity()
{
// ignore mCallingSid for legacy reasons
@@ -873,15 +847,15 @@
}
IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self()),
- mServingStackPointer(nullptr),
- mServingStackPointerGuard(nullptr),
- mWorkSource(kUnsetWorkSource),
- mPropagateWorkSource(false),
- mIsLooper(false),
- mStrictModePolicy(0),
- mLastTransactionBinderFlags(0),
- mCallRestriction(mProcess->mCallRestriction) {
+ : mProcess(ProcessState::self()),
+ mServingStackPointer(nullptr),
+ mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
+ mIsLooper(false),
+ mStrictModePolicy(0),
+ mLastTransactionBinderFlags(0),
+ mCallRestriction(mProcess->mCallRestriction)
+{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index e5a6026..2ba9fa2 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,9 +18,7 @@
#include "RpcState.h"
-#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
#include "Debug.h"
@@ -30,8 +28,6 @@
namespace android {
-using base::ScopeGuard;
-
RpcState::RpcState() {}
RpcState::~RpcState() {}
@@ -474,18 +470,6 @@
status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
- IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
- IPCThreadState::SpGuard spGuard{"processing binder RPC command"};
- IPCThreadState::SpGuard* origGuard;
- if (kernelBinderState != nullptr) {
- origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
- }
- ScopeGuard guardUnguard = [&]() {
- if (kernelBinderState != nullptr) {
- kernelBinderState->restoreGetCallingSpGuard(origGuard);
- }
- };
-
switch (command.command) {
case RPC_COMMAND_TRANSACT:
return processTransact(fd, session, command);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 5220b62..23a0cb0 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -81,32 +81,6 @@
*/
uid_t getCallingUid() const;
- /**
- * Make it an abort to rely on getCalling* for a section of
- * execution.
- *
- * Usage:
- * IPCThreadState::SpGuard guard { "..." };
- * auto* orig = pushGetCallingSpGuard(&guard);
- * {
- * // will abort if you call getCalling*, unless you are
- * // serving a nested binder transaction
- * }
- * restoreCallingSpGuard(orig);
- */
- struct SpGuard {
- const char* context;
- };
- SpGuard* pushGetCallingSpGuard(SpGuard* guard);
- void restoreGetCallingSpGuard(SpGuard* guard);
- /**
- * Used internally by getCalling*. Can also be used to assert that
- * you are in a binder context (getCalling* is valid). This is
- * intentionally not exposed as a boolean API since code should be
- * written to know its environment.
- */
- void checkContextIsBinderForUse(const char* use) const;
-
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
@@ -229,7 +203,6 @@
Parcel mOut;
status_t mLastError;
const void* mServingStackPointer;
- SpGuard* mServingStackPointerGuard;
pid_t mCallingPid;
const char* mCallingSid;
uid_t mCallingUid;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 9e2050b..78f2d3a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -150,6 +150,11 @@
/**
* This is called whenever a transaction needs to be processed by a local implementation.
*
+ * This method will be called after the equivalent of
+ * android.os.Parcel#enforceInterface is called. That is, the interface
+ * descriptor associated with the AIBinder_Class descriptor will already be
+ * checked.
+ *
* \param binder the object being transacted on.
* \param code implementation-specific code representing which transaction should be taken.
* \param in the implementation-specific input data to this transaction.
@@ -452,12 +457,14 @@
*/
/**
- * Creates a parcel to start filling out for a transaction. This may add data to the parcel for
- * security, debugging, or other purposes. This parcel is to be sent via AIBinder_transact and it
- * represents the input data to the transaction. It is recommended to check if the object is local
- * and call directly into its user data before calling this as the parceling and unparceling cost
- * can be avoided. This AIBinder must be either built with a class or associated with a class before
- * using this API.
+ * Creates a parcel to start filling out for a transaction. This will add a header to the
+ * transaction that corresponds to android.os.Parcel#writeInterfaceToken. This may add debugging
+ * or other information to the transaction for platform use or to enable other features to work. The
+ * contents of this header is a platform implementation detail, and it is required to use
+ * libbinder_ndk. This parcel is to be sent via AIBinder_transact and it represents the input data
+ * to the transaction. It is recommended to check if the object is local and call directly into its
+ * user data before calling this as the parceling and unparceling cost can be avoided. This AIBinder
+ * must be either built with a class or associated with a class before using this API.
*
* This does not affect the ownership of binder. When this function succeeds, the in parcel's
* ownership is passed to the caller. At this point, the parcel can be filled out and passed to
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 41daccc..ef4198d 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -55,6 +55,4 @@
oneway void sleepMsAsync(int ms);
void die(boolean cleanup);
-
- void useKernelBinderCallingId();
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 45b2776..0c3fbcd 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -73,7 +73,6 @@
BINDER_LIB_TEST_REGISTER_SERVER,
BINDER_LIB_TEST_ADD_SERVER,
BINDER_LIB_TEST_ADD_POLL_SERVER,
- BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION,
BINDER_LIB_TEST_CALL_BACK,
BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
BINDER_LIB_TEST_DELAYED_CALL_BACK,
@@ -605,24 +604,6 @@
EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
}
-TEST_F(BinderLibTest, NoBinderCallContextGuard) {
- IPCThreadState::SpGuard spGuard{"NoBinderCallContext"};
- IPCThreadState::SpGuard *origGuard = IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
-
- // yes, this test uses threads, but it's careful and uses fork in addServer
- EXPECT_DEATH({ IPCThreadState::self()->getCallingPid(); },
- "In context NoBinderCallContext, getCallingPid does not make sense.");
-
- IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
-}
-
-TEST_F(BinderLibTest, BinderCallContextGuard) {
- sp<IBinder> binder = addServer();
- Parcel data, reply;
- EXPECT_THAT(binder->transact(BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION, data, &reply),
- StatusEq(DEAD_OBJECT));
-}
-
TEST_F(BinderLibTest, AddServer)
{
sp<IBinder> server = addServer();
@@ -1281,18 +1262,6 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return ret;
}
- case BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION: {
- IPCThreadState::SpGuard spGuard{"GuardInBinderTransaction"};
- IPCThreadState::SpGuard *origGuard =
- IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
-
- // if the guard works, this should abort
- (void)IPCThreadState::self()->getCallingPid();
-
- IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
- return NO_ERROR;
- }
-
case BINDER_LIB_TEST_GETPID:
reply->writeInt32(getpid());
return NO_ERROR;
@@ -1520,11 +1489,6 @@
{
binderLibTestServiceName += String16(binderserversuffix);
- // Testing to make sure that calls that we are serving can use getCallin*
- // even though we don't here.
- IPCThreadState::SpGuard spGuard{"main server thread"};
- (void)IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
-
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
BinderLibTestService* testServicePtr;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 3f94df2..a96deb5 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -23,7 +23,6 @@
#include <android/binder_libbinder.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
@@ -192,13 +191,6 @@
_exit(1);
}
}
- Status useKernelBinderCallingId() override {
- // this is WRONG! It does not make sense when using RPC binder, and
- // because it is SO wrong, and so much code calls this, it should abort!
-
- (void)IPCThreadState::self()->getCallingPid();
- return Status::ok();
- }
};
sp<IBinder> MyBinderRpcTest::mHeldBinder;
@@ -895,19 +887,6 @@
}
}
-TEST_P(BinderRpc, UseKernelBinderCallingId) {
- auto proc = createRpcTestSocketServerProcess(1);
-
- // we can't allocate IPCThreadState so actually the first time should
- // succeed :(
- EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
-
- // second time! we catch the error :)
- EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
-
- proc.expectInvalid = true;
-}
-
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
auto proc = createRpcTestSocketServerProcess(1);
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 08800f7..a2868c6 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -317,6 +317,11 @@
std::unique_lock _lock{mMutex};
BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+ if (mSurfaceControl != nullptr) {
+ mTransformHint = mSurfaceControl->getTransformHint();
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ }
+
auto it = mSubmitted.find(graphicBufferId);
if (it == mSubmitted.end()) {
BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
@@ -596,6 +601,14 @@
status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
return mBbq->setFrameTimelineInfo(frameTimelineInfo);
}
+ protected:
+ uint32_t getTransformHint() const override {
+ if (mStickyTransform == 0 && !transformToDisplayInverse()) {
+ return mBbq->getLastTransformHint();
+ } else {
+ return 0;
+ }
+ }
};
// TODO: Can we coalesce this with frame updates? Need to confirm
@@ -765,4 +778,12 @@
return convertedFormat;
}
+uint32_t BLASTBufferQueue::getLastTransformHint() const {
+ if (mSurfaceControl != nullptr) {
+ return mSurfaceControl->getTransformHint();
+ } else {
+ return 0;
+ }
+}
+
} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 89e80c1..d27d1ec 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1288,7 +1288,7 @@
mUserHeight ? mUserHeight : mDefaultHeight);
return NO_ERROR;
case NATIVE_WINDOW_TRANSFORM_HINT:
- *value = static_cast<int>(mTransformHint);
+ *value = static_cast<int>(getTransformHint());
return NO_ERROR;
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
status_t err = NO_ERROR;
@@ -1858,7 +1858,7 @@
return getExtraBufferCount(extraBuffers);
}
-bool Surface::transformToDisplayInverse() {
+bool Surface::transformToDisplayInverse() const {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 37750fa..d7c07b9 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -166,7 +166,7 @@
Mutex::Autolock _l(mLock);
mWidth = width; mHeight = height;
if (mBbq) {
- mBbq->update(this, width, height, mFormat);
+ mBbq->update(mBbqChild, width, height, mFormat);
}
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 139dbb7..c4ca399 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -103,6 +103,8 @@
void setSidebandStream(const sp<NativeHandle>& stream);
+ uint32_t getLastTransformHint() const;
+
virtual ~BLASTBufferQueue();
private:
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 250e13f..48885eb 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -279,7 +279,6 @@
int dispatchGetLastQueuedBuffer2(va_list args);
int dispatchSetFrameTimelineInfo(va_list args);
int dispatchGetExtraBufferCount(va_list args);
- bool transformToDisplayInverse();
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -491,6 +490,8 @@
// mTransformHint is the transform probably applied to buffers of this
// window. this is only a hint, actual transform may differ.
uint32_t mTransformHint;
+ virtual uint32_t getTransformHint() const { return mTransformHint; }
+ bool transformToDisplayInverse() const;
// mProducerControlledByApp whether this buffer producer is controlled
// by the application
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 8546bbb..9947720 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -57,11 +57,12 @@
info.frameLeft == frameLeft && info.frameTop == frameTop &&
info.frameRight == frameRight && info.frameBottom == frameBottom &&
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
- info.visible == visible && info.trustedOverlay == trustedOverlay &&
- info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
- info.hasWallpaper == hasWallpaper && info.paused == paused &&
- info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.transform == transform && info.displayWidth == displayWidth &&
+ info.displayHeight == displayHeight &&
+ info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
+ info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
+ info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
+ info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
info.packageName == packageName && info.inputFeatures == inputFeatures &&
info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -99,6 +100,8 @@
parcel->writeFloat(transform.dtdy()) ?:
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
+ parcel->writeInt32(displayWidth) ?:
+ parcel->writeInt32(displayHeight) ?:
parcel->writeBool(visible) ?:
parcel->writeBool(focusable) ?:
parcel->writeBool(hasWallpaper) ?:
@@ -153,6 +156,8 @@
parcel->readFloat(&dtdy) ?:
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
+ parcel->readInt32(&displayWidth) ?:
+ parcel->readInt32(&displayHeight) ?:
parcel->readBool(&visible) ?:
parcel->readBool(&focusable) ?:
parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index c18a17f..493f2f4 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -55,6 +55,8 @@
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+ i.displayWidth = 1000;
+ i.displayHeight = 2000;
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
@@ -91,6 +93,8 @@
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
+ ASSERT_EQ(i.displayWidth, i2.displayWidth);
+ ASSERT_EQ(i.displayHeight, i2.displayHeight);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index bfb7465..63ed1ba 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -194,18 +194,6 @@
lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
}
-static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
- return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
- lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
- lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
- lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
- lhs.bottom == rhs.bottom;
-}
-
-static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
- return !(lhs == rhs);
-}
-
static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
return false;
@@ -288,7 +276,7 @@
}
static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
- *os << "LayerSettings {";
+ *os << "LayerSettings for '" << settings.name.c_str() << "' {";
*os << "\n .geometry = ";
PrintTo(settings.geometry, os);
*os << "\n .source = ";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 0eee564..77e01f4 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -37,10 +37,6 @@
0.f, 0.7f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
67.3f, 52.2f, 0.f, 1.f);
-const auto kScaleYOnly = mat4(1.f, 0.f, 0.f, 0.f,
- 0.f, 0.7f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 0.f, 0.f, 0.f, 1.f);
// clang-format on
// When setting layer.sourceDataspace, whether it matches the destination or not determines whether
// a color correction effect is added to the shader.
@@ -188,33 +184,51 @@
}
}
-static void drawTextureScaleLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
- const std::shared_ptr<ExternalTexture>& dstTexture,
- const std::shared_ptr<ExternalTexture>& srcTexture) {
+// The unique feature of these layers is that the boundary is slightly smaller than the rounded
+// rect crop, so the rounded edges intersect that boundary and require a different clipping method.
+static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
const Rect& displayRect = display.physicalDisplay;
- FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller
+
+ // clang-format off
+ const auto symmetric = mat4(0.9f, 0.f, 0.f, 0.f,
+ 0.f, 0.9f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 8.8f, 8.1f, 0.f, 1.f);
+ const auto asymmetric = mat4(0.9f, 0.f, 0.f, 0.f,
+ 0.f, 0.7f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 8.8f, 8.1f, 0.f, 1.f);
+
+ // clang-format on
LayerSettings layer{
.geometry =
Geometry{
.boundaries = rect,
- .roundedCornersCrop = rect,
- .positionTransform = kScaleAndTranslate,
- .roundedCornersRadius = 300,
+ .roundedCornersRadius = 27, // larger than the 20 above.
+ .roundedCornersCrop =
+ FloatRect(0, 0, displayRect.width(), displayRect.height()),
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
+ .isOpaque = 0,
.maxLuminanceNits = 1000.f,
- .textureTransform = kScaleYOnly,
}},
.sourceDataspace = kOtherDataSpace,
};
auto layers = std::vector<const LayerSettings*>{&layer};
- for (float alpha : {0.5f, 1.f}) {
- layer.alpha = alpha,
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ for (auto transform : {symmetric, asymmetric}) {
+ layer.geometry.positionTransform = transform;
+ // In real use, I saw alpha of 1.0 and 0.999, probably a mistake, but cache both shaders.
+ for (float alpha : {0.5f, 1.f}) {
+ layer.alpha = alpha,
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
}
}
@@ -293,7 +307,7 @@
drawImageLayers(renderengine, display, dstTexture, externalTexture);
// Draw layers for b/185569240.
- drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+ drawClippedLayers(renderengine, display, dstTexture, externalTexture);
const nsecs_t timeAfter = systemTime();
const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 05bbc12..6cc3d37 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -902,7 +902,16 @@
if (layer->shadow.length > 0) {
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
- drawShadow(canvas, bounds, layer->shadow);
+
+ // Technically, if bounds is a rect and roundRectClip is not empty,
+ // it means that the bounds and roundedCornersCrop were different
+ // enough that we should intersect them to find the proper shadow.
+ // In practice, this often happens when the two rectangles appear to
+ // not match due to rounding errors. Draw the rounded version, which
+ // looks more like the intent.
+ const auto& rrect =
+ bounds.isRect() && !roundRectClip.isEmpty() ? roundRectClip : bounds;
+ drawShadow(canvas, rrect, layer->shadow);
continue;
}
@@ -1092,8 +1101,8 @@
SkRRect clip;
if (cornerRadius > 0) {
- // it the crop and the bounds are equivalent then we don't need a clip
- if (bounds == crop) {
+ // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
+ if (bounds == crop || crop.isEmpty()) {
return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
}
@@ -1107,34 +1116,47 @@
const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+ const bool leftEqual = bounds.fLeft == crop.fLeft;
+ const bool topEqual = bounds.fTop == crop.fTop;
+ const bool rightEqual = bounds.fRight == crop.fRight;
+ const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
// compute the UpperLeft corner radius
- if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) {
+ if (leftEqual && topEqual) {
radii[0].set(cornerRadius, cornerRadius);
- } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) {
+ } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
+ insetCrop.contains(bounds.fLeft, bounds.fTop)) {
radii[0].set(0, 0);
} else {
intersectionIsRoundRect = false;
}
// compute the UpperRight corner radius
- if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) {
+ if (rightEqual && topEqual) {
radii[1].set(cornerRadius, cornerRadius);
- } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) {
+ } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fRight <= insetCrop.fRight) ||
+ insetCrop.contains(bounds.fRight, bounds.fTop)) {
radii[1].set(0, 0);
} else {
intersectionIsRoundRect = false;
}
// compute the BottomRight corner radius
- if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) {
+ if (rightEqual && bottomEqual) {
radii[2].set(cornerRadius, cornerRadius);
- } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) {
+ } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
+ insetCrop.contains(bounds.fRight, bounds.fBottom)) {
radii[2].set(0, 0);
} else {
intersectionIsRoundRect = false;
}
// compute the BottomLeft corner radius
- if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) {
+ if (leftEqual && bottomEqual) {
radii[3].set(cornerRadius, cornerRadius);
- } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.fBottom) {
+ } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
+ insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
radii[3].set(0, 0);
} else {
intersectionIsRoundRect = false;
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 9e989ea..14a3b61 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -90,10 +90,8 @@
// matrix transforming from linear XYZ to linear RGB immediately before OETF.
// We also provide additional HDR metadata upon creating the shader:
// * The max display luminance is the max luminance of the physical display in nits
-// * The max mastering luminance is provided as the max luminance from the SMPTE 2086
-// standard.
-// * The max content luminance is provided as the max light level from the CTA 861.3
-// standard.
+// * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
+// or as the max light level from the CTA 861.3 standard.
sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
const LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 636fbde..9dc9beb 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -36,6 +36,7 @@
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
+using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -597,7 +598,7 @@
if (!outDataspace) {
return BAD_VALUE;
}
- aidl::android::hardware::graphics::common::Dataspace dataspace;
+ AidlDataspace dataspace;
status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
&dataspace);
if (error) {
@@ -841,6 +842,7 @@
uint32_t pixelFormatFourCC;
uint64_t pixelFormatModifier;
uint64_t usage;
+ AidlDataspace dataspace;
uint64_t allocationSize;
uint64_t protectedContent;
ExtendableType compression;
@@ -892,6 +894,11 @@
if (error != NO_ERROR) {
return error;
}
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::DATASPACE,
+ gralloc4::decodeDataspace, &dataspace);
+ if (error != NO_ERROR) {
+ return error;
+ }
error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
gralloc4::decodeAllocationSize, &allocationSize);
if (error != NO_ERROR) {
@@ -932,6 +939,7 @@
<< "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
<< std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
<< ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+ << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace)
<< ", compressed: ";
if (less) {
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
index 69a586e..a9ca369 100644
--- a/libs/ui/include/ui/BlurRegion.h
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -20,6 +20,8 @@
#include <iosfwd>
#include <iostream>
+#include <math/HashCombine.h>
+
namespace android {
struct BlurRegion {
@@ -33,6 +35,16 @@
int top;
int right;
int bottom;
+
+ inline bool operator==(const BlurRegion& other) const {
+ return blurRadius == other.blurRadius && cornerRadiusTL == other.cornerRadiusTL &&
+ cornerRadiusTR == other.cornerRadiusTR && cornerRadiusBL == other.cornerRadiusBL &&
+ cornerRadiusBR == other.cornerRadiusBR && alpha == other.alpha &&
+ left == other.left && top == other.top && right == other.right &&
+ bottom == other.bottom;
+ }
+
+ inline bool operator!=(const BlurRegion& other) const { return !(*this == other); }
};
static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) {
@@ -50,4 +62,15 @@
*os << "\n}";
}
-} // namespace android
\ No newline at end of file
+} // namespace android
+
+namespace std {
+template <>
+struct hash<android::BlurRegion> {
+ size_t operator()(const android::BlurRegion& region) const {
+ return android::hashCombine(region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+ region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+ region.left, region.top, region.right, region.bottom);
+ }
+};
+} // namespace std
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 6ed9593..881024f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -296,7 +296,7 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor, vec2 displaySize)
+ ui::Transform transform, float globalScaleFactor, int2 displaySize)
: seq(nextSeq()),
eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 45c5e24..b5d3571 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -215,7 +215,7 @@
int32_t targetFlags;
ui::Transform transform;
float globalScaleFactor;
- vec2 displaySize;
+ int2 displaySize;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -228,7 +228,7 @@
int32_t resolvedFlags;
DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor, vec2 displaySize);
+ ui::Transform transform, float globalScaleFactor, int2 displaySize);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8bc877f..485a53a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2385,7 +2385,7 @@
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
inputTarget.displaySize =
- vec2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
+ int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
}
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 2543852..1c4980b 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -101,7 +101,7 @@
float globalScaleFactor = 1.0f;
// Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
- vec2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+ int2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE};
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index fcf8299..54daa10 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -682,7 +682,10 @@
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
- if (mSidebandStreamChanged.exchange(false)) {
+ // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+ const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
+ if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
mSidebandStream = s.sidebandStream;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 06f26eb..dc6eab4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -110,6 +110,9 @@
// CachedSet and punching a hole.
bool requiresHolePunch() const;
+ // True if any constituent layer is configured to blur any layers behind.
+ bool hasBlurBehind() const;
+
// Add a layer that will be drawn behind this one. ::render() will render a
// hole in this CachedSet's buffer, allowing the supplied layer to peek
// through. Must be called before ::render().
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 864251f..213c55e 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 <numeric>
#include <vector>
namespace android {
@@ -60,6 +61,73 @@
bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
std::chrono::steady_clock::time_point now);
+ // A Run is a sequence of CachedSets, which is a candidate for flattening into a single
+ // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
+ // CachedSet
+ class Run {
+ public:
+ // A builder for a Run, to aid in construction
+ class Builder {
+ private:
+ std::vector<CachedSet>::const_iterator mStart;
+ std::vector<size_t> mLengths;
+ const CachedSet* mHolePunchCandidate = nullptr;
+
+ public:
+ // Initializes a Builder a CachedSet to start from.
+ // This start iterator must be an iterator for mLayers
+ void init(const std::vector<CachedSet>::const_iterator& start) {
+ mStart = start;
+ mLengths.push_back(start->getLayerCount());
+ }
+
+ // Appends a new CachedSet to the end of the run
+ // The provided length must be the size of the next sequential CachedSet in layers
+ void append(size_t length) { mLengths.push_back(length); }
+
+ // Sets the hole punch candidate for the Run.
+ void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
+ mHolePunchCandidate = holePunchCandidate;
+ }
+
+ // Builds a Run instance, if a valid Run may be built.
+ std::optional<Run> validateAndBuild() {
+ if (mLengths.size() <= 1) {
+ return std::nullopt;
+ }
+
+ return Run(mStart,
+ std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
+ [](size_t left, size_t right) { return left + right; }),
+ mHolePunchCandidate);
+ }
+
+ void reset() { *this = {}; }
+ };
+
+ // Gets the starting CachedSet of this run.
+ // This is an iterator into mLayers
+ const std::vector<CachedSet>::const_iterator& getStart() const { return mStart; }
+ // Gets the total number of layers encompassing this Run.
+ size_t getLayerLength() const { return mLength; }
+ // Gets the hole punch candidate for this Run.
+ const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; }
+
+ private:
+ Run(std::vector<CachedSet>::const_iterator start, size_t length,
+ const CachedSet* holePunchCandidate)
+ : mStart(start), mLength(length), mHolePunchCandidate(holePunchCandidate) {}
+ const std::vector<CachedSet>::const_iterator mStart;
+ const size_t mLength;
+ const CachedSet* const mHolePunchCandidate;
+
+ friend class Builder;
+ };
+
+ std::vector<Run> findCandidateRuns(std::chrono::steady_clock::time_point now) const;
+
+ std::optional<Run> findBestRun(std::vector<Run>& runs) const;
+
void buildCachedSets(std::chrono::steady_clock::time_point now);
const bool mEnableHolePunch;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 3391273..fef0dfb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -26,6 +26,7 @@
#include <string>
#include "DisplayHardware/Hal.h"
+#include "math/HashCombine.h"
namespace std {
template <typename T>
@@ -48,24 +49,26 @@
// clang-format off
enum class LayerStateField : uint32_t {
- None = 0u,
- Id = 1u << 0,
- Name = 1u << 1,
- DisplayFrame = 1u << 2,
- SourceCrop = 1u << 3,
- BufferTransform = 1u << 4,
- BlendMode = 1u << 5,
- Alpha = 1u << 6,
- LayerMetadata = 1u << 7,
- VisibleRegion = 1u << 8,
- Dataspace = 1u << 9,
- PixelFormat = 1u << 10,
- ColorTransform = 1u << 11,
- SurfaceDamage = 1u << 12,
- CompositionType = 1u << 13,
- SidebandStream = 1u << 14,
- Buffer = 1u << 15,
- SolidColor = 1u << 16,
+ None = 0u,
+ Id = 1u << 0,
+ Name = 1u << 1,
+ DisplayFrame = 1u << 2,
+ SourceCrop = 1u << 3,
+ BufferTransform = 1u << 4,
+ BlendMode = 1u << 5,
+ Alpha = 1u << 6,
+ LayerMetadata = 1u << 7,
+ VisibleRegion = 1u << 8,
+ Dataspace = 1u << 9,
+ PixelFormat = 1u << 10,
+ ColorTransform = 1u << 11,
+ SurfaceDamage = 1u << 12,
+ CompositionType = 1u << 13,
+ SidebandStream = 1u << 14,
+ Buffer = 1u << 15,
+ SolidColor = 1u << 16,
+ BackgroundBlurRadius = 1u << 17,
+ BlurRegions = 1u << 18,
};
// clang-format on
@@ -225,6 +228,9 @@
const std::string& getName() const { return mName.get(); }
Rect getDisplayFrame() const { return mDisplayFrame.get(); }
const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
+ bool hasBlurBehind() const {
+ return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
+ }
hardware::graphics::composer::hal::Composition getCompositionType() const {
return mCompositionType.get();
}
@@ -398,7 +404,45 @@
return std::vector<std::string>{stream.str()};
}};
- static const constexpr size_t kNumNonUniqueFields = 14;
+ OutputLayerState<int32_t, LayerStateField::BackgroundBlurRadius> mBackgroundBlurRadius{
+ [](auto layer) {
+ return layer->getLayerFE().getCompositionState()->backgroundBlurRadius;
+ }};
+
+ using BlurRegionsState =
+ OutputLayerState<std::vector<BlurRegion>, LayerStateField::BlurRegions>;
+ BlurRegionsState mBlurRegions{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->blurRegions;
+ },
+ [](const std::vector<BlurRegion>& regions) {
+ std::vector<std::string> result;
+ for (const auto region : regions) {
+ std::string str;
+ base::StringAppendF(&str,
+ "{radius=%du, cornerRadii=[%f, %f, "
+ "%f, %f], alpha=%f, rect=[%d, "
+ "%d, %d, %d]",
+ region.blurRadius,
+ region.cornerRadiusTL,
+ region.cornerRadiusTR,
+ region.cornerRadiusBL,
+ region.cornerRadiusBR, region.alpha,
+ region.left, region.top, region.right,
+ region.bottom);
+ result.push_back(str);
+ }
+ return result;
+ },
+ BlurRegionsState::getDefaultEquals(),
+ [](const std::vector<BlurRegion>& regions) {
+ size_t hash = 0;
+ for (const auto& region : regions) {
+ android::hashCombineSingle(hash, region);
+ }
+ return hash;
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 16;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -413,10 +457,10 @@
std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
return {
- &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
- &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
- &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
- &mBuffer, &mSolidColor,
+ &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode,
+ &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
+ &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
+ &mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1ffb1c8..953eb76 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -192,16 +192,7 @@
if (const auto halDisplayId = HalDisplayId::tryCast(mId);
outputLayer && !mIsDisconnected && halDisplayId) {
auto& hwc = getCompositionEngine().getHwComposer();
- // Note: For the moment we ensure it is safe to take a reference to the
- // HWComposer implementation by destroying all the OutputLayers (and
- // hence the HWC2::Layers they own) before setting a new HWComposer. See
- // for example SurfaceFlinger::updateVrFlinger().
- // TODO(b/121291683): Make this safer.
- auto hwcLayer =
- std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId),
- [&hwc, id = *halDisplayId](HWC2::Layer* layer) {
- hwc.destroyLayer(id, layer);
- });
+ auto hwcLayer = hwc.createLayer(*halDisplayId);
ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
getName().c_str());
outputLayer->setHwcLayer(std::move(hwcLayer));
@@ -267,7 +258,7 @@
auto& hwc = getCompositionEngine().getHwComposer();
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
- &changes);
+ getState().earliestPresentTime, &changes);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -367,13 +358,8 @@
return fences;
}
- {
- ATRACE_NAME("wait for earliest present time");
- std::this_thread::sleep_until(getState().earliestPresentTime);
- }
-
auto& hwc = getCompositionEngine().getHwComposer();
- hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
+ hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 67854cf..63085cc 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -280,6 +280,11 @@
return layerFE.hasRoundedCorners();
}
+bool CachedSet::hasBlurBehind() const {
+ return std::any_of(mLayers.cbegin(), mLayers.cend(),
+ [](const Layer& layer) { return layer.getState()->hasBlurBehind(); });
+}
+
namespace {
bool contains(const Rect& outer, const Rect& inner) {
return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 4453a99..294ec4b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -327,85 +327,94 @@
return true;
}
-void Flattener::buildCachedSets(time_point now) {
- struct Run {
- Run(std::vector<CachedSet>::const_iterator start, size_t length)
- : start(start), length(length) {}
-
- std::vector<CachedSet>::const_iterator start;
- size_t length;
- };
-
- if (mLayers.empty()) {
- ALOGV("[%s] No layers found, returning", __func__);
- return;
- }
-
+std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
std::vector<Run> runs;
bool isPartOfRun = false;
-
- // Keep track of the layer that follows a run. It's possible that we will
- // render it with a hole-punch.
- const CachedSet* holePunchLayer = nullptr;
+ Run::Builder builder;
+ bool firstLayer = true;
+ bool runHasFirstLayer = false;
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
- if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
- // Layer is inactive
+ const bool layerIsInactive = now - currentSet->getLastUpdate() > kActiveLayerTimeout;
+ const bool layerHasBlur = currentSet->hasBlurBehind();
+ if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur)) {
if (isPartOfRun) {
- runs.back().length += currentSet->getLayerCount();
+ builder.append(currentSet->getLayerCount());
} else {
// Runs can't start with a non-buffer layer
if (currentSet->getFirstLayer().getBuffer() == nullptr) {
ALOGV("[%s] Skipping initial non-buffer layer", __func__);
} else {
- runs.emplace_back(currentSet, currentSet->getLayerCount());
+ builder.init(currentSet);
+ if (firstLayer) {
+ runHasFirstLayer = true;
+ }
isPartOfRun = true;
}
}
} else if (isPartOfRun) {
- // Runs must be at least 2 sets long or there's nothing to combine
- if (runs.back().start->getLayerCount() == runs.back().length) {
- runs.pop_back();
- } else {
- // The prior run contained at least two sets. Currently, we'll
- // only possibly merge a single run, so only keep track of a
- // holePunchLayer if this is the first run.
- if (runs.size() == 1) {
- holePunchLayer = &(*currentSet);
- }
-
- // TODO(b/185114532: Break out of the loop? We may find more runs, but we
- // won't do anything with them.
+ builder.setHolePunchCandidate(&(*currentSet));
+ if (auto run = builder.validateAndBuild(); run) {
+ runs.push_back(*run);
}
+ runHasFirstLayer = false;
+ builder.reset();
isPartOfRun = false;
}
+
+ firstLayer = false;
}
- // Check for at least 2 sets one more time in case the set includes the last layer
- if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
- runs.pop_back();
+ // If we're in the middle of a run at the end, we still need to validate and build it.
+ if (isPartOfRun) {
+ if (auto run = builder.validateAndBuild(); run) {
+ runs.push_back(*run);
+ }
}
ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
+ return runs;
+}
+
+std::optional<Flattener::Run> Flattener::findBestRun(std::vector<Flattener::Run>& runs) const {
if (runs.empty()) {
+ return std::nullopt;
+ }
+
+ // TODO (b/181192467): Choose the best run, instead of just the first.
+ return runs[0];
+}
+
+void Flattener::buildCachedSets(time_point now) {
+ if (mLayers.empty()) {
+ ALOGV("[%s] No layers found, returning", __func__);
return;
}
- mNewCachedSet.emplace(*runs[0].start);
+ std::vector<Run> runs = findCandidateRuns(now);
+
+ std::optional<Run> bestRun = findBestRun(runs);
+
+ if (!bestRun) {
+ return;
+ }
+
+ mNewCachedSet.emplace(*bestRun->getStart());
mNewCachedSet->setLastUpdate(now);
- auto currentSet = runs[0].start;
- while (mNewCachedSet->getLayerCount() < runs[0].length) {
+ auto currentSet = bestRun->getStart();
+ while (mNewCachedSet->getLayerCount() < bestRun->getLayerLength()) {
++currentSet;
mNewCachedSet->append(*currentSet);
}
- if (mEnableHolePunch && holePunchLayer && holePunchLayer->requiresHolePunch()) {
+ if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
+ bestRun->getHolePunchCandidate()->requiresHolePunch()) {
// Add the pip layer to mNewCachedSet, but in a special way - it should
// replace the buffer with a clear round rect.
- mNewCachedSet->addHolePunchLayerIfFeasible(*holePunchLayer,
- runs[0].start == mLayers.cbegin());
+ mNewCachedSet->addHolePunchLayerIfFeasible(*bestRun->getHolePunchCandidate(),
+ bestRun->getStart() == mLayers.cbegin());
}
// TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8a83639..e12cb57 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -533,16 +533,15 @@
TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) {
sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
- StrictMock<HWC2::mock::Layer> hwcLayer;
+ auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>();
EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID)))
- .WillOnce(Return(&hwcLayer));
+ .WillOnce(Return(hwcLayer));
auto outputLayer = mDisplay->createOutputLayer(layerFE);
- EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
+ EXPECT_EQ(hwcLayer.get(), outputLayer->getHwcLayer());
- EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer));
outputLayer.reset();
}
@@ -627,7 +626,7 @@
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _))
.WillOnce(Return(INVALID_OPERATION));
mDisplay->chooseCompositionStrategy();
@@ -649,7 +648,8 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
+ EXPECT_CALL(mHwComposer,
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
@@ -679,8 +679,9 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
- .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
+ EXPECT_CALL(mHwComposer,
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(changes), Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
@@ -867,7 +868,8 @@
sp<Fence> layer1Fence = new Fence();
sp<Fence> layer2Fence = new Fence();
- EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _))
+ .Times(1);
EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
.WillOnce(Return(presentFence));
EXPECT_CALL(mHwComposer,
@@ -1039,7 +1041,7 @@
mDisplay->editState().isEnabled = true;
- EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
mDisplay->postFramebuffer();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index bac894a..cd2d09e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -48,15 +48,15 @@
MOCK_METHOD3(allocateVirtualDisplay,
std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
- MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
- MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
- MOCK_METHOD3(getDeviceCompositionChanges,
- status_t(HalDisplayId, bool,
+ MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
+ MOCK_METHOD4(getDeviceCompositionChanges,
+ status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
ui::Dataspace));
- MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+ MOCK_METHOD2(presentAndGetReleaseFences,
+ status_t(HalDisplayId, std::chrono::steady_clock::time_point));
MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index a39331c..488f64d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -567,5 +567,30 @@
}
}
+TEST_F(CachedSetTest, hasBlurBehind) {
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ mTestLayers[1]->layerState->update(&mTestLayers[1]->outputLayer);
+ mTestLayers[2]->layerFECompositionState.blurRegions.push_back(
+ BlurRegion{1, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet1(layer1);
+ CachedSet cachedSet2(layer2);
+ CachedSet cachedSet3(layer3);
+
+ // Cached set 4 will consist of layers 1 and 2, which will contain a blur behind
+ CachedSet cachedSet4(layer1);
+ cachedSet4.addLayer(layer2.getState(), kStartTime);
+
+ EXPECT_FALSE(cachedSet1.hasBlurBehind());
+ EXPECT_TRUE(cachedSet2.hasBlurBehind());
+ EXPECT_TRUE(cachedSet3.hasBlurBehind());
+ EXPECT_TRUE(cachedSet4.hasBlurBehind());
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 25fab49..42096f3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -642,5 +642,149 @@
EXPECT_EQ(&mTestLayers[2]->outputLayer, peekThroughLayer1);
EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
}
+
+TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerState2->update(&mTestLayers[1]->outputLayer);
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the first two layers inactive, which contain the blur behind
+ mTime += 200ms;
+ layerState3->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // the new flattened layer is replaced
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerState2->update(&mTestLayers[1]->outputLayer);
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the last two layers inactive, which contains the blur layer, but does not contain the
+ // first layer
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // nothing is flattened because the last two frames cannot be cached due to containing a blur
+ // layer
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+}
+
+TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+
+ auto& layerStateWithBlurBehind = mTestLayers[1]->layerState;
+ mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+ layerStateWithBlurBehind->update(&mTestLayers[1]->outputLayer);
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ auto& layerState4 = mTestLayers[3]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& blurOverrideBuffer =
+ layerStateWithBlurBehind->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerStateWithBlurBehind.get(),
+ layerState3.get(),
+ layerState4.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // Mark the last three layers inactive, which contains the blur layer, but does not contain the
+ // first layer
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate();
+
+ // layers would be flattened but the buffer would not be overridden
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+ for (const auto layer : layers) {
+ EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+
+ // the new flattened layer is replaced
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, blurOverrideBuffer);
+ EXPECT_NE(nullptr, overrideBuffer3);
+ EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 948c850..a09ce14 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -58,6 +58,10 @@
const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
.value = std::vector<uint8_t>({1, 3}),
};
+const constexpr int32_t sBgBlurRadiusOne = 3;
+const constexpr int32_t sBgBlurRadiusTwo = 4;
+const BlurRegion sBlurRegionOne = BlurRegion{1, 2.f, 3.f, 4.f, 5.f, 6.f, 7, 8, 9, 10};
+const BlurRegion sBlurRegionTwo = BlurRegion{2, 3.f, 4.f, 5.f, 6.f, 7.f, 8, 9, 10, 11};
struct LayerStateTest : public testing::Test {
LayerStateTest() {
@@ -830,6 +834,114 @@
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
}
+TEST_F(LayerStateTest, updateBackgroundBlur) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BackgroundBlurRadius), updates);
+}
+
+TEST_F(LayerStateTest, compareBackgroundBlur) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ LayerStateField::BackgroundBlurRadius);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlurRegions) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlurRegions), updates);
+}
+
+TEST_F(LayerStateTest, compareBlurRegions) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlurRegions);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_noBlur_returnsFalse) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_FALSE(mLayerState->hasBlurBehind());
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_withBackgroundBlur_returnsTrue) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->hasBlurBehind());
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->hasBlurBehind());
+}
+
TEST_F(LayerStateTest, dumpDoesNotCrash) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index d04b5f7..27146ab 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -78,7 +78,19 @@
}
Display::~Display() {
- mLayers.clear();
+ // Note: The calls to onOwningDisplayDestroyed() are allowed (and expected)
+ // to call Display::onLayerDestroyed(). As that call removes entries from
+ // mLayers, we do not want to have a for loop directly over it here. Since
+ // the end goal is an empty mLayers anyway, we just go ahead and swap an
+ // initially empty local container with mLayers, and then enumerate
+ // the contents of the local container.
+ Layers destroyingLayers;
+ std::swap(mLayers, destroyingLayers);
+ for (const auto& [_, weakLayer] : destroyingLayers) {
+ if (std::shared_ptr layer = weakLayer.lock()) {
+ layer->onOwningDisplayDestroyed();
+ }
+ }
Error error = Error::NONE;
const char* msg;
@@ -110,29 +122,21 @@
return static_cast<Error>(intError);
}
-Error Display::createLayer(HWC2::Layer** outLayer) {
- if (!outLayer) {
- return Error::BAD_PARAMETER;
- }
+base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> Display::createLayer() {
HWLayerId layerId = 0;
auto intError = mComposer.createLayer(mId, &layerId);
auto error = static_cast<Error>(intError);
if (error != Error::NONE) {
- return error;
+ return base::unexpected(error);
}
- auto layer = std::make_unique<impl::Layer>(mComposer, mCapabilities, mId, layerId);
- *outLayer = layer.get();
- mLayers.emplace(layerId, std::move(layer));
- return Error::NONE;
+ auto layer = std::make_shared<impl::Layer>(mComposer, mCapabilities, *this, layerId);
+ mLayers.emplace(layerId, layer);
+ return layer;
}
-Error Display::destroyLayer(HWC2::Layer* layer) {
- if (!layer) {
- return Error::BAD_PARAMETER;
- }
- mLayers.erase(layer->getId());
- return Error::NONE;
+void Display::onLayerDestroyed(hal::HWLayerId layerId) {
+ mLayers.erase(layerId);
}
bool Display::isVsyncPeriodSwitchSupported() const {
@@ -161,7 +165,7 @@
auto type = types[element];
ALOGV("getChangedCompositionTypes: adding %" PRIu64 " %s",
layer->getId(), to_string(type).c_str());
- outTypes->emplace(layer, type);
+ outTypes->emplace(layer.get(), type);
} else {
ALOGE("getChangedCompositionTypes: invalid layer %" PRIu64 " found"
" on display %" PRIu64, layerIds[element], mId);
@@ -254,7 +258,7 @@
if (layer) {
auto layerRequest =
static_cast<LayerRequest>(layerRequests[element]);
- outLayerRequests->emplace(layer, layerRequest);
+ outLayerRequests->emplace(layer.get(), layerRequest);
} else {
ALOGE("getRequests: invalid layer %" PRIu64 " found on display %"
PRIu64, layerIds[element], mId);
@@ -340,7 +344,7 @@
auto layer = getLayerById(layerIds[element]);
if (layer) {
sp<Fence> fence(new Fence(fenceFds[element]));
- releaseFences.emplace(layer, fence);
+ releaseFences.emplace(layer.get(), fence);
} else {
ALOGE("getReleaseFences: invalid layer %" PRIu64
" found on display %" PRIu64, layerIds[element], mId);
@@ -550,12 +554,9 @@
// Other Display methods
-HWC2::Layer* Display::getLayerById(HWLayerId id) const {
- if (mLayers.count(id) == 0) {
- return nullptr;
- }
-
- return mLayers.at(id).get();
+std::shared_ptr<HWC2::Layer> Display::getLayerById(HWLayerId id) const {
+ auto it = mLayers.find(id);
+ return it != mLayers.end() ? it->second.lock() : nullptr;
}
} // namespace impl
@@ -566,47 +567,78 @@
namespace impl {
Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
- HWDisplayId displayId, HWLayerId layerId)
+ HWC2::Display& display, HWLayerId layerId)
: mComposer(composer),
mCapabilities(capabilities),
- mDisplayId(displayId),
+ mDisplay(&display),
mId(layerId),
mColorMatrix(android::mat4()) {
- ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
+ ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, display.getId());
}
Layer::~Layer()
{
- auto intError = mComposer.destroyLayer(mDisplayId, mId);
+ onOwningDisplayDestroyed();
+}
+
+void Layer::onOwningDisplayDestroyed() {
+ // Note: onOwningDisplayDestroyed() may be called to perform cleanup by
+ // either the Layer dtor or by the Display dtor and must be safe to call
+ // from either path. In particular, the call to Display::onLayerDestroyed()
+ // is expected to be safe to do,
+
+ if (CC_UNLIKELY(!mDisplay)) {
+ return;
+ }
+
+ mDisplay->onLayerDestroyed(mId);
+
+ // Note: If the HWC display was actually disconnected, these calls are will
+ // return an error. We always make them as there may be other reasons for
+ // the HWC2::Display to be destroyed.
+ auto intError = mComposer.destroyLayer(mDisplay->getId(), mId);
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::NONE,
"destroyLayer(%" PRIu64 ", %" PRIu64 ")"
" failed: %s (%d)",
- mDisplayId, mId, to_string(error).c_str(), intError);
+ mDisplay->getId(), mId, to_string(error).c_str(), intError);
+
+ mDisplay = nullptr;
}
Error Layer::setCursorPosition(int32_t x, int32_t y)
{
- auto intError = mComposer.setCursorPosition(mDisplayId, mId, x, y);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setCursorPosition(mDisplay->getId(), mId, x, y);
return static_cast<Error>(intError);
}
Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer,
const sp<Fence>& acquireFence)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (buffer == nullptr && mBufferSlot == slot) {
return Error::NONE;
}
mBufferSlot = slot;
int32_t fenceFd = acquireFence->dup();
- auto intError = mComposer.setLayerBuffer(mDisplayId, mId, slot, buffer,
- fenceFd);
+ auto intError = mComposer.setLayerBuffer(mDisplay->getId(), mId, slot, buffer, fenceFd);
return static_cast<Error>(intError);
}
Error Layer::setSurfaceDamage(const Region& damage)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (damage.isRect() && mDamageRegion.isRect() &&
(damage.getBounds() == mDamageRegion.getBounds())) {
return Error::NONE;
@@ -617,8 +649,8 @@
// rects for HWC
Hwc2::Error intError = Hwc2::Error::NONE;
if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
- intError = mComposer.setLayerSurfaceDamage(mDisplayId,
- mId, std::vector<Hwc2::IComposerClient::Rect>());
+ intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId,
+ std::vector<Hwc2::IComposerClient::Rect>());
} else {
size_t rectCount = 0;
auto rectArray = damage.getArray(&rectCount);
@@ -629,7 +661,7 @@
rectArray[rect].right, rectArray[rect].bottom});
}
- intError = mComposer.setLayerSurfaceDamage(mDisplayId, mId, hwcRects);
+ intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId, hwcRects);
}
return static_cast<Error>(intError);
@@ -637,34 +669,54 @@
Error Layer::setBlendMode(BlendMode mode)
{
- auto intError = mComposer.setLayerBlendMode(mDisplayId, mId, mode);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerBlendMode(mDisplay->getId(), mId, mode);
return static_cast<Error>(intError);
}
Error Layer::setColor(Color color) {
- auto intError = mComposer.setLayerColor(mDisplayId, mId, color);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerColor(mDisplay->getId(), mId, color);
return static_cast<Error>(intError);
}
Error Layer::setCompositionType(Composition type)
{
- auto intError = mComposer.setLayerCompositionType(mDisplayId, mId, type);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerCompositionType(mDisplay->getId(), mId, type);
return static_cast<Error>(intError);
}
Error Layer::setDataspace(Dataspace dataspace)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (dataspace == mDataSpace) {
return Error::NONE;
}
mDataSpace = dataspace;
- auto intError = mComposer.setLayerDataspace(mDisplayId, mId, mDataSpace);
+ auto intError = mComposer.setLayerDataspace(mDisplay->getId(), mId, mDataSpace);
return static_cast<Error>(intError);
}
Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
const android::HdrMetadata& metadata)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (metadata == mHdrMetadata) {
return Error::NONE;
}
@@ -705,7 +757,7 @@
}
Error error = static_cast<Error>(
- mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
+ mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas));
if (validTypes & HdrMetadata::HDR10PLUS) {
if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
@@ -715,8 +767,9 @@
std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
perFrameMetadataBlobs.push_back(
{Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
- Error setMetadataBlobsError = static_cast<Error>(
- mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs));
+ Error setMetadataBlobsError =
+ static_cast<Error>(mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId,
+ perFrameMetadataBlobs));
if (error == Error::NONE) {
return setMetadataBlobsError;
}
@@ -726,46 +779,70 @@
Error Layer::setDisplayFrame(const Rect& frame)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top,
frame.right, frame.bottom};
- auto intError = mComposer.setLayerDisplayFrame(mDisplayId, mId, hwcRect);
+ auto intError = mComposer.setLayerDisplayFrame(mDisplay->getId(), mId, hwcRect);
return static_cast<Error>(intError);
}
Error Layer::setPlaneAlpha(float alpha)
{
- auto intError = mComposer.setLayerPlaneAlpha(mDisplayId, mId, alpha);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerPlaneAlpha(mDisplay->getId(), mId, alpha);
return static_cast<Error>(intError);
}
Error Layer::setSidebandStream(const native_handle_t* stream)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (mCapabilities.count(Capability::SIDEBAND_STREAM) == 0) {
ALOGE("Attempted to call setSidebandStream without checking that the "
"device supports sideband streams");
return Error::UNSUPPORTED;
}
- auto intError = mComposer.setLayerSidebandStream(mDisplayId, mId, stream);
+ auto intError = mComposer.setLayerSidebandStream(mDisplay->getId(), mId, stream);
return static_cast<Error>(intError);
}
Error Layer::setSourceCrop(const FloatRect& crop)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
Hwc2::IComposerClient::FRect hwcRect{
crop.left, crop.top, crop.right, crop.bottom};
- auto intError = mComposer.setLayerSourceCrop(mDisplayId, mId, hwcRect);
+ auto intError = mComposer.setLayerSourceCrop(mDisplay->getId(), mId, hwcRect);
return static_cast<Error>(intError);
}
Error Layer::setTransform(Transform transform)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
auto intTransform = static_cast<Hwc2::Transform>(transform);
- auto intError = mComposer.setLayerTransform(mDisplayId, mId, intTransform);
+ auto intError = mComposer.setLayerTransform(mDisplay->getId(), mId, intTransform);
return static_cast<Error>(intError);
}
Error Layer::setVisibleRegion(const Region& region)
{
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (region.isRect() && mVisibleRegion.isRect() &&
(region.getBounds() == mVisibleRegion.getBounds())) {
return Error::NONE;
@@ -781,22 +858,30 @@
rectArray[rect].right, rectArray[rect].bottom});
}
- auto intError = mComposer.setLayerVisibleRegion(mDisplayId, mId, hwcRects);
+ auto intError = mComposer.setLayerVisibleRegion(mDisplay->getId(), mId, hwcRects);
return static_cast<Error>(intError);
}
Error Layer::setZOrder(uint32_t z)
{
- auto intError = mComposer.setLayerZOrder(mDisplayId, mId, z);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerZOrder(mDisplay->getId(), mId, z);
return static_cast<Error>(intError);
}
// Composer HAL 2.3
Error Layer::setColorTransform(const android::mat4& matrix) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
if (matrix == mColorMatrix) {
return Error::NONE;
}
- auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray());
+ auto intError = mComposer.setLayerColorTransform(mDisplay->getId(), mId, matrix.asArray());
Error error = static_cast<Error>(intError);
if (error != Error::NONE) {
return error;
@@ -808,7 +893,12 @@
// Composer HAL 2.4
Error Layer::setLayerGenericMetadata(const std::string& name, bool mandatory,
const std::vector<uint8_t>& value) {
- auto intError = mComposer.setLayerGenericMetadata(mDisplayId, mId, name, mandatory, value);
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError =
+ mComposer.setLayerGenericMetadata(mDisplay->getId(), mId, name, mandatory, value);
return static_cast<Error>(intError);
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e7bf286..fae95e7 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/expected.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
@@ -84,10 +85,11 @@
virtual void setConnected(bool connected) = 0; // For use by Device only
virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
virtual bool isVsyncPeriodSwitchSupported() const = 0;
+ virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
[[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0;
- [[clang::warn_unused_result]] virtual hal::Error createLayer(Layer** outLayer) = 0;
- [[clang::warn_unused_result]] virtual hal::Error destroyLayer(Layer* layer) = 0;
+ [[clang::warn_unused_result]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
+ createLayer() = 0;
[[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
[[clang::warn_unused_result]] virtual hal::Error getColorModes(
@@ -152,6 +154,8 @@
namespace impl {
+class Layer;
+
class Display : public HWC2::Display {
public:
Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId,
@@ -160,10 +164,9 @@
// Required by HWC2
hal::Error acceptChanges() override;
- hal::Error createLayer(Layer** outLayer) override;
- hal::Error destroyLayer(Layer*) override;
+ base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> createLayer() override;
hal::Error getChangedCompositionTypes(
- std::unordered_map<Layer*, hal::Composition>* outTypes) override;
+ std::unordered_map<HWC2::Layer*, hal::Composition>* outTypes) override;
hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
// Returns a bitmask which contains HdrMetadata::Type::*.
int32_t getSupportedPerFrameMetadata() const override;
@@ -174,7 +177,7 @@
hal::Error getName(std::string* outName) const override;
hal::Error getRequests(
hal::DisplayRequest* outDisplayRequests,
- std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) override;
+ std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override;
hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
hal::Error supportsDoze(bool* outSupport) const override;
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
@@ -185,8 +188,8 @@
uint64_t maxFrames) const override;
hal::Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
android::DisplayedFrameStats* outStats) const override;
- hal::Error getReleaseFences(
- std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
+ hal::Error getReleaseFences(std::unordered_map<HWC2::Layer*, android::sp<android::Fence>>*
+ outFences) const override;
hal::Error present(android::sp<android::Fence>* outPresentFence) override;
hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence,
@@ -218,13 +221,14 @@
const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
return mDisplayCapabilities;
};
- virtual bool isVsyncPeriodSwitchSupported() const override;
+ bool isVsyncPeriodSwitchSupported() const override;
+ void onLayerDestroyed(hal::HWLayerId layerId) override;
private:
// This may fail (and return a null pointer) if no layer with this ID exists
// on this display
- Layer* getLayerById(hal::HWLayerId) const;
+ std::shared_ptr<HWC2::Layer> getLayerById(hal::HWLayerId id) const;
friend android::TestableSurfaceFlinger;
@@ -240,7 +244,8 @@
hal::DisplayType mType;
bool mIsConnected = false;
- std::unordered_map<hal::HWLayerId, std::unique_ptr<Layer>> mLayers;
+ using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>;
+ Layers mLayers;
std::once_flag mDisplayCapabilityQueryFlag;
std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
@@ -295,10 +300,12 @@
class Layer : public HWC2::Layer {
public:
Layer(android::Hwc2::Composer& composer,
- const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId displayId,
+ const std::unordered_set<hal::Capability>& capabilities, HWC2::Display& display,
hal::HWLayerId layerId);
~Layer() override;
+ void onOwningDisplayDestroyed();
+
hal::HWLayerId getId() const override { return mId; }
hal::Error setCursorPosition(int32_t x, int32_t y) override;
@@ -334,7 +341,7 @@
android::Hwc2::Composer& mComposer;
const std::unordered_set<hal::Capability>& mCapabilities;
- hal::HWDisplayId mDisplayId;
+ HWC2::Display* mDisplay;
hal::HWLayerId mId;
// Cached HWC2 data, to ensure the same commands aren't sent to the HWC
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b73d032..36876dc 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -305,20 +305,15 @@
return value;
}
-HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
+std::shared_ptr<HWC2::Layer> HWComposer::createLayer(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
- HWC2::Layer* layer;
- auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer);
- RETURN_IF_HWC_ERROR(error, displayId, nullptr);
- return layer;
-}
-
-void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) {
- RETURN_IF_INVALID_DISPLAY(displayId);
-
- auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
- RETURN_IF_HWC_ERROR(error, displayId);
+ auto expected = mDisplayData[displayId].hwcDisplay->createLayer();
+ if (!expected.has_value()) {
+ auto error = std::move(expected).error();
+ RETURN_IF_HWC_ERROR(error, displayId, nullptr);
+ }
+ return std::move(expected).value();
}
bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
@@ -471,6 +466,7 @@
status_t HWComposer::getDeviceCompositionChanges(
HalDisplayId displayId, bool frameUsesClientComposition,
+ std::chrono::steady_clock::time_point earliestPresentTime,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -487,12 +483,14 @@
hal::Error error = hal::Error::NONE;
- // First try to skip validate altogether when there is no client
- // composition. When there is client composition, since we haven't
- // rendered to the client target yet, we should not attempt to skip
- // validate.
+ // First try to skip validate altogether when we passed the earliest time
+ // to present and there is no client. Otherwise, we may present a frame too
+ // early or in case of client composition we first need to render the
+ // client target buffer.
+ const bool canSkipValidate =
+ std::chrono::steady_clock::now() >= earliestPresentTime && !frameUsesClientComposition;
displayData.validateWasSkipped = false;
- if (!frameUsesClientComposition) {
+ if (canSkipValidate) {
sp<Fence> outPresentFence;
uint32_t state = UINT32_MAX;
error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
@@ -556,7 +554,8 @@
return fence->second;
}
-status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(
+ HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime) {
ATRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -572,6 +571,11 @@
return NO_ERROR;
}
+ {
+ ATRACE_NAME("wait for earliest present time");
+ std::this_thread::sleep_until(earliestPresentTime);
+ }
+
auto error = hwcDisplay->present(&displayData.lastPresentFence);
RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f532e50..d0c0c11 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -116,9 +116,7 @@
virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
// Attempts to create a new layer on this display
- virtual HWC2::Layer* createLayer(HalDisplayId) = 0;
- // Destroy a previously created layer
- virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0;
+ virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0;
// Gets any required composition change requests from the HWC device.
//
@@ -129,13 +127,15 @@
// expected.
virtual status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
+ std::chrono::steady_clock::time_point earliestPresentTime,
std::optional<DeviceRequestedChanges>* outChanges) = 0;
virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
// Present layers to the display and read releaseFences.
- virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
+ virtual status_t presentAndGetReleaseFences(
+ HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) = 0;
// set power mode
virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -262,19 +262,19 @@
void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
// Attempts to create a new layer on this display
- HWC2::Layer* createLayer(HalDisplayId) override;
- // Destroy a previously created layer
- void destroyLayer(HalDisplayId, HWC2::Layer*) override;
+ std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override;
status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
+ std::chrono::steady_clock::time_point earliestPresentTime,
std::optional<DeviceRequestedChanges>* outChanges) override;
status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) override;
// Present layers to the display and read releaseFences.
- status_t presentAndGetReleaseFences(HalDisplayId) override;
+ status_t presentAndGetReleaseFences(
+ HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) override;
// set power mode
status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4fee723..8b7dc4d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -221,7 +221,10 @@
}
void Layer::removeFromCurrentState() {
- mRemovedFromCurrentState = true;
+ if (!mRemovedFromCurrentState) {
+ mRemovedFromCurrentState = true;
+ mFlinger->mScheduler->deregisterLayer(this);
+ }
mFlinger->markLayerPendingRemovalLocked(this);
}
@@ -246,7 +249,10 @@
}
void Layer::addToCurrentState() {
- mRemovedFromCurrentState = false;
+ if (mRemovedFromCurrentState) {
+ mRemovedFromCurrentState = false;
+ mFlinger->mScheduler->registerLayer(this);
+ }
for (const auto& child : mCurrentChildren) {
child->addToCurrentState();
@@ -622,23 +628,6 @@
return {};
}
- float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
- const FloatRect& cornerRadiusCropRect = shadowLayer.geometry.roundedCornersCrop;
- const FloatRect& casterRect = shadowLayer.geometry.boundaries;
-
- // crop used to set the corner radius may be larger than the content rect. Adjust the corner
- // radius accordingly.
- if (casterCornerRadius > 0.f) {
- float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
- std::abs(cornerRadiusCropRect.left - casterRect.left));
- if (cropRectOffset > casterCornerRadius) {
- casterCornerRadius = 0;
- } else {
- casterCornerRadius -= cropRectOffset;
- }
- shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
- }
-
return shadowLayer;
}
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 00090d9..653aca6 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -56,16 +56,14 @@
noWorkNeeded,
idleTimerWaiting,
waitForQuietFrame,
- waitForZeroPhase,
waitForSamplePhase,
sample
};
-constexpr auto timeForRegionSampling = 5000000ns;
-constexpr auto maxRegionSamplingSkips = 10;
constexpr auto defaultRegionSamplingWorkDuration = 3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+constexpr auto maxRegionSamplingDelay = 100ms;
// TODO: (b/127403193) duration to string conversion could probably be constexpr
template <typename Rep, typename Per>
inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
@@ -99,97 +97,22 @@
}
}
-struct SamplingOffsetCallback : VSyncSource::Callback {
- SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
- std::chrono::nanoseconds targetSamplingWorkDuration)
- : mRegionSamplingThread(samplingThread),
- mTargetSamplingWorkDuration(targetSamplingWorkDuration),
- mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
- 0ns,
- /*traceVsync=*/false)) {
- mVSyncSource->setCallback(this);
- }
-
- ~SamplingOffsetCallback() { stopVsyncListener(); }
-
- SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
- SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
-
- void startVsyncListener() {
- std::lock_guard lock(mMutex);
- if (mVsyncListening) return;
-
- mPhaseIntervalSetting = Phase::ZERO;
- mVSyncSource->setVSyncEnabled(true);
- mVsyncListening = true;
- }
-
- void stopVsyncListener() {
- std::lock_guard lock(mMutex);
- stopVsyncListenerLocked();
- }
-
-private:
- void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
- if (!mVsyncListening) return;
-
- mVSyncSource->setVSyncEnabled(false);
- mVsyncListening = false;
- }
-
- void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
- nsecs_t /*deadlineTimestamp*/) final {
- std::unique_lock<decltype(mMutex)> lock(mMutex);
-
- if (mPhaseIntervalSetting == Phase::ZERO) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
- mPhaseIntervalSetting = Phase::SAMPLING;
- mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
- return;
- }
-
- if (mPhaseIntervalSetting == Phase::SAMPLING) {
- mPhaseIntervalSetting = Phase::ZERO;
- mVSyncSource->setDuration(0ns, 0ns);
- stopVsyncListenerLocked();
- lock.unlock();
- mRegionSamplingThread.notifySamplingOffset();
- return;
- }
- }
-
- RegionSamplingThread& mRegionSamplingThread;
- const std::chrono::nanoseconds mTargetSamplingWorkDuration;
- mutable std::mutex mMutex;
- enum class Phase {
- ZERO,
- SAMPLING
- } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
- = Phase::ZERO;
- bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
- std::unique_ptr<VSyncSource> mVSyncSource;
-};
-
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
- const TimingTunables& tunables)
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables)
: mFlinger(flinger),
- mScheduler(scheduler),
mTunables(tunables),
mIdleTimer(
"RegSampIdle",
std::chrono::duration_cast<std::chrono::milliseconds>(
mTunables.mSamplingTimerTimeout),
[] {}, [this] { checkForStaleLuma(); }),
- mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
- tunables.mSamplingDuration)),
- lastSampleTime(0ns) {
+ mLastSampleTime(0ns) {
mThread = std::thread([this]() { threadMain(); });
pthread_setname_np(mThread.native_handle(), "RegionSampling");
mIdleTimer.start();
}
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
- : RegionSamplingThread(flinger, scheduler,
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger)
+ : RegionSamplingThread(flinger,
TimingTunables{defaultRegionSamplingWorkDuration,
defaultRegionSamplingPeriod,
defaultRegionSamplingTimerTimeout}) {}
@@ -224,48 +147,46 @@
void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mThreadControlMutex);
- if (mDiscardedFrames > 0) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
- mDiscardedFrames = 0;
- mPhaseCallback->startVsyncListener();
+ if (mSampleRequestTime.has_value()) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ mSampleRequestTime.reset();
+ mFlinger.scheduleRegionSamplingThread();
}
}
-void RegionSamplingThread::notifyNewContent() {
- doSample();
+void RegionSamplingThread::onCompositionComplete(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
+ doSample(samplingDeadline);
}
-void RegionSamplingThread::notifySamplingOffset() {
- doSample();
-}
-
-void RegionSamplingThread::doSample() {
+void RegionSamplingThread::doSample(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
std::lock_guard lock(mThreadControlMutex);
- auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
- if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+ const auto now = std::chrono::steady_clock::now();
+ if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
+ // content changed, but we sampled not too long ago, so we need to sample some time in the
+ // future.
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
- if (mDiscardedFrames == 0) mDiscardedFrames++;
+ mSampleRequestTime = now;
return;
}
- if (mDiscardedFrames < maxRegionSamplingSkips) {
+ if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) {
// If there is relatively little time left for surfaceflinger
// until the next vsync deadline, defer this sampling work
// to a later frame, when hopefully there will be more time.
- const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
- if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+ if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
- mDiscardedFrames++;
+ mSampleRequestTime = mSampleRequestTime.value_or(now);
return;
}
}
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
- mDiscardedFrames = 0;
- lastSampleTime = now;
+ mSampleRequestTime.reset();
+ mLastSampleTime = now;
mIdleTimer.reset();
- mPhaseCallback->stopVsyncListener();
mSampleRequested = true;
mCondition.notify_one();
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 86632db..2231853 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -63,9 +63,8 @@
struct EnvironmentTimingTunables : TimingTunables {
EnvironmentTimingTunables();
};
- explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
- const TimingTunables& tunables);
- explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger);
~RegionSamplingThread();
@@ -76,12 +75,11 @@
// Remove the listener to stop receiving median luma notifications.
void removeListener(const sp<IRegionSamplingListener>& listener);
- // Notifies sampling engine that new content is available. This will trigger a sampling
- // pass at some point in the future.
- void notifyNewContent();
-
- // Notifies the sampling engine that it has a good timing window in which to sample.
- void notifySamplingOffset();
+ // Notifies sampling engine that composition is done and new content is
+ // available, and the deadline for the sampling work on the main thread to
+ // be completed without eating the budget of another frame.
+ void onCompositionComplete(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
private:
struct Descriptor {
@@ -99,7 +97,7 @@
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
- void doSample();
+ void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
void binderDied(const wp<IBinder>& who) override;
void checkForStaleLuma();
@@ -107,20 +105,18 @@
void threadMain();
SurfaceFlinger& mFlinger;
- Scheduler& mScheduler;
const TimingTunables mTunables;
scheduler::OneShotTimer mIdleTimer;
- std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
-
std::thread mThread;
std::mutex mThreadControlMutex;
std::condition_variable_any mCondition;
bool mRunning GUARDED_BY(mThreadControlMutex) = true;
bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
- uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
- std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
+ std::optional<std::chrono::steady_clock::time_point> mSampleRequestTime
+ GUARDED_BY(mThreadControlMutex);
+ std::chrono::steady_clock::time_point mLastSampleTime GUARDED_BY(mThreadControlMutex);
std::mutex mSamplingMutex;
std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index ce5c31a..50b38c9 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -60,8 +60,7 @@
mRegistration.schedule({.workDuration = mWorkDuration.count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = mLastCallTime.count()});
- LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
- "Error scheduling callback: rc %X", scheduleResult);
+ LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
}
void stop() {
@@ -100,8 +99,7 @@
mRegistration.schedule({.workDuration = mWorkDuration.count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = vsyncTime});
- LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
- "Error rescheduling callback: rc %X", scheduleResult);
+ LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
}
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index f4bc2a1..0563795 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,8 +84,11 @@
LayerHistory::~LayerHistory() = default;
void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
- auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
std::lock_guard lock(mLock);
+ for (const auto& info : mLayerInfos) {
+ LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
+ }
+ auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
mLayerInfos.emplace_back(layer, std::move(info));
}
@@ -94,7 +97,7 @@
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
[layer](const auto& pair) { return pair.first == layer; });
- LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+ LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
if (i < mActiveLayersEnd) {
@@ -111,7 +114,11 @@
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
[layer](const auto& pair) { return pair.first == layer; });
- LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+ if (it == mLayerInfos.end()) {
+ // Offscreen layer
+ ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
+ return;
+ }
const auto& info = it->second;
const auto layerProps = LayerInfo::LayerProps{
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index ba03c89..34cc389 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -272,7 +272,7 @@
// Used for sanitizing the heuristic data. If two frames are less than
// this period apart from each other they'll be considered as duplicates.
- static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
+ static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
// Used for sanitizing the heuristic data. If two frames are more than
// this period apart from each other, the interval between them won't be
// taken into account when calculating average frame rate.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 7ff0ddf..4d51125 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -23,7 +23,6 @@
#include <utils/threads.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include "EventThread.h"
#include "FrameTimeline.h"
@@ -33,27 +32,32 @@
namespace android::impl {
void MessageQueue::Handler::dispatchRefresh() {
- if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+ if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
}
}
void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
- if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
mVsyncId = vsyncId;
mExpectedVSyncTime = expectedVSyncTimestamp;
mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
}
}
+bool MessageQueue::Handler::invalidatePending() {
+ constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
+ return (mEventMask.load() & pendingMask) != 0;
+}
+
void MessageQueue::Handler::handleMessage(const Message& message) {
switch (message.what) {
case INVALIDATE:
- android_atomic_and(~eventMaskInvalidate, &mEventMask);
+ mEventMask.fetch_and(~eventMaskInvalidate);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
case REFRESH:
- android_atomic_and(~eventMaskRefresh, &mEventMask);
+ mEventMask.fetch_and(~eventMaskRefresh);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
}
@@ -106,7 +110,7 @@
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
- mVsync.mScheduled = false;
+ mVsync.scheduled = false;
}
mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime}),
@@ -131,9 +135,10 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
- if (mVsync.mScheduled) {
- mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
- mVsync.lastCallbackTime.count()});
+ if (mVsync.scheduled) {
+ mVsync.expectedWakeupTime = mVsync.registration->schedule(
+ {mVsync.workDuration.get().count(),
+ /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
}
}
@@ -176,10 +181,11 @@
}
std::lock_guard lock(mVsync.mutex);
- mVsync.mScheduled = true;
- mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
- .readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.count()});
+ mVsync.scheduled = true;
+ mVsync.expectedWakeupTime =
+ mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ .readyDuration = 0,
+ .earliestVsync = mVsync.lastCallbackTime.count()});
}
void MessageQueue::refresh() {
@@ -200,4 +206,19 @@
}
}
+std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
+ if (mHandler->invalidatePending()) {
+ return std::chrono::steady_clock::now();
+ }
+
+ std::lock_guard lock(mVsync.mutex);
+ if (mVsync.scheduled) {
+ LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
+ const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
+ return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+ }
+
+ return std::nullopt;
+}
+
} // namespace android::impl
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 2934af0..58ce9b9 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -72,6 +72,7 @@
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void invalidate() = 0;
virtual void refresh() = 0;
+ virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
};
// ---------------------------------------------------------------------------
@@ -81,9 +82,13 @@
class MessageQueue : public android::MessageQueue {
protected:
class Handler : public MessageHandler {
- enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
+ enum : uint32_t {
+ eventMaskInvalidate = 0x1,
+ eventMaskRefresh = 0x2,
+ eventMaskTransaction = 0x4
+ };
MessageQueue& mQueue;
- int32_t mEventMask;
+ std::atomic<uint32_t> mEventMask;
std::atomic<int64_t> mVsyncId;
std::atomic<nsecs_t> mExpectedVSyncTime;
@@ -92,6 +97,7 @@
void handleMessage(const Message& message) override;
virtual void dispatchRefresh();
virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+ virtual bool invalidatePending();
};
friend class Handler;
@@ -107,7 +113,8 @@
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
- bool mScheduled GUARDED_BY(mutex) = false;
+ bool scheduled GUARDED_BY(mutex) = false;
+ std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
@@ -141,6 +148,8 @@
// sends REFRESH message at next VSYNC
void refresh() override;
+
+ std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 9d71103..b52706f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,8 +16,10 @@
#pragma once
+#include <utils/Log.h>
#include <utils/Timers.h>
#include <functional>
+#include <optional>
#include <string>
#include "StrongTyping.h"
@@ -26,7 +28,8 @@
class TimeKeeper;
class VSyncTracker;
-enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+using ScheduleResult = std::optional<nsecs_t>;
+
enum class CancelResult { Cancelled, TooLate, Error };
/*
@@ -121,11 +124,8 @@
*
* \param [in] token The callback to schedule.
* \param [in] scheduleTiming The timing information for this schedule call
- * \return A ScheduleResult::Scheduled if callback was scheduled.
- * A ScheduleResult::CannotSchedule
- * if (workDuration + readyDuration - earliestVsync) is in the past,
- * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
- * there was another error.
+ * \return The expected callback time if a callback was scheduled.
+ * std::nullopt if the callback is not registered.
*/
virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ca6ea27..28be962 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -26,6 +26,20 @@
namespace android::scheduler {
using base::StringAppendF;
+namespace {
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+ const VSyncDispatch::ScheduleTiming& timing) {
+ return nextVsyncTime - timing.readyDuration - timing.workDuration;
+}
+
+nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
+ const VSyncDispatch::ScheduleTiming& timing) {
+ const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+ std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ return getExpectedCallbackTime(nextVsyncTime, timing);
+}
+} // namespace
+
VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
TimeKeeper::~TimeKeeper() = default;
@@ -74,7 +88,7 @@
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
if (wouldSkipAVsyncTarget) {
- return ScheduleResult::Scheduled;
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
bool const alreadyDispatchedForVsync = mLastDispatchTime &&
@@ -89,7 +103,7 @@
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
- return ScheduleResult::Scheduled;
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
@@ -317,7 +331,7 @@
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
- auto result = ScheduleResult::Error;
+ ScheduleResult result;
{
std::lock_guard lock(mMutex);
@@ -333,11 +347,11 @@
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
callback->addPendingWorkloadUpdate(scheduleTiming);
- return ScheduleResult::Scheduled;
+ return getExpectedCallbackTime(mTracker, now, scheduleTiming);
}
result = callback->schedule(scheduleTiming, mTracker, now);
- if (result == ScheduleResult::CannotSchedule) {
+ if (!result.has_value()) {
return result;
}
@@ -416,7 +430,7 @@
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
- return ScheduleResult::Error;
+ return std::nullopt;
}
return mDispatch.get().schedule(mToken, scheduleTiming);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4a007ff..a5b7107 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1720,11 +1720,11 @@
}
SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
- // We are storing the last 2 present fences. If sf's phase offset is to be
- // woken up before the actual vsync but targeting the next vsync, we need to check
- // fence N-2
- return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ const auto now = systemTime();
+ const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
+ const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
+ return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
}
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1920,6 +1920,7 @@
mRefreshPending = true;
onMessageRefresh();
}
+ notifyRegionSamplingThread();
}
bool SurfaceFlinger::handleMessageTransaction() {
@@ -2312,10 +2313,6 @@
}
}
- if (mLumaSampling && mRegionSamplingThread) {
- mRegionSamplingThread->notifyNewContent();
- }
-
// Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
if (ATRACE_ENABLED()) {
@@ -3068,8 +3065,7 @@
configs.late.sfWorkDuration);
mRegionSamplingThread =
- new RegionSamplingThread(*this, *mScheduler,
- RegionSamplingThread::EnvironmentTimingTunables());
+ new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
@@ -6450,13 +6446,17 @@
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
mNumLayers++;
- mScheduler->registerLayer(layer);
+ if (!layer->isRemovedFromCurrentState()) {
+ mScheduler->registerLayer(layer);
+ }
}
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
- mScheduler->deregisterLayer(layer);
mNumLayers--;
removeFromOffscreenLayers(layer);
+ if (!layer->isRemovedFromCurrentState()) {
+ mScheduler->deregisterLayer(layer);
+ }
}
// WARNING: ONLY CALL THIS FROM LAYER DTOR
@@ -6765,6 +6765,19 @@
return layer;
}
+
+void SurfaceFlinger::scheduleRegionSamplingThread() {
+ static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
+}
+
+void SurfaceFlinger::notifyRegionSamplingThread() {
+ if (!mLumaSampling || !mRegionSamplingThread) {
+ return;
+ }
+
+ mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index da874b6..cb8d312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1433,6 +1433,9 @@
REQUIRES(mStateLock);
std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
+
+ void scheduleRegionSamplingThread();
+ void notifyRegionSamplingThread();
};
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 10d58a6..d6a0787 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -351,12 +351,16 @@
std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
}
+static int32_t toMs(nsecs_t nanos) {
+ int64_t millis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
+ .count();
+ millis = std::clamp(millis, int64_t(INT32_MIN), int64_t(INT32_MAX));
+ return static_cast<int32_t>(millis);
+}
+
static int32_t msBetween(nsecs_t start, nsecs_t end) {
- int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::nanoseconds(end - start))
- .count();
- delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
- return static_cast<int32_t>(delta);
+ return toMs(end - start);
}
void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
@@ -829,10 +833,9 @@
// TimeStats Histograms only retain positive values, so we don't need to check if these
// deadlines were really missed if we know that the frame had jank, since deadlines
// that were met will be dropped.
- timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
- timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
- timeStatsLayer.deltas["appDeadlineDeltas"].insert(
- static_cast<int32_t>(info.appDeadlineDelta));
+ timelineStats.displayDeadlineDeltas.insert(toMs(info.displayDeadlineDelta));
+ timelineStats.displayPresentDeltas.insert(toMs(info.displayPresentJitter));
+ timeStatsLayer.deltas["appDeadlineDeltas"].insert(toMs(info.appDeadlineDelta));
}
}
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 2828d61..43d957c 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -273,6 +273,198 @@
}
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferRotationTransform) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> parent;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = LayerTransactionTest::createLayer("parent", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const uint32_t bufferWidth = 1500;
+ const uint32_t bufferHeight = 300;
+
+ const uint32_t layerWidth = 300;
+ const uint32_t layerHeight = 1500;
+
+ const uint32_t testArea = 4;
+ const float cornerRadius = 120.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, bufferWidth, bufferHeight));
+
+ Transaction()
+ .reparent(layer, parent)
+ .setColor(parent, half3(0, 1, 0))
+ .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+ .setCornerRadius(parent, cornerRadius)
+
+ .setTransform(layer, ui::Transform::ROT_90)
+ .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+ .apply();
+ {
+ auto shot = getScreenCapture();
+ // Corners are transparent
+ // top-left
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ // top-right
+ shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+ // bottom-left
+ shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+ // bottom-right
+ shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+ layerHeight),
+ Color::BLACK);
+
+ // Area after corner radius is solid
+ // top-left to top-right under the corner
+ shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED);
+ // bottom-left to bottom-right above the corner
+ shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth,
+ layerHeight - cornerRadius),
+ Color::RED);
+ // left side after the corner
+ shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED);
+ // right side before the corner
+ shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius,
+ layerHeight),
+ Color::RED);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferCropTransform) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> parent;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = LayerTransactionTest::createLayer("parent", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const uint32_t bufferWidth = 150 * 2;
+ const uint32_t bufferHeight = 750 * 2;
+
+ const Rect bufferCrop(0, 0, 150, 750);
+
+ const uint32_t layerWidth = 300;
+ const uint32_t layerHeight = 1500;
+
+ const uint32_t testArea = 4;
+ const float cornerRadius = 120.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerQuadrant(layer, bufferWidth, bufferHeight, Color::RED,
+ Color::BLACK, Color::GREEN, Color::BLUE));
+
+ Transaction()
+ .reparent(layer, parent)
+ .setColor(parent, half3(0, 1, 0))
+ .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+ .setCornerRadius(parent, cornerRadius)
+
+ .setBufferCrop(layer, bufferCrop)
+ .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+ .apply();
+ {
+ auto shot = getScreenCapture();
+ // Corners are transparent
+ // top-left
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ // top-right
+ shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+ // bottom-left
+ shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+ // bottom-right
+ shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+ layerHeight),
+ Color::BLACK);
+
+ // Area after corner radius is solid
+ // since the buffer is scaled, there will blending so adjust some of the bounds when
+ // checking.
+ float adjustedCornerRadius = cornerRadius + 15;
+ float adjustedLayerHeight = layerHeight - 15;
+ float adjustedLayerWidth = layerWidth - 15;
+
+ // top-left to top-right under the corner
+ shot->expectColor(Rect(15, adjustedCornerRadius, adjustedLayerWidth,
+ adjustedCornerRadius + testArea),
+ Color::RED);
+ // bottom-left to bottom-right above the corner
+ shot->expectColor(Rect(15, adjustedLayerHeight - adjustedCornerRadius - testArea,
+ adjustedLayerWidth, adjustedLayerHeight - adjustedCornerRadius),
+ Color::RED);
+ // left side after the corner
+ shot->expectColor(Rect(adjustedCornerRadius, 15, adjustedCornerRadius + testArea,
+ adjustedLayerHeight),
+ Color::RED);
+ // right side before the corner
+ shot->expectColor(Rect(adjustedLayerWidth - adjustedCornerRadius - testArea, 15,
+ adjustedLayerWidth - adjustedCornerRadius, adjustedLayerHeight),
+ Color::RED);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildBufferRotationTransform) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> parent;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = LayerTransactionTest::createLayer("parent", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const uint32_t bufferWidth = 1500;
+ const uint32_t bufferHeight = 300;
+
+ const uint32_t layerWidth = 300;
+ const uint32_t layerHeight = 1500;
+
+ const uint32_t testArea = 4;
+ const float cornerRadius = 120.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::BLUE, bufferWidth, bufferHeight));
+
+ sp<SurfaceControl> child;
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", bufferWidth, bufferHeight));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::RED, bufferWidth, bufferHeight));
+
+ Transaction()
+ .reparent(layer, parent)
+ .reparent(child, layer)
+ .setColor(parent, half3(0, 1, 0))
+ .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+ .setCornerRadius(parent, cornerRadius) /* */
+
+ .setTransform(layer, ui::Transform::ROT_90)
+ .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+
+ .setTransform(child, ui::Transform::ROT_90)
+ .setDestinationFrame(child, Rect(0, 0, layerWidth, layerHeight))
+ .apply();
+ {
+ auto shot = getScreenCapture();
+ // Corners are transparent
+ // top-left
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ // top-right
+ shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+ // bottom-left
+ shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+ // bottom-right
+ shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+ layerHeight),
+ Color::BLACK);
+
+ // Area after corner radius is solid
+ // top-left to top-right under the corner
+ shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED);
+ // bottom-left to bottom-right above the corner
+ shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth,
+ layerHeight - cornerRadius),
+ Color::RED);
+ // left side after the corner
+ shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED);
+ // right side before the corner
+ shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius,
+ layerHeight),
+ Color::RED);
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
if (!deviceSupportsBlurs()) GTEST_SKIP();
if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 88fb811..b33434e 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -94,6 +94,7 @@
"VSyncReactorTest.cpp",
"VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
+ "mock/DisplayHardware/MockHWC2.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 54f4c7c..a9ad249 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -66,7 +66,7 @@
ALOGD("schedule: %zu", token.value());
if (mCallbacks.count(token) == 0) {
ALOGD("schedule: callback %zu not registered", token.value());
- return scheduler::ScheduleResult::Error;
+ return scheduler::ScheduleResult{};
}
auto& callback = mCallbacks.at(token);
@@ -75,7 +75,7 @@
callback.targetWakeupTime =
timing.earliestVsync - timing.workDuration - timing.readyDuration;
ALOGD("schedule: callback %zu scheduled", token.value());
- return scheduler::ScheduleResult::Scheduled;
+ return scheduler::ScheduleResult{callback.targetWakeupTime};
});
ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 6b82170..cbf8cc2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -37,6 +37,7 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockHWC2.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
@@ -120,13 +121,19 @@
static constexpr hal::HWLayerId kLayerId = static_cast<hal::HWLayerId>(1002);
HWComposerLayerTest(const std::unordered_set<hal::Capability>& capabilities)
- : mCapabilies(capabilities) {}
+ : mCapabilies(capabilities) {
+ EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(kDisplayId));
+ }
- ~HWComposerLayerTest() override { EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); }
+ ~HWComposerLayerTest() override {
+ EXPECT_CALL(mDisplay, onLayerDestroyed(kLayerId));
+ EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId));
+ }
std::unique_ptr<Hwc2::mock::Composer> mHal{new StrictMock<Hwc2::mock::Composer>()};
const std::unordered_set<hal::Capability> mCapabilies;
- HWC2::impl::Layer mLayer{*mHal, mCapabilies, kDisplayId, kLayerId};
+ StrictMock<HWC2::mock::Display> mDisplay;
+ HWC2::impl::Layer mLayer{*mHal, mCapabilies, mDisplay, kLayerId};
};
struct HWComposerLayerGenericMetadataTest : public HWComposerLayerTest {
@@ -176,4 +183,4 @@
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 325fb8f..d6ce5e2 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -126,7 +126,7 @@
std::deque<FrameTimeData> frameTimes;
constexpr auto kExpectedFps = Fps(50.0f);
constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
- constexpr auto kSmallPeriod = Fps(150.0f).getPeriodNsecs();
+ constexpr auto kSmallPeriod = Fps(250.0f).getPeriodNsecs();
constexpr int kNumIterations = 10;
for (int i = 1; i <= kNumIterations; i++) {
frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 8208b3f..dbd51fe 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -104,8 +104,12 @@
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
}
TEST_F(MessageQueueTest, invalidateTwice) {
@@ -114,11 +118,15 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
}
TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
@@ -127,8 +135,10 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
const auto startTime = 100;
const auto endTime = startTime + mDuration.count();
@@ -141,12 +151,14 @@
EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+ EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = presentTime};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
}
@@ -158,7 +170,7 @@
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 0b70f27..7ace70a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1450,12 +1450,12 @@
.seamlessness = Seamlessness::OnlySeamless,
.weight = 1.0f,
.focused = true}};
- auto& seamedLayer = layers[0];
ASSERT_EQ(HWC_CONFIG_ID_50,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+ auto& seamedLayer = layers[0];
seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f);
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
@@ -1464,6 +1464,27 @@
.getModeId());
}
+TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.name = "Min",
+ .vote = LayerVoteType::Min,
+ .weight = 1.f,
+ .focused = true}};
+
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_90Device,
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index ff53a7b..3e4e130 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1011,6 +1011,9 @@
constexpr size_t MISSED_FRAMES = 4;
constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
+ constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+ constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+ constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -1036,24 +1039,35 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerCpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, 1, 2, 3});
+ JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, 1, 2, 3});
+ JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, 1, 2, 3});
+ JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, 1, 2, 3});
+ JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
- 3});
+ JankType::AppDeadlineMissed | JankType::BufferStuffing,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::BufferStuffing, 1, 2, 3});
+ JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, 1, 2, 3});
+ JankType::None, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
@@ -1137,6 +1151,10 @@
TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
constexpr size_t LATE_ACQUIRE_FRAMES = 2;
constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+ constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+ constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+ constexpr nsecs_t APP_DEADLINE_DELTA_2MS = 2'000'000;
+ constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
@@ -1155,22 +1173,32 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerCpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+ JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::DisplayHAL, 1, 2, 3});
+ JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed, 1, 2, 3});
+ JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerScheduling, 1, 2, 2});
+ JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::PredictionError, 1, 2, 2});
+ JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
- 2});
+ JankType::AppDeadlineMissed | JankType::BufferStuffing,
+ DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
+ APP_DEADLINE_DELTA_2MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- JankType::None, 1, 2, 3});
+ JankType::None, DISPLAY_DEADLINE_DELTA,
+ DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b64cce9..d59d64b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -232,11 +232,12 @@
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
mVsyncMoveThreshold};
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
}
}
@@ -245,11 +246,13 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = intended});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
+
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -277,11 +280,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = workDuration,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = workDuration,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -289,11 +293,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
}
@@ -302,11 +306,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
mMockClock.advanceBy(950);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
}
@@ -316,11 +320,11 @@
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -337,11 +341,11 @@
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod}),
- ScheduleResult::Scheduled);
+ const auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod - 100, *result);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -535,21 +539,25 @@
std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
[&](auto timestamp, auto, auto) {
- EXPECT_EQ(mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp - mVsyncMoveThreshold}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp + mVsyncMoveThreshold}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp - mVsyncMoveThreshold});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ result = mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
+ result = mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp + mVsyncMoveThreshold});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(mPeriod + timestamp - 400, *result);
lastTarget = timestamp;
},
"oo");
@@ -627,36 +635,41 @@
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
- EXPECT_THAT(mDispatch.schedule(token,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000}),
- Eq(ScheduleResult::Error));
+ EXPECT_FALSE(mDispatch
+ .schedule(token,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+ .has_value());
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
}
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
+ result = mDispatch.schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
}
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1200, *result);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
@@ -667,24 +680,30 @@
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(602, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb0,
+ {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -692,26 +711,32 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0,
- {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb0,
+ {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1100, *result);
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
advanceToNextCallback();
}
@@ -754,16 +779,19 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1900, *result);
mMockClock.advanceBy(80);
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -779,16 +807,19 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ result = mDispatch.schedule(cb,
+ {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1630, *result);
mMockClock.advanceBy(80);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -802,12 +833,15 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1900, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -828,12 +862,15 @@
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1900, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -861,12 +898,15 @@
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_EQ(mDispatch.schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2,
- {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
- ScheduleResult::Scheduled);
+ auto result =
+ mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+ result = mDispatch.schedule(cb2,
+ {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(610, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(700);
@@ -886,11 +926,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb,
- {.workDuration = 70,
- .readyDuration = 30,
- .earliestVsync = intended}),
- ScheduleResult::Scheduled);
+ const auto result = mDispatch.schedule(cb,
+ {.workDuration = 70,
+ .readyDuration = 30,
+ .earliestVsync = intended});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(900, *result);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -922,9 +963,9 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -944,9 +985,9 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
- mStubTracker, now),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+ mStubTracker, now)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(9500));
@@ -967,9 +1008,9 @@
},
mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -1002,9 +1043,9 @@
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
@@ -1018,9 +1059,9 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
entry.update(mStubTracker, 0);
auto const wakeup = entry.wakeupTime();
@@ -1031,26 +1072,26 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+ mStubTracker, 0)
+ .has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
}
@@ -1071,32 +1112,32 @@
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
entry.executing(); // 1000 is executing
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
+ EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
@@ -1128,9 +1169,9 @@
},
mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
- mStubTracker, 0),
- Eq(ScheduleResult::Scheduled));
+ EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+ mStubTracker, 0)
+ .has_value());
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp
new file mode 100644
index 0000000..2647bf4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "MockHWC2"
+
+#include "mock/DisplayHardware/MockHWC2.h"
+
+namespace android::HWC2::mock {
+
+// Explicit default instantiation is recommended.
+Display::Display() = default;
+Display::~Display() = default;
+
+Layer::Layer() = default;
+Layer::~Layer() = default;
+
+} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
new file mode 100644
index 0000000..c3919d9
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/HWC2.h"
+
+namespace android::HWC2::mock {
+
+class Display : public HWC2::Display {
+public:
+ Display();
+ ~Display() override;
+
+ MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
+ MOCK_METHOD(bool, isConnected, (), (const, override));
+ MOCK_METHOD(void, setConnected, (bool), (override));
+ MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (),
+ (const, override));
+ MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
+ MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));
+
+ MOCK_METHOD(hal::Error, acceptChanges, (), (override));
+ MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (),
+ (override));
+ MOCK_METHOD(hal::Error, getChangedCompositionTypes,
+ ((std::unordered_map<Layer *, hal::Composition> *)), (override));
+ MOCK_METHOD(hal::Error, getColorModes, (std::vector<hal::ColorMode> *), (const, override));
+ MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (), (const, override));
+ MOCK_METHOD(hal::Error, getRenderIntents, (hal::ColorMode, std::vector<hal::RenderIntent> *),
+ (const, override));
+ MOCK_METHOD(hal::Error, getDataspaceSaturationMatrix, (hal::Dataspace, android::mat4 *),
+ (override));
+ MOCK_METHOD(hal::Error, getName, (std::string *), (const, override));
+ MOCK_METHOD(hal::Error, getRequests,
+ (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)),
+ (override));
+ MOCK_METHOD(hal::Error, getConnectionType, (ui::DisplayConnectionType *), (const, override));
+ MOCK_METHOD(hal::Error, supportsDoze, (bool *), (const, override));
+ MOCK_METHOD(hal::Error, getHdrCapabilities, (android::HdrCapabilities *), (const, override));
+ MOCK_METHOD(hal::Error, getDisplayedContentSamplingAttributes,
+ (hal::PixelFormat *, hal::Dataspace *, uint8_t *), (const, override));
+ MOCK_METHOD(hal::Error, setDisplayContentSamplingEnabled, (bool, uint8_t, uint64_t),
+ (const, override));
+ MOCK_METHOD(hal::Error, getDisplayedContentSample,
+ (uint64_t, uint64_t, android::DisplayedFrameStats *), (const, override));
+ MOCK_METHOD(hal::Error, getReleaseFences,
+ ((std::unordered_map<Layer *, android::sp<android::Fence>> *)), (const, override));
+ MOCK_METHOD(hal::Error, present, (android::sp<android::Fence> *), (override));
+ MOCK_METHOD(hal::Error, setClientTarget,
+ (uint32_t, const android::sp<android::GraphicBuffer> &,
+ const android::sp<android::Fence> &, hal::Dataspace),
+ (override));
+ MOCK_METHOD(hal::Error, setColorMode, (hal::ColorMode, hal::RenderIntent), (override));
+ MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &, hal::ColorTransform),
+ (override));
+ MOCK_METHOD(hal::Error, setOutputBuffer,
+ (const android::sp<android::GraphicBuffer> &, const android::sp<android::Fence> &),
+ (override));
+ MOCK_METHOD(hal::Error, setPowerMode, (hal::PowerMode), (override));
+ MOCK_METHOD(hal::Error, setVsyncEnabled, (hal::Vsync), (override));
+ MOCK_METHOD(hal::Error, validate, (uint32_t *, uint32_t *), (override));
+ MOCK_METHOD(hal::Error, presentOrValidate,
+ (uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *), (override));
+ MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness, (float), (override));
+ MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
+ (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
+ hal::VsyncPeriodChangeTimeline *),
+ (override));
+ MOCK_METHOD(hal::Error, setAutoLowLatencyMode, (bool), (override));
+ MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
+ (const, override));
+ MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override));
+ MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *), (override));
+};
+
+class Layer : public HWC2::Layer {
+public:
+ Layer();
+ ~Layer() override;
+
+ MOCK_METHOD(hal::HWLayerId, getId, (), (const, override));
+ MOCK_METHOD(hal::Error, setCursorPosition, (int32_t, int32_t), (override));
+ MOCK_METHOD(hal::Error, setBuffer,
+ (uint32_t, const android::sp<android::GraphicBuffer> &,
+ const android::sp<android::Fence> &),
+ (override));
+ MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
+ MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
+ MOCK_METHOD(hal::Error, setColor, (hal::Color), (override));
+ MOCK_METHOD(hal::Error, setCompositionType, (hal::Composition), (override));
+ MOCK_METHOD(hal::Error, setDataspace, (android::ui::Dataspace), (override));
+ MOCK_METHOD(hal::Error, setPerFrameMetadata, (const int32_t, const android::HdrMetadata &),
+ (override));
+ MOCK_METHOD(hal::Error, setDisplayFrame, (const android::Rect &), (override));
+ MOCK_METHOD(hal::Error, setPlaneAlpha, (float), (override));
+ MOCK_METHOD(hal::Error, setSidebandStream, (const native_handle_t *), (override));
+ MOCK_METHOD(hal::Error, setSourceCrop, (const android::FloatRect &), (override));
+ MOCK_METHOD(hal::Error, setTransform, (hal::Transform), (override));
+ MOCK_METHOD(hal::Error, setVisibleRegion, (const android::Region &), (override));
+ MOCK_METHOD(hal::Error, setZOrder, (uint32_t), (override));
+ MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
+ MOCK_METHOD(hal::Error, setLayerGenericMetadata,
+ (const std::string &, bool, const std::vector<uint8_t> &), (override));
+};
+
+} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 453c93a..0e7b320 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -39,6 +39,7 @@
void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds));
MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
+ MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
};
} // namespace android::mock