Merge "Expand setBrightness to take BrightnessInfo object" into sc-dev
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c62d302..0712c0a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -411,7 +411,6 @@
}
TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
- SectionExists("HIGH connectivity", /* bytes= */ 3000);
SectionExists("connectivity", /* bytes= */ 5000);
}
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 3c53c02..bbbe6f7 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -495,7 +495,7 @@
Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
cc.rectangle().bottom());
- t.setCrop_legacy(mLayers[id], r);
+ t.setCrop(mLayers[id], r);
}
void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index f1b2258..3e5674e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -33,6 +33,7 @@
#include <unordered_map>
#include <android-base/chrono_utils.h>
+#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
@@ -374,20 +375,24 @@
*/
status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
+ struct Finished {
+ uint32_t seq;
+ bool handled;
+ nsecs_t consumeTime;
+ };
+
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
- * If a signal was received, returns the message sequence number,
- * whether the consumer handled the message, and the time the event was first read by the
- * consumer.
+ * If a signal was received, returns a Finished object.
*
* The returned sequence number is never 0 unless the operation failed.
*
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no signal present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Other errors probably indicate that the channel is broken.
+ * Returned error codes:
+ * OK on success.
+ * WOULD_BLOCK if there is no signal present.
+ * DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
*/
- status_t receiveFinishedSignal(
- const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback);
+ android::base::Result<Finished> receiveFinishedSignal();
private:
std::shared_ptr<InputChannel> mChannel;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6df04f0..a17e482 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -214,6 +214,7 @@
"-misc-redundant-expression",
"-misc-unused-using-decls",
"performance*",
+ "-performance-no-int-to-ptr",
"portability*",
],
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index f1085cf..0386d66 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -138,7 +138,11 @@
OP_RECORD_AUDIO_HOTWORD = 102,
// Ops 103-105 are currently unused in native, and intentionally omitted
OP_RECORD_AUDIO_OUTPUT = 106,
- _NUM_OP = 107
+ OP_SCHEDULE_EXACT_ALARM = 107,
+ OP_FINE_LOCATION_SOURCE = 108,
+ OP_COARSE_LOCATION_SOURCE = 109,
+ OP_MANAGE_MEDIA = 110,
+ _NUM_OP = 111
};
AppOpsManager();
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 288bf92..6bb8bf2 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,7 +41,6 @@
flags(0),
mask(0),
reserved(0),
- crop_legacy(Rect::INVALID_RECT),
cornerRadius(0.0f),
backgroundBlurRadius(0),
barrierFrameNumber(0),
@@ -85,7 +84,7 @@
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
SAFE_PARCEL(matrix.write, output);
- SAFE_PARCEL(output.write, crop_legacy);
+ SAFE_PARCEL(output.write, crop);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
@@ -191,7 +190,7 @@
SAFE_PARCEL(input.readUint32, &mask);
SAFE_PARCEL(matrix.read, input);
- SAFE_PARCEL(input.read, crop_legacy);
+ SAFE_PARCEL(input.read, crop);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
@@ -407,10 +406,6 @@
what |= eLayerStackChanged;
layerStack = other.layerStack;
}
- if (other.what & eCropChanged_legacy) {
- what |= eCropChanged_legacy;
- crop_legacy = other.crop_legacy;
- }
if (other.what & eCornerRadiusChanged) {
what |= eCornerRadiusChanged;
cornerRadius = other.cornerRadius;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 6639440..c16a98f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1048,15 +1048,15 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy(
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
const sp<SurfaceControl>& sc, const Rect& crop) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eCropChanged_legacy;
- s->crop_legacy = crop;
+ s->what |= layer_state_t::eCropChanged;
+ s->crop = crop;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1204,20 +1204,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
- const sp<SurfaceControl>& sc, const Rect& crop) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eCropChanged;
- s->crop = crop;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
const sp<SurfaceControl>& sc, const Rect& frame) {
layer_state_t* s = getLayerState(sc);
@@ -1458,7 +1444,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
- setCrop_legacy(sc, source);
+ setCrop(sc, source);
int x = dst.left;
int y = dst.top;
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index b273805..183ec40 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -98,7 +98,7 @@
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
- eCropChanged_legacy = 0x00000100,
+ /* was eCropChanged_legacy, now available 0x00000100, */
eDeferTransaction_legacy = 0x00000200,
/* was ScalingModeChanged, now available 0x00000400, */
eShadowRadiusChanged = 0x00000800,
@@ -167,7 +167,6 @@
uint32_t mask;
uint8_t reserved;
matrix22_t matrix;
- Rect crop_legacy;
float cornerRadius;
uint32_t backgroundBlurRadius;
sp<SurfaceControl> barrierSurfaceControl_legacy;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index df37780..88484c0 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -437,7 +437,7 @@
float alpha);
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
- Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
@@ -470,7 +470,6 @@
Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
- Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fb07a19..7895e99 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -458,7 +458,7 @@
ASSERT_NE(nullptr, bg.get());
Transaction t;
t.setLayerStack(bg, 0)
- .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
.apply();
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index d65a40b..59e5c13 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -195,7 +195,7 @@
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
t.setLayer(mSurfaceControl, LAYER_BASE);
t.setPosition(mSurfaceControl, x, y);
- t.setCrop_legacy(mSurfaceControl, crop);
+ t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
t.apply(true);
}
@@ -670,7 +670,7 @@
parentSurface->showAt(100, 100);
nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
- t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
t.reparent(sc, parentSurface->mSurfaceControl);
});
@@ -694,7 +694,7 @@
parentSurface->showAt(50, 50);
nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
- t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
t.reparent(sc, parentSurface->mSurfaceControl);
});
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 5c1bebb..0cd150d 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -46,8 +46,7 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButton, 0x7fffffff)
- .setCrop_legacy(mButton,
- {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+ .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
.setPosition(mButton, samplingArea.left + BUTTON_PADDING,
samplingArea.top + BUTTON_PADDING)
.setColor(mButton, half3{1, 1, 1})
@@ -59,9 +58,8 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButtonBlend, 0x7ffffffe)
- .setCrop_legacy(mButtonBlend,
- {0, 0, width - 2 * SAMPLE_AREA_PADDING,
- height - 2 * SAMPLE_AREA_PADDING})
+ .setCrop(mButtonBlend,
+ {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING})
.setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
samplingArea.top + SAMPLE_AREA_PADDING)
.setColor(mButtonBlend, half3{1, 1, 1})
@@ -77,7 +75,7 @@
SurfaceComposerClient::Transaction{}
.setLayer(mSamplingArea, 0x7ffffffd)
- .setCrop_legacy(mSamplingArea, {0, 0, 100, 32})
+ .setCrop(mSamplingArea, {0, 0, 100, 32})
.setPosition(mSamplingArea, 490, 1606)
.setColor(mSamplingArea, half3{0, 1, 0})
.setAlpha(mSamplingArea, 0.1)
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 6ef0173..c2a3cf1 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -629,24 +629,26 @@
return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::receiveFinishedSignal(
- const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) {
+android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+ ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
}
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
- return result;
+ return android::base::Error(result);
}
if (msg.header.type != InputMessage::Type::FINISHED) {
ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
- return UNKNOWN_ERROR;
+ return android::base::Error(UNKNOWN_ERROR);
}
- callback(msg.header.seq, msg.body.finished.handled, msg.body.finished.consumeTime);
- return OK;
+ return Finished{
+ .seq = msg.header.seq,
+ .handled = msg.body.finished.handled,
+ .consumeTime = msg.body.finished.consumeTime,
+ };
}
// --- InputConsumer ---
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
index 0dbb4cf..6de030f 100644
--- a/libs/input/tests/Flags_test.cpp
+++ b/libs/input/tests/Flags_test.cpp
@@ -148,6 +148,11 @@
ASSERT_NE(flags1, flags2);
}
+TEST(Flags, GetValue) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags.get(), 0x3);
+}
+
TEST(Flags, String_NoFlags) {
Flags<TestFlags> flags;
ASSERT_EQ(flags.string(), "0x0");
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index b5ed8d7..fc31715 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -27,6 +27,8 @@
#include <utils/StopWatch.h>
#include <utils/Timers.h>
+using android::base::Result;
+
namespace android {
class InputPublisherAndConsumerTest : public testing::Test {
@@ -122,23 +124,13 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- ASSERT_EQ(OK, status)
- << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(consumeTime, publishTime)
+ Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -272,23 +264,13 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = true;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- ASSERT_EQ(OK, status)
- << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_FALSE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(consumeTime, publishTime)
+ Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_FALSE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -322,22 +304,14 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(consumeTime, publishTime)
+ Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+
+ ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -369,22 +343,13 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(consumeTime, publishTime)
+ android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -410,32 +375,23 @@
ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
<< "consumer should have returned a drag event";
- DragEvent* dragEvent = static_cast<DragEvent*>(event);
+ const DragEvent& dragEvent = static_cast<const DragEvent&>(*event);
EXPECT_EQ(seq, consumeSeq);
- EXPECT_EQ(eventId, dragEvent->getId());
- EXPECT_EQ(isExiting, dragEvent->isExiting());
- EXPECT_EQ(x, dragEvent->getX());
- EXPECT_EQ(y, dragEvent->getY());
+ EXPECT_EQ(eventId, dragEvent.getId());
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
+ android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
<< "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
+ ASSERT_TRUE(result->handled)
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(consumeTime, publishTime)
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb3b434..026b19a 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -84,6 +84,7 @@
name: "librenderengine_skia_sources",
srcs: [
"skia/AutoBackendTexture.cpp",
+ "skia/Cache.cpp",
"skia/ColorSpaces.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2b09c15..a2963a7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,7 +515,7 @@
return mDrawingBuffer.get();
}
-void GLESRenderEngine::primeCache() const {
+void GLESRenderEngine::primeCache() {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
mUseColorManagement, mPrecacheToneMapperShaderOnly);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 7496b74..06a1722 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() const override;
+ void primeCache() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ddae34a..7c51f1b 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -97,7 +97,7 @@
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual void primeCache() const = 0;
+ virtual void primeCache() = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 895ba3f..5f75b81 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_CONST_METHOD0(primeCache, void());
+ MOCK_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
new file mode 100644
index 0000000..4fdae74
--- /dev/null
+++ b/libs/renderengine/skia/Cache.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Cache.h"
+#include "AutoBackendTexture.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/unique_fd.h"
+#include "renderengine/DisplaySettings.h"
+#include "renderengine/LayerSettings.h"
+#include "ui/GraphicBuffer.h"
+#include "ui/GraphicTypes.h"
+#include "ui/PixelFormat.h"
+#include "ui/Rect.h"
+#include "utils/Timers.h"
+
+namespace android::renderengine::skia {
+
+static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer) {
+ // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
+ // on actual use.
+ FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ },
+ .shadow =
+ ShadowSettings{
+ .ambientColor = vec4(0, 0, 0, 0.00935997f),
+ .spotColor = vec4(0, 0, 0, 0.0455841f),
+ .lightPos = vec3(370.508f, -1527.03f, 1650.f),
+ .lightRadius = 2200.0f,
+ .length = 0.955342f,
+ },
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ // The identity matrix will generate the fast shaders, and the second matrix
+ // (based on one seen while going from dialer to the home screen) will
+ // generate the slower (more general case) version. If we also need a
+ // slow version without color correction, we should use this matrix with
+ // display.outputDataspace set to SRGB.
+ for (const mat4 transform : { mat4(), mat4(0.728872f, 0.f, 0.f, 0.f,
+ 0.f, 0.727627f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 167.355743f, 1852.257812f, 0.f, 1.f) }) {
+ layer.geometry.positionTransform = transform;
+ renderengine->drawLayers(display, layers, dstBuffer, false /* useFrameBufferCache*/,
+ base::unique_fd(), nullptr);
+ }
+}
+
+static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ // This matrix is based on actual data seen when opening the dialer.
+ // What seems to be important in matching the actual use cases are:
+ // - it is not identity
+ // - the layer will be drawn (not clipped out etc)
+ .positionTransform = mat4(.19f, .0f, .0f, .0f,
+ .0f, .19f, .0f, .0f,
+ .0f, .0f, 1.f, .0f,
+ 169.f, 1527.f, .0f, 1.f),
+ .roundedCornersCrop = rect,
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ .maxMasteringLuminance = 1000.f,
+ .maxContentLuminance = 1000.f,
+ }},
+ };
+
+ // Test both drawRect and drawRRect
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (float roundedCornersRadius : {0.0f, 500.f}) {
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ // No need to check UNKNOWN, which is treated as SRGB.
+ for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
+ layer.sourceDataspace = dataspace;
+ for (bool isOpaque : {true, false}) {
+ layer.source.buffer.isOpaque = isOpaque;
+ for (auto alpha : {half(.23999f), half(1.0f)}) {
+ layer.alpha = alpha;
+ renderengine->drawLayers(display, layers, dstBuffer,
+ false /* useFrameBufferCache*/, base::unique_fd(),
+ nullptr);
+ }
+ }
+ }
+ }
+}
+
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+ const nsecs_t timeBefore = systemTime();
+ // The dimensions should not matter, so long as we draw inside them.
+ const Rect displayRect(0, 0, 1080, 2340);
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ };
+
+ const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+ sp<GraphicBuffer> dstBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usage, "primeShaderCache_dst");
+ // This buffer will be the source for the call to drawImageLayers. Draw
+ // something to it as a placeholder for what an app draws. We should draw
+ // something, but the details are not important. We only need one version of
+ // the shadow's SkSL, so draw it here, giving us both a placeholder image
+ // and a chance to compile the shadow's SkSL.
+ sp<GraphicBuffer> srcBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usage, "drawImageLayer_src");
+ drawShadowLayer(renderengine, display, srcBuffer);
+
+ drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
new file mode 100644
index 0000000..437571e
--- /dev/null
+++ b/libs/renderengine/skia/Cache.h
@@ -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.
+ */
+
+#pragma once
+
+namespace android::renderengine::skia {
+
+class SkiaRenderEngine;
+
+class Cache {
+public:
+ static void primeShaderCache(SkiaRenderEngine*);
+
+private:
+ Cache() = default;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 91b163e..f76bfa2 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -40,6 +40,7 @@
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
+#include "Cache.h"
#include <cmath>
#include <cstdint>
@@ -224,6 +225,10 @@
return engine;
}
+void SkiaGLRenderEngine::primeCache() {
+ Cache::primeShaderCache(this);
+}
+
EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
status_t err;
EGLConfig config;
@@ -646,9 +651,8 @@
if (mBlurFilter) {
bool requiresCompositionLayer = false;
for (const auto& layer : layers) {
- if (layer->backgroundBlurRadius > 0) {
- // when skbug.com/11208 and b/176903027 are resolved we can add the additional
- // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius
+ if (layer->backgroundBlurRadius > 0 &&
+ layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
requiresCompositionLayer = true;
}
for (auto region : layer->blurRegions) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 1c3a633..15d834d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -50,6 +50,7 @@
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
+ void primeCache() override;
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
void unbindExternalTextureBuffer(uint64_t bufferId) override;
status_t drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 79a1040..f403725 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,7 +39,7 @@
SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
~SkiaRenderEngine() override {}
- virtual void primeCache() const override{};
+ virtual void primeCache() override{};
virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
@@ -64,4 +64,4 @@
} // namespace renderengine
} // namespace android
-#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 7c7d165..6a91c7c 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -86,7 +86,7 @@
}
}
-void RenderEngineThreaded::primeCache() const {
+void RenderEngineThreaded::primeCache() {
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d362e17..df0551d 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
- void primeCache() const override;
+ void primeCache() override;
void dump(std::string& result) override;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a19b04f..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -130,6 +130,23 @@
pointerCaptureEnabled ? "true" : "false");
}
+// --- DragEntry ---
+
+// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting,
+ float x, float y)
+ : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER),
+ connectionToken(connectionToken),
+ isExiting(isExiting),
+ x(x),
+ y(y) {}
+
+DragEntry::~DragEntry() {}
+
+std::string DragEntry::getDescription() const {
+ return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
+}
+
// --- KeyEntry ---
KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ed17e68..e5fb26c 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -38,6 +38,7 @@
MOTION,
SENSOR,
POINTER_CAPTURE_CHANGED,
+ DRAG,
};
int32_t id;
@@ -111,6 +112,18 @@
virtual ~PointerCaptureChangedEntry();
};
+struct DragEntry : EventEntry {
+ sp<IBinder> connectionToken;
+ bool isExiting;
+ float x, y;
+
+ DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
+ float y);
+ std::string getDescription() const override;
+
+ ~DragEntry() override;
+};
+
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6d9190c..fe46d17 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -80,6 +80,7 @@
#define INDENT4 " "
using android::base::HwTimeoutMultiplier;
+using android::base::Result;
using android::base::StringPrintf;
using android::os::BlockUntrustedTouchesMode;
using android::os::IInputConstants;
@@ -752,6 +753,14 @@
break;
}
+ case EventEntry::Type::DRAG: {
+ std::shared_ptr<DragEntry> typedEntry =
+ std::static_pointer_cast<DragEntry>(mPendingEvent);
+ dispatchDragLocked(currentTime, typedEntry);
+ done = true;
+ break;
+ }
+
case EventEntry::Type::KEY: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
@@ -920,7 +929,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
// nothing to do
break;
}
@@ -942,7 +952,8 @@
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
int32_t y, TouchState* touchState,
bool addOutsideTargets,
- bool addPortalWindows) {
+ bool addPortalWindows,
+ bool ignoreDragWindow) {
if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
LOG_ALWAYS_FATAL(
"Must provide a valid touch state if adding portal windows or outside targets");
@@ -950,6 +961,9 @@
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+ if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) {
+ continue;
+ }
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
auto flags = windowInfo->flags;
@@ -1059,7 +1073,8 @@
case EventEntry::Type::SENSOR: {
break;
}
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::FOCUS:
@@ -1553,6 +1568,35 @@
return true;
}
+void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+ bool isExiting, const MotionEntry& motionEntry) {
+ // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
+ // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
+ LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
+ PointerCoords pointerCoords;
+ pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
+ pointerCoords.transform(windowHandle->getInfo()->transform);
+
+ std::unique_ptr<DragEntry> dragEntry =
+ std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
+ windowHandle->getToken(), isExiting, pointerCoords.getX(),
+ pointerCoords.getY());
+
+ enqueueInboundEventLocked(std::move(dragEntry));
+}
+
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ if (channel == nullptr) {
+ return; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, {target});
+}
+
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
@@ -1659,7 +1703,8 @@
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
return ADISPLAY_ID_NONE;
}
@@ -2016,6 +2061,8 @@
goto Failed;
}
+ addDragEventLocked(entry, tempTouchState);
+
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
@@ -2271,6 +2318,38 @@
return injectionResult;
}
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
+ if (entry.pointerCount != 1 || !state.dragWindow) {
+ return;
+ }
+
+ int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+ int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+ const sp<InputWindowHandle> hoverWindowHandle =
+ findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+ false /*addOutsideTargets*/, false /*addPortalWindows*/,
+ true /*ignoreDragWindow*/);
+ // enqueue drag exit if needed.
+ if (hoverWindowHandle != state.dragHoverWindowHandle &&
+ !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
+ if (state.dragHoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+ }
+ state.dragHoverWindowHandle = hoverWindowHandle;
+ }
+ // enqueue drag location if needed.
+ if (hoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+ maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ state.dragWindow = nullptr;
+ state.dragHoverWindowHandle = nullptr;
+ }
+}
+
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
@@ -2542,7 +2621,8 @@
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
if (eventEntry.type == EventEntry::Type::FOCUS ||
- eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+ eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
+ eventEntry.type == EventEntry::Type::DRAG) {
// Focus or pointer capture changed events are passed to apps, but do not represent user
// activity.
return;
@@ -2584,7 +2664,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s events are not user activity",
NamedEnum::string(eventEntry.type).c_str());
break;
@@ -2798,7 +2879,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::SENSOR: {
@@ -3021,6 +3103,15 @@
break;
}
+ case EventEntry::Type::DRAG: {
+ const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
+ status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
+ dragEntry.id, dragEntry.x,
+ dragEntry.y,
+ dragEntry.isExiting);
+ break;
+ }
+
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
@@ -3193,17 +3284,17 @@
nsecs_t currentTime = now();
bool gotOne = false;
- status_t status;
+ status_t status = OK;
for (;;) {
- std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
- std::bind(&InputDispatcher::finishDispatchCycleLocked, d, currentTime,
- connection, std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3);
-
- status = connection->inputPublisher.receiveFinishedSignal(callback);
- if (status) {
+ Result<InputPublisher::Finished> result =
+ connection->inputPublisher.receiveFinishedSignal();
+ if (!result.ok()) {
+ status = result.error().code();
break;
}
+ const InputPublisher::Finished& finished = *result;
+ d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
+ finished.handled, finished.consumeTime);
gotOne = true;
}
if (gotOne) {
@@ -3318,7 +3409,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("Canceling %s events is not supported",
NamedEnum::string(cancelationEventEntry->type).c_str());
break;
@@ -3383,7 +3475,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
NamedEnum::string(downEventEntry->type).c_str());
break;
@@ -4347,6 +4440,15 @@
++i;
}
}
+
+ // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+ // could just clear the state here.
+ if (state.dragWindow &&
+ std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+ windowHandles.end()) {
+ state.dragWindow = nullptr;
+ state.dragHoverWindowHandle = nullptr;
+ }
}
// Release information for windows that are no longer present.
@@ -4537,7 +4639,8 @@
mBlockUntrustedTouchesMode = mode;
}
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
ALOGD("Trivial transfer to same window.");
@@ -4581,6 +4684,11 @@
InputTarget::FLAG_DISPATCH_AS_IS);
state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
+ // Store the dragging window.
+ if (isDragDrop) {
+ state.dragWindow = toWindowHandle;
+ }
+
found = true;
goto Found;
}
@@ -4891,8 +4999,7 @@
}
}
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
- const std::string& name) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATION
ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
@@ -4921,8 +5028,10 @@
return clientChannel;
}
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
- int32_t displayId, bool isGestureMonitor, const std::string& name, int32_t pid) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name,
+ int32_t pid) {
std::shared_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
status_t result = openInputChannelPair(name, serverChannel, clientChannel);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 83094c2..b2f3625 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -120,8 +120,8 @@
virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
- const sp<IBinder>& toToken) override;
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
@@ -185,6 +185,9 @@
// Enqueues a focus event.
void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
const std::string& reason) REQUIRES(mLock);
+ // Enqueues a drag event.
+ void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
+ const MotionEntry& motionEntry) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -204,7 +207,8 @@
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
TouchState* touchState,
bool addOutsideTargets = false,
- bool addPortalWindows = false) REQUIRES(mLock);
+ bool addPortalWindows = false,
+ bool ignoreDragWindow = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
@@ -387,6 +391,7 @@
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
@@ -489,10 +494,12 @@
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
+ // Enqueue a drag event if needed, and update the touch state.
+ // Uses findTouchedWindowTargetsLocked to make the decision
+ void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock);
struct TouchOcclusionInfo {
bool hasBlockingOcclusion;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 81b3cf0..4165f49 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,6 +49,8 @@
windows = other.windows;
portalWindows = other.portalWindows;
gestureMonitors = other.gestureMonitors;
+ dragHoverWindowHandle = other.dragHoverWindowHandle;
+ dragWindow = other.dragWindow;
}
void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 623c6a8..d7a561c 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,6 +41,11 @@
std::vector<TouchedMonitor> gestureMonitors;
+ // The last drag hover window which could receive the drag event.
+ sp<InputWindowHandle> dragHoverWindowHandle;
+ // The window being dragged.
+ sp<InputWindowHandle> dragWindow;
+
TouchState();
~TouchState();
void reset();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3491893..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -149,8 +149,8 @@
*
* Returns true on success. False if the window did not actually have touch focus.
*/
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) = 0;
/**
* Sets focus on the specified window.
*/
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 18d0226..743587c 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -67,6 +67,7 @@
cflags: ["-Wall", "-Werror"],
shared_libs: [
+ "libbase",
"libbinder",
"libinputflingerhost",
"libutils",
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 8a2828c..1af70a4 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -68,6 +68,7 @@
"libcutils",
"libinput",
"liblog",
+ "libstatslog",
"libui",
"libutils",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 1a7fcd5..07011f5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,13 +37,14 @@
// #define LOG_NDEBUG 0
#include <android-base/file.h>
-#include <android-base/strings.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/properties.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/VirtualKeyMap.h>
#include <openssl/sha.h>
+#include <statslog.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -1461,7 +1462,7 @@
for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
it++) {
std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
- if (tryAddVideoDevice(*device, videoDevice)) {
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
// videoDevice was transferred to 'device'
it = mUnattachedVideoDevices.erase(it);
break;
@@ -1771,6 +1772,13 @@
}
}
+void EventHub::reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+ Flags<InputDeviceClass> classes) {
+ android::util::stats_write(android::util::INPUTDEVICE_REGISTERED, identifier.name.c_str(),
+ identifier.vendor, identifier.product, identifier.version,
+ identifier.bus, identifier.uniqueId.c_str(), classes.get());
+}
+
void EventHub::openDeviceLocked(const std::string& devicePath) {
// If an input device happens to register around the time when EventHub's constructor runs, it
// is possible that the same input event node (for example, /dev/input/event3) will be noticed
@@ -2076,7 +2084,7 @@
}
// Transfer ownership of this video device to a matching input device
for (const auto& [id, device] : mDevices) {
- if (tryAddVideoDevice(*device, videoDevice)) {
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
return; // 'device' now owns 'videoDevice'
}
}
@@ -2088,8 +2096,8 @@
mUnattachedVideoDevices.push_back(std::move(videoDevice));
}
-bool EventHub::tryAddVideoDevice(EventHub::Device& device,
- std::unique_ptr<TouchVideoDevice>& videoDevice) {
+bool EventHub::tryAddVideoDeviceLocked(EventHub::Device& device,
+ std::unique_ptr<TouchVideoDevice>& videoDevice) {
if (videoDevice->getName() != device.identifier.name) {
return false;
}
@@ -2163,6 +2171,7 @@
}
void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+ reportDeviceAddedForStatisticsLocked(device->identifier, device->classes);
mOpeningDevices.push_back(std::move(device));
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5e5f85e..2afaa85 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -570,8 +570,8 @@
/**
* Create a new device for the provided path.
*/
- void openDeviceLocked(const std::string& devicePath);
- void openVideoDeviceLocked(const std::string& devicePath);
+ void openDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
+ void openVideoDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
/**
* Try to associate a video device with an input device. If the association succeeds,
* the videoDevice is moved into the input device. 'videoDevice' will become null if this
@@ -579,39 +579,42 @@
* Return true if the association succeeds.
* Return false otherwise.
*/
- bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
- void createVirtualKeyboardLocked();
- void addDeviceLocked(std::unique_ptr<Device> device);
- void assignDescriptorLocked(InputDeviceIdentifier& identifier);
+ bool tryAddVideoDeviceLocked(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice)
+ REQUIRES(mLock);
+ void createVirtualKeyboardLocked() REQUIRES(mLock);
+ void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
+ void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
- void closeDeviceByPathLocked(const std::string& devicePath);
- void closeVideoDeviceByPathLocked(const std::string& devicePath);
- void closeDeviceLocked(Device& device);
- void closeAllDevicesLocked();
+ void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+ void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+ void closeDeviceLocked(Device& device) REQUIRES(mLock);
+ void closeAllDevicesLocked() REQUIRES(mLock);
status_t registerFdForEpoll(int fd);
status_t unregisterFdFromEpoll(int fd);
- status_t registerDeviceForEpollLocked(Device& device);
- void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
- status_t unregisterDeviceFromEpollLocked(Device& device);
- void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
+ status_t registerDeviceForEpollLocked(Device& device) REQUIRES(mLock);
+ void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
+ status_t unregisterDeviceFromEpollLocked(Device& device) REQUIRES(mLock);
+ void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
- status_t scanDirLocked(const std::string& dirname);
- status_t scanVideoDirLocked(const std::string& dirname);
- void scanDevicesLocked();
- status_t readNotifyLocked();
+ status_t scanDirLocked(const std::string& dirname) REQUIRES(mLock);
+ status_t scanVideoDirLocked(const std::string& dirname) REQUIRES(mLock);
+ void scanDevicesLocked() REQUIRES(mLock);
+ status_t readNotifyLocked() REQUIRES(mLock);
- Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
- Device* getDeviceLocked(int32_t deviceId) const;
- Device* getDeviceByPathLocked(const std::string& devicePath) const;
+ Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
+ Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
+ Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock);
/**
* Look through all available fd's (both for input devices and for video devices),
* and return the device pointer.
*/
- Device* getDeviceByFdLocked(int fd) const;
+ Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock);
- int32_t getNextControllerNumberLocked(const std::string& name);
- void releaseControllerNumberLocked(int32_t num);
+ int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock);
+ void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock);
+ void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+ Flags<InputDeviceClass> classes) REQUIRES(mLock);
// Protect all internal state.
mutable std::mutex mLock;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5cdcfaf..32f9b69 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -766,6 +766,9 @@
case AINPUT_EVENT_TYPE_CAPTURE: {
FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
}
+ case AINPUT_EVENT_TYPE_DRAG: {
+ FAIL() << "Use 'consumeDragEvent' for DRAG events";
+ }
default: {
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
@@ -803,6 +806,23 @@
EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ const InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of DRAG event";
+
+ EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+ }
+
void assertNoEvents() {
InputEvent* event = consume();
if (event == nullptr) {
@@ -905,7 +925,7 @@
mInfo.frameTop = frame.top;
mInfo.frameRight = frame.right;
mInfo.frameBottom = frame.bottom;
- mInfo.transform.set(frame.left, frame.top);
+ mInfo.transform.set(-frame.left, -frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
@@ -1003,6 +1023,10 @@
expectedFlags);
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ mInputReceiver->consumeDragEvent(isExiting, x, y);
+ }
+
std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -4682,4 +4706,87 @@
mTouchWindow->consumeAnyMotionDown();
}
+class InputDispatcherDragTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mSecondWindow;
+ sp<FakeWindowHandle> mDragWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+ mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ }
+
+ // Start performing drag, we will create a drag window and transfer touch to it.
+ void performDrag() {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // The drag window covers the entire display
+ mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Transfer touch focus to the drag window
+ mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+ true /* isDragDrop */);
+ mWindow->consumeMotionCancel();
+ mDragWindow->consumeMotionDown();
+ }
+};
+
+TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
+ performDrag();
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // Move back to original window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->consumeDragEvent(true, -50, 50);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 23893ea..d8e8b52 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -686,9 +686,9 @@
ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
if (isClientDisabledLocked(ident)) {
- ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+ ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
ident, handle);
- return INVALID_OPERATION;
+ return NO_ERROR;
}
if (info.batchParams.indexOfKey(ident) >= 0) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index d215298..5fed79f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -182,7 +182,7 @@
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
// Returns true if the next buffer should be presented at the expected present time
- bool shouldPresentNow(nsecs_t expectedPresentTime) const final;
+ bool shouldPresentNow(nsecs_t expectedPresentTime) const;
// Returns true if the next buffer should be presented at the expected present time,
// overridden by BufferStateLayer and BufferQueueLayer for implementation
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e017cdc..96a0c3c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -177,6 +177,8 @@
// leak.
ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
mName.c_str());
+ std::string miniDump = mPendingJankClassifications.front()->miniDump();
+ ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
mPendingJankClassifications.pop_front();
}
mPendingJankClassifications.emplace_back(surfaceFrame);
@@ -255,8 +257,8 @@
}
bool BufferStateLayer::setTransform(uint32_t transform) {
- if (mCurrentState.transform == transform) return false;
- mCurrentState.transform = transform;
+ if (mCurrentState.bufferTransform == transform) return false;
+ mCurrentState.bufferTransform = transform;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -308,17 +310,17 @@
h = frame.bottom;
}
- if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
- mCurrentState.active.w == w && mCurrentState.active.h == h) {
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
+ mCurrentState.width == w && mCurrentState.height == h) {
return false;
}
if (!frame.isValid()) {
x = y = w = h = 0;
}
- mCurrentState.active.transform.set(x, y);
- mCurrentState.active.w = w;
- mCurrentState.active.h = h;
+ mCurrentState.transform.set(x, y);
+ mCurrentState.width = w;
+ mCurrentState.height = h;
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -779,7 +781,7 @@
mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
mBufferInfo.mFence = s.acquireFence;
- mBufferInfo.mTransform = s.transform;
+ mBufferInfo.mTransform = s.bufferTransform;
mBufferInfo.mDataspace = translateDataspace(s.dataspace);
mBufferInfo.mCrop = computeCrop(s);
mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -846,10 +848,10 @@
const State& s(getDrawingState());
if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
return RoundedCornerState();
- return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
- static_cast<float>(s.active.transform.ty()),
- static_cast<float>(s.active.transform.tx() + s.active.w),
- static_cast<float>(s.active.transform.ty() + s.active.h)),
+ return RoundedCornerState(FloatRect(static_cast<float>(s.transform.tx()),
+ static_cast<float>(s.transform.ty()),
+ static_cast<float>(s.transform.tx() + s.width),
+ static_cast<float>(s.transform.ty() + s.height)),
radius);
}
@@ -863,7 +865,7 @@
uint32_t bufferHeight = s.buffer->height;
// Undo any transformations on the buffer and return the result.
- if (s.transform & ui::Transform::ROT_90) {
+ if (s.bufferTransform & ui::Transform::ROT_90) {
std::swap(bufferWidth, bufferHeight);
}
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 003edd5..ebf40cb 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -55,11 +55,9 @@
void pushPendingState() override;*/
bool applyPendingStates(Layer::State* stateToCommit) override;
- uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
- uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
- ui::Transform getActiveTransform(const Layer::State& s) const override {
- return s.active.transform;
- }
+ uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
+ uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
+ ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
@@ -91,7 +89,6 @@
bool /*allowNonRectPreservingTransforms*/) override {
return false;
}
- bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
uint64_t /*frameNumber*/) override {}
void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -118,6 +115,8 @@
std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
+ bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
+
protected:
void gatherBufferInfo() override;
uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index f9e5b9a..08147ed 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -103,6 +103,10 @@
test_suites: ["device-tests"],
defaults: ["libcompositionengine_defaults"],
srcs: [
+ "tests/planner/CachedSetTest.cpp",
+ "tests/planner/FlattenerTest.cpp",
+ "tests/planner/LayerStateTest.cpp",
+ "tests/planner/PredictorTest.cpp",
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 5f834be..a3e84e2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -89,6 +89,7 @@
sp<GraphicBuffer> buffer = nullptr;
sp<Fence> acquireFence = nullptr;
Rect displayFrame = {};
+ ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
} overrideInfo;
/*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 00424b2..b0e42b7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -17,15 +17,12 @@
#pragma once
#include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
#include <chrono>
namespace android {
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
namespace compositionengine::impl::planner {
std::string durationString(std::chrono::milliseconds duration);
@@ -63,8 +60,9 @@
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
size_t getAge() const { return mAge; }
- const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
const sp<Fence>& getDrawFence() const { return mDrawFence; }
+ ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
NonBufferHash getNonBufferHash() const;
@@ -82,7 +80,8 @@
void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
void append(const CachedSet& other) {
- mBuffer = nullptr;
+ mTexture.setBuffer(nullptr, nullptr);
+ mOutputDataspace = ui::Dataspace::UNKNOWN;
mDrawFence = nullptr;
mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
@@ -93,7 +92,8 @@
}
void incrementAge() { ++mAge; }
- void render(renderengine::RenderEngine&);
+ // Renders the cached set with the supplied output dataspace.
+ void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
void dump(std::string& result) const;
@@ -105,8 +105,34 @@
std::vector<Layer> mLayers;
Rect mBounds = Rect::EMPTY_RECT;
size_t mAge = 0;
- sp<GraphicBuffer> mBuffer;
+
+ class Texture {
+ public:
+ ~Texture() { setBuffer(nullptr, nullptr); }
+
+ void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
+ if (mRE && mBuffer) {
+ mRE->unbindExternalTextureBuffer(mBuffer->getId());
+ }
+
+ mBuffer = buffer;
+ mRE = re;
+
+ if (mRE && mBuffer) {
+ mRE->cacheExternalTextureBuffer(mBuffer);
+ }
+ }
+
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+ private:
+ sp<GraphicBuffer> mBuffer = nullptr;
+ renderengine::RenderEngine* mRE = nullptr;
+ };
+
+ Texture mTexture;
sp<Fence> mDrawFence;
+ ui::Dataspace mOutputDataspace;
static const bool sDebugHighlighLayers;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 6c86408..5b9a9f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -39,9 +39,11 @@
void setDisplaySize(ui::Size size) { mDisplaySize = size; }
- NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash);
+ NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
+ std::chrono::steady_clock::time_point now);
- void renderCachedSets(renderengine::RenderEngine&);
+ // Renders the newest cached sets with the supplied output dataspace
+ void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
void reset();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index d19ac62..a3beadc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -40,6 +40,7 @@
// clang-format off
enum class LayerStateField : uint32_t {
+ None = 0u,
Id = 1u << 0,
Name = 1u << 1,
DisplayFrame = 1u << 2,
@@ -173,8 +174,12 @@
// Returns which fields were updated
Flags<LayerStateField> update(compositionengine::OutputLayer*);
+ // Computes a hash for this LayerState.
+ // The hash is only computed from NonUniqueFields.
size_t getHash(Flags<LayerStateField> skipFields) const;
+ // Returns the bit-set of differing fields between this LayerState and another LayerState.
+ // This bit-set is based on NonUniqueFields only
Flags<LayerStateField> getDifferingFields(const LayerState& other,
Flags<LayerStateField> skipFields) const;
@@ -273,26 +278,14 @@
}};
using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
- DataspaceState mDataspace{[](auto layer) { return layer->getState().dataspace; },
- DataspaceState::getHalToStrings()};
+ DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
+ DataspaceState::getHalToStrings()};
// TODO(b/180638831): Buffer format
// Output-independent per-frame state
- OutputLayerState<mat4, LayerStateField::ColorTransform>
- mColorTransform{[](auto layer) {
- const auto state = layer->getLayerFE().getCompositionState();
- return state->colorTransformIsIdentity ? mat4{}
- : state->colorTransform;
- },
- [](const mat4& mat) {
- using namespace std::string_literals;
- std::vector<std::string> split =
- base::Split(std::string(mat.asString().string()), "\n"s);
- split.pop_back(); // Strip the last (empty) line
- return split;
- }};
+ OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
// TODO(b/180638831): Surface damage
@@ -348,7 +341,7 @@
std::array<const StateInterface*, 13> getNonUniqueFields() const {
return {
&mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
- &mBlendMode, &mAlpha, &mVisibleRegion, &mDataspace,
+ &mBlendMode, &mAlpha, &mVisibleRegion, &mOutputDataspace,
&mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
&mSolidColor,
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e96abb7..89de34d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,7 @@
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
// The planner will call to the Flattener to render any pending cached set
- void renderCachedSets(renderengine::RenderEngine&);
+ void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
void dump(const Vector<String16>& args, std::string&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index 422af77..fe486d3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -24,16 +24,28 @@
public:
LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {}
+ // Describes an approximate match between two layer stacks
struct ApproximateMatch {
bool operator==(const ApproximateMatch& other) const {
return differingIndex == other.differingIndex &&
differingFields == other.differingFields;
}
+ // The index of the single differing layer between the two stacks.
+ // This implies that only one layer is allowed to differ in an approximate match.
size_t differingIndex;
+ // Set of fields that differ for the differing layer in the approximate match.
Flags<LayerStateField> differingFields;
};
+ // Returns an approximate match when comparing this layer stack with the provided list of
+ // layers, for the purposes of scoring how closely the two layer stacks will match composition
+ // strategies.
+ //
+ // If the two layer stacks are identical, then an approximate match is still returned, but the
+ // differing fields will be empty to represent an exact match.
+ //
+ // If the two layer stacks differ by too much, then an empty optional is returned.
std::optional<ApproximateMatch> getApproximateMatch(
const std::vector<const LayerState*>& other) const;
@@ -108,6 +120,10 @@
}
friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); }
+ friend std::ostream& operator<<(std::ostream& os, const Plan& plan) {
+ return os << to_string(plan);
+ }
+
private:
std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
};
@@ -146,6 +162,10 @@
}
}
+ friend std::ostream& operator<<(std::ostream& os, const Type& type) {
+ return os << to_string(type);
+ }
+
Prediction(const std::vector<const LayerState*>& layers, Plan plan)
: mExampleLayerStack(layers), mPlan(std::move(plan)) {}
@@ -205,11 +225,25 @@
NonBufferHash hash;
Plan plan;
Prediction::Type type;
+
+ friend bool operator==(const PredictedPlan& lhs, const PredictedPlan& rhs) {
+ return lhs.hash == rhs.hash && lhs.plan == rhs.plan && lhs.type == rhs.type;
+ }
};
- std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&,
- NonBufferHash) const;
+ // Retrieves the predicted plan based on a layer stack alongside its hash.
+ //
+ // If the exact layer stack has previously been seen by the predictor, then report the plan used
+ // for that layer stack.
+ //
+ // Otherwise, try to match to the best approximate stack to retireve the most likely plan.
+ std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash) const;
+ // Records a comparison between the predicted plan and the resulting plan, alongside the layer
+ // stack we used.
+ //
+ // This method is intended to help with scoring how effective the prediction engine is.
void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
@@ -275,4 +309,13 @@
mutable size_t mMissCount = 0;
};
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(Predictor::PredictedPlan plan, ::std::ostream* os) {
+ *os << "PredictedPlan {";
+ *os << "\n .hash = " << plan.hash;
+ *os << "\n .plan = " << plan.plan;
+ *os << "\n .type = " << plan.type;
+ *os << "\n}";
+}
+
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index dc1aacc..ded2dcc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1252,7 +1252,7 @@
void Output::renderCachedSets() {
if (mPlanner) {
- mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine());
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 54784a2..b364649 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -681,6 +681,7 @@
.buffer = getState().overrideInfo.buffer,
.fence = getState().overrideInfo.acquireFence,
}};
+ settings.sourceDataspace = getState().overrideInfo.dataspace;
settings.alpha = 1.0f;
return {static_cast<LayerFE::LayerSettings>(settings)};
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ab3fe9e..137697b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -79,10 +79,11 @@
return mFingerprint;
}
- // TODO(b/181192080): Add all fields which contribute to geometry of override layer (e.g.,
- // dataspace)
+ // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
+ // necessary (and therefore we need to match implementations).
size_t hash = 0;
android::hashCombineSingle(hash, mBounds);
+ android::hashCombineSingle(hash, mOutputDataspace);
return hash;
}
@@ -126,7 +127,7 @@
}
bool CachedSet::hasReadyBuffer() const {
- return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+ return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
}
std::vector<CachedSet> CachedSet::decompose() const {
@@ -148,10 +149,11 @@
}
}
-void CachedSet::render(renderengine::RenderEngine& renderEngine) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
renderengine::DisplaySettings displaySettings{
.physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
.clip = mBounds,
+ .outputDataspace = outputDataspace,
};
Region clearRegion = Region::INVALID_REGION;
@@ -163,8 +165,7 @@
.supportsProtectedContent = false,
.clearRegion = clearRegion,
.viewport = viewport,
- // TODO(181192086): Propagate the Output's dataspace instead of using UNKNOWN
- .dataspace = ui::Dataspace::UNKNOWN,
+ .dataspace = outputDataspace,
.realContentIsVisible = true,
.clearContent = false,
.disableBlurs = false,
@@ -209,12 +210,14 @@
HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
base::unique_fd drawFence;
+
status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
base::unique_fd(), &drawFence);
if (result == NO_ERROR) {
- mBuffer = buffer;
+ mTexture.setBuffer(buffer, &renderEngine);
mDrawFence = new Fence(drawFence.release());
+ mOutputDataspace = outputDataspace;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 0c09714..30b5761 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -28,9 +28,7 @@
namespace android::compositionengine::impl::planner {
NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
- NonBufferHash hash) {
- const auto now = std::chrono::steady_clock::now();
-
+ NonBufferHash hash, time_point now) {
const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
mUnflattenedDisplayCost += unflattenedDisplayCost;
@@ -53,12 +51,13 @@
return hash;
}
-void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
+ ui::Dataspace outputDataspace) {
if (!mNewCachedSet) {
return;
}
- mNewCachedSet->render(renderEngine);
+ mNewCachedSet->render(renderEngine, outputDataspace);
}
void Flattener::reset() {
@@ -225,6 +224,7 @@
.buffer = mNewCachedSet->getBuffer(),
.acquireFence = mNewCachedSet->getDrawFence(),
.displayFrame = mNewCachedSet->getBounds(),
+ .dataspace = mNewCachedSet->getOutputDataspace(),
};
++incomingLayerIter;
}
@@ -255,6 +255,7 @@
.buffer = currentLayerIter->getBuffer(),
.acquireFence = currentLayerIter->getDrawFence(),
.displayFrame = currentLayerIter->getBounds(),
+ .dataspace = currentLayerIter->getOutputDataspace(),
};
++incomingLayerIter;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 7cf4819..222b2be 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -16,9 +16,28 @@
#include <compositionengine/impl/planner/LayerState.h>
+namespace {
+extern "C" const char* __attribute__((unused)) __asan_default_options() {
+ return "detect_container_overflow=0";
+}
+} // namespace
+
namespace android::compositionengine::impl::planner {
-LayerState::LayerState(compositionengine::OutputLayer* layer) : mOutputLayer(layer) {
+LayerState::LayerState(compositionengine::OutputLayer* layer)
+ : mOutputLayer(layer),
+ mColorTransform({[](auto layer) {
+ const auto state = layer->getLayerFE().getCompositionState();
+ return state->colorTransformIsIdentity ? mat4{}
+ : state->colorTransform;
+ },
+ [](const mat4& mat) {
+ using namespace std::string_literals;
+ std::vector<std::string> split =
+ base::Split(std::string(mat.asString().string()), "\n"s);
+ split.pop_back(); // Strip the last (empty) line
+ return split;
+ }}) {
update(layer);
}
@@ -139,7 +158,8 @@
lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
- lhs.mDataspace == rhs.mDataspace && lhs.mColorTransform == rhs.mColorTransform &&
+ lhs.mOutputDataspace == rhs.mOutputDataspace &&
+ lhs.mColorTransform == rhs.mColorTransform &&
lhs.mCompositionType == rhs.mCompositionType &&
lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
(lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 52efff5..87721c7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -89,7 +89,8 @@
});
const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
- mFlattenedHash = mFlattener.flattenLayers(mCurrentLayers, hash);
+ mFlattenedHash =
+ mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
const bool layersWereFlattened = hash != mFlattenedHash;
ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
@@ -132,8 +133,9 @@
finalPlan);
}
-void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
- mFlattener.renderCachedSets(renderEngine);
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
+ ui::Dataspace outputDataspace) {
+ mFlattener.renderCachedSets(renderEngine, outputDataspace);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index ba5e64d..07920b8 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -50,8 +50,8 @@
return std::nullopt;
}
- // If layers are not identical, but we already have a prior approximate match,
- // the LayerStacks differ by too much, so return nothing
+ // If layers are not identical, but we already detected a prior approximate match for a
+ // previous layer, the LayerStacks differ by too much, so return nothing
if (approximateMatch) {
return std::nullopt;
}
@@ -72,6 +72,10 @@
}
}
+ if (approximateMatch) {
+ return approximateMatch;
+ }
+
// If we make it through the layer-by-layer comparison without an approximate match,
// it means that all layers were either identical or had client-composited layers in common,
// which don't affect the composition strategy, so return a successful result with
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
new file mode 100644
index 0000000..377f817
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine {
+using namespace std::chrono_literals;
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+
+using impl::planner::CachedSet;
+using impl::planner::LayerState;
+using impl::planner::LayerStateField;
+
+namespace {
+
+class CachedSetTest : public testing::Test {
+public:
+ CachedSetTest() = default;
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
+
+ struct TestLayer {
+ mock::OutputLayer outputLayer;
+ impl::OutputLayerCompositionState outputLayerCompositionState;
+ // LayerFE inherits from RefBase and must be held by an sp<>
+ sp<mock::LayerFE> layerFE;
+ LayerFECompositionState layerFECompositionState;
+
+ std::unique_ptr<LayerState> layerState;
+ std::unique_ptr<CachedSet::Layer> cachedSetLayer;
+ };
+
+ static constexpr size_t kNumLayers = 5;
+ std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+
+ android::renderengine::mock::RenderEngine mRenderEngine;
+};
+
+void CachedSetTest::SetUp() {
+ for (size_t i = 0; i < kNumLayers; i++) {
+ auto testLayer = std::make_unique<TestLayer>();
+ auto pos = static_cast<int32_t>(i);
+ testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+
+ testLayer->layerFE = sp<mock::LayerFE>::make();
+
+ EXPECT_CALL(*testLayer->layerFE, getSequence)
+ .WillRepeatedly(Return(static_cast<int32_t>(i)));
+ EXPECT_CALL(*testLayer->layerFE, getDebugName).WillRepeatedly(Return("testLayer"));
+ EXPECT_CALL(*testLayer->layerFE, getCompositionState)
+ .WillRepeatedly(Return(&testLayer->layerFECompositionState));
+ EXPECT_CALL(testLayer->outputLayer, getLayerFE)
+ .WillRepeatedly(ReturnRef(*testLayer->layerFE));
+ EXPECT_CALL(testLayer->outputLayer, getState)
+ .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+
+ testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
+ testLayer->layerState->incrementFramesSinceBufferUpdate();
+ testLayer->cachedSetLayer =
+ std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
+
+ mTestLayers.emplace_back(std::move(testLayer));
+ }
+}
+
+void CachedSetTest::TearDown() {
+ mTestLayers.clear();
+}
+
+void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) {
+ EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
+ EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
+ EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
+ EXPECT_EQ(1u, cachedSet.getLayerCount());
+ EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState());
+ EXPECT_EQ(0u, cachedSet.getAge());
+ EXPECT_EQ(layer.getHash(), cachedSet.getNonBufferHash());
+}
+
+void expectEqual(const CachedSet& cachedSet, const LayerState& layerState,
+ std::chrono::steady_clock::time_point lastUpdate) {
+ CachedSet::Layer layer(&layerState, lastUpdate);
+ expectEqual(cachedSet, layer);
+}
+
+void expectNoBuffer(const CachedSet& cachedSet) {
+ EXPECT_EQ(nullptr, cachedSet.getBuffer());
+ EXPECT_EQ(nullptr, cachedSet.getDrawFence());
+ EXPECT_FALSE(cachedSet.hasReadyBuffer());
+}
+
+void expectReadyBuffer(const CachedSet& cachedSet) {
+ EXPECT_NE(nullptr, cachedSet.getBuffer());
+ EXPECT_NE(nullptr, cachedSet.getDrawFence());
+ EXPECT_TRUE(cachedSet.hasReadyBuffer());
+}
+
+TEST_F(CachedSetTest, createFromLayer) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet cachedSet(layer);
+ expectEqual(cachedSet, layer);
+ expectNoBuffer(cachedSet);
+}
+
+TEST_F(CachedSetTest, createFromLayerState) {
+ LayerState& layerState = *mTestLayers[0]->layerState.get();
+ CachedSet cachedSet(&layerState, kStartTime);
+ expectEqual(cachedSet, layerState, kStartTime);
+ expectNoBuffer(cachedSet);
+}
+
+TEST_F(CachedSetTest, addLayer) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+ EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
+ EXPECT_EQ(2u, cachedSet.getLayerCount());
+ EXPECT_EQ(0u, cachedSet.getAge());
+ expectNoBuffer(cachedSet);
+ // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
+ // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
+ // cachedSet.getNonBufferHash());
+}
+
+TEST_F(CachedSetTest, decompose) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+ cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+ std::vector<CachedSet> decomposed = cachedSet.decompose();
+ EXPECT_EQ(3u, decomposed.size());
+ expectEqual(decomposed[0], *layer1.getState(), kStartTime);
+ expectNoBuffer(decomposed[0]);
+
+ expectEqual(decomposed[1], *layer2.getState(), kStartTime + 10ms);
+ expectNoBuffer(decomposed[1]);
+
+ expectEqual(decomposed[2], *layer3.getState(), kStartTime + 20ms);
+ expectNoBuffer(decomposed[2]);
+}
+
+TEST_F(CachedSetTest, setLastUpdate) {
+ LayerState& layerState = *mTestLayers[0]->layerState.get();
+ CachedSet cachedSet(&layerState, kStartTime);
+ cachedSet.setLastUpdate(kStartTime + 10ms);
+ expectEqual(cachedSet, layerState, kStartTime + 10ms);
+}
+
+TEST_F(CachedSetTest, incrementAge) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet cachedSet(layer);
+ EXPECT_EQ(0u, cachedSet.getAge());
+ cachedSet.incrementAge();
+ EXPECT_EQ(1u, cachedSet.getAge());
+ cachedSet.incrementAge();
+ EXPECT_EQ(2u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+ cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+ std::vector<const LayerState*> incomingLayers = {
+ layer1.getState(),
+ layer2.getState(),
+ layer3.getState(),
+ };
+
+ EXPECT_FALSE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+}
+
+TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer1);
+ cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+ cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+ mTestLayers[1]->layerState->resetFramesSinceBufferUpdate();
+
+ std::vector<const LayerState*> incomingLayers = {
+ layer1.getState(),
+ layer2.getState(),
+ layer3.getState(),
+ };
+
+ EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+}
+
+TEST_F(CachedSetTest, append) {
+ 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);
+ cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
+ cachedSet1.append(cachedSet2);
+
+ EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
+ EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
+ EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
+ EXPECT_EQ(3u, cachedSet1.getLayerCount());
+ EXPECT_EQ(0u, cachedSet1.getAge());
+ expectNoBuffer(cachedSet1);
+ // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
+ // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
+ // cachedSet1.getNonBufferHash());
+}
+
+TEST_F(CachedSetTest, updateAge_NoUpdate) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+
+ CachedSet cachedSet(layer);
+ cachedSet.incrementAge();
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(1u, cachedSet.getAge());
+
+ cachedSet.updateAge(kStartTime + 10ms);
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(1u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, updateAge_BufferUpdate) {
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerState->resetFramesSinceBufferUpdate();
+
+ CachedSet cachedSet(layer);
+ cachedSet.incrementAge();
+ EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+ EXPECT_EQ(1u, cachedSet.getAge());
+
+ cachedSet.updateAge(kStartTime + 10ms);
+ EXPECT_EQ(kStartTime + 10ms, cachedSet.getLastUpdate());
+ EXPECT_EQ(0u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, render) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+ clientCompList1[0].alpha = 0.5f;
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+ clientCompList2[0].alpha = 0.75f;
+
+ const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>& layers,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*) -> size_t {
+ EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip);
+ EXPECT_EQ(0.5f, layers[0]->alpha);
+ EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+ return NO_ERROR;
+ };
+
+ EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
+ cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+ expectReadyBuffer(cachedSet);
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
new file mode 100644
index 0000000..bd77559
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine {
+using namespace std::chrono_literals;
+using impl::planner::Flattener;
+using impl::planner::LayerState;
+using impl::planner::NonBufferHash;
+using impl::planner::Predictor;
+
+using testing::_;
+using testing::ByMove;
+using testing::ByRef;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::Sequence;
+using testing::SetArgPointee;
+
+namespace {
+
+class FlattenerTest : public testing::Test {
+public:
+ FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
+ void SetUp() override;
+
+protected:
+ void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
+ void initializeFlattener(const std::vector<const LayerState*>& layers);
+ void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
+
+ // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
+ // mPredictor should be mocked and checked for expectations.
+ Predictor mPredictor;
+
+ // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
+ renderengine::mock::RenderEngine mRenderEngine;
+ std::unique_ptr<Flattener> mFlattener;
+
+ const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
+ std::chrono::steady_clock::time_point mTime = kStartTime;
+
+ struct TestLayer {
+ std::string name;
+ mock::OutputLayer outputLayer;
+ impl::OutputLayerCompositionState outputLayerCompositionState;
+ // LayerFE inherits from RefBase and must be held by an sp<>
+ sp<mock::LayerFE> layerFE;
+ LayerFECompositionState layerFECompositionState;
+
+ std::unique_ptr<LayerState> layerState;
+ };
+
+ static constexpr size_t kNumLayers = 5;
+ std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+};
+
+void FlattenerTest::SetUp() {
+ for (size_t i = 0; i < kNumLayers; i++) {
+ auto testLayer = std::make_unique<TestLayer>();
+ auto pos = static_cast<int32_t>(i);
+ std::stringstream ss;
+ ss << "testLayer" << i;
+ testLayer->name = ss.str();
+
+ testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+
+ testLayer->layerFECompositionState.buffer =
+ new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
+ "output");
+
+ testLayer->layerFE = sp<mock::LayerFE>::make();
+
+ EXPECT_CALL(*testLayer->layerFE, getSequence)
+ .WillRepeatedly(Return(static_cast<int32_t>(i)));
+ EXPECT_CALL(*testLayer->layerFE, getDebugName)
+ .WillRepeatedly(Return(testLayer->name.c_str()));
+ EXPECT_CALL(*testLayer->layerFE, getCompositionState)
+ .WillRepeatedly(Return(&testLayer->layerFECompositionState));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+
+ EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList)
+ .WillRepeatedly(Return(clientCompositionList));
+ EXPECT_CALL(testLayer->outputLayer, getLayerFE)
+ .WillRepeatedly(ReturnRef(*testLayer->layerFE));
+ EXPECT_CALL(testLayer->outputLayer, getState)
+ .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+ EXPECT_CALL(testLayer->outputLayer, editState)
+ .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+
+ testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
+ testLayer->layerState->incrementFramesSinceBufferUpdate();
+
+ mTestLayers.emplace_back(std::move(testLayer));
+ }
+}
+
+void FlattenerTest::initializeOverrideBuffer(const std::vector<const LayerState*>& layers) {
+ for (const auto layer : layers) {
+ layer->getOutputLayer()->editState().overrideInfo = {};
+ }
+}
+
+void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& layers) {
+ // layer stack is unknown, reset current geomentry
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ // same geometry, update the internal layer stack
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+}
+
+void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
+ // 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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
+ EXPECT_NE(nullptr, buffer);
+ for (const auto layer : layers) {
+ EXPECT_EQ(buffer, layer->getOutputLayer()->getState().overrideInfo.buffer);
+ }
+}
+
+TEST_F(FlattenerTest, flattenLayers_NewLayerStack) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+ initializeFlattener(layers);
+}
+
+TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // layers cannot be flattened yet, since they are still active
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+}
+
+TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+ auto& layerState3 = mTestLayers[2]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+ // make all layers inactive
+ mTime += 200ms;
+
+ initializeOverrideBuffer(layers);
+ expectAllLayersFlattened(layers);
+
+ // add a new layer to the stack, this will cause all the flatenner to reset
+ layers.push_back(layerState3.get());
+
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ // Layer 1 posted a buffer update, layers would be decomposed, and a new drawFrame would be
+ // caleed for Layer2 and Layer3
+ layerState1->resetFramesSinceBufferUpdate();
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_NE(nullptr, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+
+ layerState1->incrementFramesSinceBufferUpdate();
+ mTime += 200ms;
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_NE(nullptr, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState4 = mTestLayers[3]->layerState;
+ const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState5 = mTestLayers[4]->layerState;
+ const auto& overrideBuffer5 = layerState5->getOutputLayer()->getState().overrideInfo.buffer;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(), layerState2.get(), layerState3.get(),
+ layerState4.get(), layerState5.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ // Layer 3 posted a buffer update, layers would be decomposed, and a new drawFrame would be
+ // called for Layer1 and Layer2
+ layerState3->resetFramesSinceBufferUpdate();
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_EQ(nullptr, overrideBuffer4);
+ EXPECT_EQ(nullptr, overrideBuffer5);
+
+ // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_EQ(nullptr, overrideBuffer4);
+ EXPECT_EQ(nullptr, overrideBuffer5);
+
+ // Layers 4 and 5 will be flattened
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_NE(nullptr, overrideBuffer4);
+ EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+
+ layerState3->incrementFramesSinceBufferUpdate();
+ mTime += 200ms;
+
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_NE(nullptr, overrideBuffer4);
+ EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+ EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+ EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
new file mode 100644
index 0000000..8f235ab
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -0,0 +1,1020 @@
+/*
+ * 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 "LayerStateTest"
+
+#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const std::string sDebugNameTwo = std::string("Test LayerFE2");
+const constexpr int32_t sSequenceId = 12345;
+const constexpr int32_t sSequenceIdTwo = 123456;
+const Rect sRectOne = Rect(10, 20, 30, 40);
+const Rect sRectTwo = Rect(40, 30, 20, 10);
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const constexpr int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+native_handle_t* const sFakeSidebandStreamOne = reinterpret_cast<native_handle_t*>(10);
+native_handle_t* const sFakeSidebandStreamTwo = reinterpret_cast<native_handle_t*>(11);
+const half4 sHalf4One = half4(0.2f, 0.3f, 0.4f, 0.5f);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.43, 0.2f);
+
+struct LayerStateTest : public testing::Test {
+ LayerStateTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~LayerStateTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+ const OutputLayerCompositionState& outputLayerState,
+ const LayerFECompositionState& layerFEState,
+ int32_t sequenceId = sSequenceId,
+ const std::string& debugName = sDebugName) {
+ EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sequenceId));
+ EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(debugName.c_str()));
+ EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ }
+
+ mock::LayerFE mLayerFE;
+ mock::OutputLayer mOutputLayer;
+ std::unique_ptr<LayerState> mLayerState;
+};
+
+TEST_F(LayerStateTest, getOutputLayer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer());
+}
+
+TEST_F(LayerStateTest, getId) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(sSequenceId, mLayerState->getId());
+}
+
+TEST_F(LayerStateTest, updateId) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceIdTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(sSequenceIdTwo, mLayerState->getId());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Id), updates);
+}
+
+TEST_F(LayerStateTest, compareId) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceIdTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getId(), otherLayerState->getId());
+
+ // Id is a unique field, so it's not computed in the hash for a layer state.
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Id),
+ otherLayerState->getHash(LayerStateField::Id));
+
+ // Similarly, Id cannot be included in differing fields.
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Id));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Id));
+
+ EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+ EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getName) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(sDebugName, mLayerState->getName());
+}
+
+TEST_F(LayerStateTest, updateName) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceId, sDebugNameTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(sDebugNameTwo, mLayerState->getName());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Name), updates);
+}
+
+TEST_F(LayerStateTest, compareName) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState, sSequenceId, sDebugNameTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getName(), otherLayerState->getName());
+
+ // Name is a unique field, so it's not computed in the hash for a layer state.
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Name),
+ otherLayerState->getHash(LayerStateField::Name));
+
+ // Similarly, Name cannot be included in differing fields.
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Name));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Name));
+
+ EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+ EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getDisplayFrame) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(sRectOne, mLayerState->getDisplayFrame());
+}
+
+TEST_F(LayerStateTest, updateDisplayFrame) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(sRectTwo, mLayerState->getDisplayFrame());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame), updates);
+}
+
+TEST_F(LayerStateTest, compareDisplayFrame) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getDisplayFrame(), otherLayerState->getDisplayFrame());
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::DisplayFrame),
+ otherLayerState->getHash(LayerStateField::DisplayFrame));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::DisplayFrame));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::DisplayFrame));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getCompositionType) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
+ mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, getCompositionType_forcedClient) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.forceClientComposition = true;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
+ mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, updateCompositionType) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType =
+ hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
+ mLayerState->getCompositionType());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
+}
+
+TEST_F(LayerStateTest, compareCompositionType) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.compositionType =
+ hardware::graphics::composer::hal::Composition::DEVICE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType =
+ hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getCompositionType(), otherLayerState->getCompositionType());
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::CompositionType),
+ otherLayerState->getHash(LayerStateField::CompositionType));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::CompositionType));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::CompositionType));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getBuffer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(layerFECompositionState.buffer, mLayerState->getBuffer());
+}
+
+TEST_F(LayerStateTest, updateBuffer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+}
+
+TEST_F(LayerStateTest, compareBuffer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getBuffer(), otherLayerState->getBuffer());
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Buffer),
+ otherLayerState->getHash(LayerStateField::Buffer));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Buffer));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Buffer));
+
+ // Buffers are explicitly excluded from comparison
+ EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+ EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSourceCrop) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.sourceCrop = sFloatRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop), updates);
+}
+
+TEST_F(LayerStateTest, compareSourceCrop) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.sourceCrop = sFloatRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::SourceCrop),
+ otherLayerState->getHash(LayerStateField::SourceCrop));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SourceCrop));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SourceCrop));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateZOrder) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.z = sZOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.z = sZTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder), updates);
+}
+
+TEST_F(LayerStateTest, compareZOrder) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.z = sZOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.z = sZTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::ZOrder),
+ otherLayerState->getHash(LayerStateField::ZOrder));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ZOrder));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ZOrder));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBufferTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareBufferTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::BufferTransform),
+ otherLayerState->getHash(LayerStateField::BufferTransform));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BufferTransform));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BufferTransform));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlendMode) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode), updates);
+}
+
+TEST_F(LayerStateTest, compareBlendMode) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::BlendMode),
+ otherLayerState->getHash(LayerStateField::BlendMode));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BlendMode));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BlendMode));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateAlpha) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.alpha = sAlphaOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha), updates);
+}
+
+TEST_F(LayerStateTest, compareAlpha) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.alpha = sAlphaOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Alpha),
+ otherLayerState->getHash(LayerStateField::Alpha));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Alpha));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Alpha));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateVisibleRegion) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.visibleRegion = sRegionOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion), updates);
+}
+
+TEST_F(LayerStateTest, compareVisibleRegion) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.visibleRegion = sRegionOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::VisibleRegion),
+ otherLayerState->getHash(LayerStateField::VisibleRegion));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::VisibleRegion));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::VisibleRegion));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateDataspace) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace), updates);
+}
+
+TEST_F(LayerStateTest, compareDataspace) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Dataspace),
+ otherLayerState->getHash(LayerStateField::Dataspace));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Dataspace));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Dataspace));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateColorTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.colorTransformIsIdentity = true;
+ layerFECompositionState.colorTransform = mat4();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareColorTransform) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.colorTransformIsIdentity = true;
+ layerFECompositionState.colorTransform = mat4();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::ColorTransform),
+ otherLayerState->getHash(LayerStateField::ColorTransform));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ColorTransform));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ColorTransform));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSidebandStream) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream), updates);
+}
+
+TEST_F(LayerStateTest, compareSidebandStream) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::SidebandStream),
+ otherLayerState->getHash(LayerStateField::SidebandStream));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SidebandStream));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SidebandStream));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSolidColor) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.color = sHalf4One;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.color = sHalf4Two;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor), updates);
+}
+
+TEST_F(LayerStateTest, compareSolidColor) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.color = sHalf4One;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.color = sHalf4Two;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::SolidColor),
+ otherLayerState->getHash(LayerStateField::SolidColor));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SolidColor));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SolidColor));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, dumpDoesNotCrash) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ std::string dump;
+ mLayerState->dump(dump);
+ EXPECT_TRUE(dump.size() > 0);
+}
+
+TEST_F(LayerStateTest, framesSinceBufferUpdate) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+ mLayerState->incrementFramesSinceBufferUpdate();
+ EXPECT_EQ(1, mLayerState->getFramesSinceBufferUpdate());
+ mLayerState->resetFramesSinceBufferUpdate();
+ EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_doesNotCommute) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+ getNonBufferHash({otherLayerState.get(), mLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_isIdempotent) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.displayFrame = sRectOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_EQ(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+ getNonBufferHash({mLayerState.get(), otherLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_EQ(getNonBufferHash({mLayerState.get()}), getNonBufferHash({otherLayerState.get()}));
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
new file mode 100644
index 0000000..43e119f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DisplayHardware/Hal.h"
+#undef LOG_TAG
+#define LOG_TAG "PredictorTest"
+
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const Rect sRectOne = Rect(1, 2, 3, 4);
+const Rect sRectTwo = Rect(4, 3, 2, 1);
+const constexpr int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const constexpr int32_t sSequenceId = 12345;
+
+void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+ const OutputLayerCompositionState& outputLayerState,
+ const LayerFECompositionState& layerFEState) {
+ EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sSequenceId));
+ EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(sDebugName.c_str()));
+ EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+}
+
+struct LayerStackTest : public testing::Test {
+ LayerStackTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~LayerStackTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ mock::OutputLayer outputLayerThree;
+ mock::LayerFE layerFEThree;
+ OutputLayerCompositionState outputLayerCompositionStateThree;
+ LayerFECompositionState layerFECompositionStateThree;
+ setupMocksForLayer(outputLayerThree, layerFEThree, outputLayerCompositionStateThree,
+ layerFECompositionStateThree);
+ LayerState layerStateThree(&outputLayerThree);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({}));
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateOne, &layerStateThree}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::SourceCrop;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne, &layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateOne, &layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 1;
+ expectedMatch.differingFields = LayerStateField::SourceCrop;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .visibleRegion = sRegionOne,
+ .displayFrame = sRectOne,
+ .sourceCrop = sFloatRectOne,
+ .dataspace = ui::Dataspace::SRGB,
+ .z = sZOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .visibleRegion = sRegionTwo,
+ .displayFrame = sRectTwo,
+ .sourceCrop = sFloatRectTwo,
+ .dataspace = ui::Dataspace::DISPLAY_P3,
+ .z = sZTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = buffer;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = buffer;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::None;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .visibleRegion = sRegionOne,
+ .forceClientComposition = true,
+ .displayFrame = sRectOne,
+ .sourceCrop = sFloatRectOne,
+ .dataspace = ui::Dataspace::SRGB,
+ .z = sZOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = new GraphicBuffer();
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .visibleRegion = sRegionTwo,
+ .forceClientComposition = true,
+ .displayFrame = sRectTwo,
+ .sourceCrop = sFloatRectTwo,
+ .dataspace = ui::Dataspace::DISPLAY_P3,
+ .z = sZTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::None;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = new GraphicBuffer();
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ EXPECT_TRUE(LayerStack({&layerStateOne}).getApproximateMatch({&layerStateTwo}));
+
+ LayerStack stack({&layerStateOne, &layerStateOne});
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo, &layerStateTwo}));
+}
+
+struct PredictionTest : public testing::Test {
+ PredictionTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~PredictionTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(PredictionTest, constructPrediction) {
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Prediction prediction({}, plan);
+
+ EXPECT_EQ(plan, prediction.getPlan());
+
+ // check that dump doesn't crash
+ std::string result;
+ prediction.dump(result);
+}
+
+TEST_F(PredictionTest, recordHits) {
+ Prediction prediction({}, {});
+
+ const constexpr uint32_t kExactMatches = 2;
+ for (uint32_t i = 0; i < kExactMatches; i++) {
+ prediction.recordHit(Prediction::Type::Exact);
+ }
+
+ const constexpr uint32_t kApproximateMatches = 3;
+ for (uint32_t i = 0; i < kApproximateMatches; i++) {
+ prediction.recordHit(Prediction::Type::Approximate);
+ }
+
+ EXPECT_EQ(kExactMatches, prediction.getHitCount(Prediction::Type::Exact));
+ EXPECT_EQ(kApproximateMatches, prediction.getHitCount(Prediction::Type::Approximate));
+ EXPECT_EQ(kExactMatches + kApproximateMatches, prediction.getHitCount(Prediction::Type::Total));
+}
+
+TEST_F(PredictionTest, recordMisses) {
+ Prediction prediction({}, {});
+
+ const constexpr uint32_t kExactMatches = 2;
+ for (uint32_t i = 0; i < kExactMatches; i++) {
+ prediction.recordMiss(Prediction::Type::Exact);
+ }
+
+ const constexpr uint32_t kApproximateMatches = 3;
+ for (uint32_t i = 0; i < kApproximateMatches; i++) {
+ prediction.recordMiss(Prediction::Type::Approximate);
+ }
+
+ EXPECT_EQ(kExactMatches, prediction.getMissCount(Prediction::Type::Exact));
+ EXPECT_EQ(kApproximateMatches, prediction.getMissCount(Prediction::Type::Approximate));
+ EXPECT_EQ(kExactMatches + kApproximateMatches,
+ prediction.getMissCount(Prediction::Type::Total));
+}
+
+struct PredictorTest : public testing::Test {
+ PredictorTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~PredictorTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(PredictorTest, getPredictedPlan_emptyLayersWithoutExactMatch_returnsNullopt) {
+ Predictor predictor;
+ EXPECT_FALSE(predictor.getPredictedPlan({}, 0));
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hash = getNonBufferHash({&layerStateOne});
+
+ predictor.recordResult(std::nullopt, hash, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({}, hash);
+ EXPECT_TRUE(predictedPlan);
+ Predictor::PredictedPlan expectedPlan{hash, plan, Prediction::Type::Exact};
+ EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+ NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+ predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ EXPECT_TRUE(predictedPlan);
+ Predictor::PredictedPlan expectedPlan{hashOne, plan, Prediction::Type::Approximate};
+ EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+ NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+ predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ ASSERT_TRUE(predictedPlan);
+ EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
+
+ Plan planTwo;
+ planTwo.addLayerType(hal::Composition::CLIENT);
+ predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
+ // Now trying to retrieve the predicted plan again returns a nullopt instead.
+ // TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
+ // One of the implications around this implementation is that if we miss a prediction then we
+ // can never actually correct our mistake if we see the same layer stack again, which doesn't
+ // seem robust.
+ auto predictedPlanTwo = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ EXPECT_FALSE(predictedPlanTwo);
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 3442706..b1dff8d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -296,7 +296,7 @@
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter)
+ TraceCookieCounter* traceCookieCounter, bool isBuffer)
: mToken(frameTimelineInfo.vsyncId),
mInputEventId(frameTimelineInfo.inputEventId),
mOwnerPid(ownerPid),
@@ -310,7 +310,8 @@
mActuals({0, 0, 0}),
mTimeStats(timeStats),
mJankClassificationThresholds(thresholds),
- mTraceCookieCounter(*traceCookieCounter) {}
+ mTraceCookieCounter(*traceCookieCounter),
+ mIsBuffer(isBuffer) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::scoped_lock lock(mMutex);
@@ -395,6 +396,20 @@
return mDropTime;
}
+void SurfaceFrame::promoteToBuffer() {
+ std::scoped_lock lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(mIsBuffer == true,
+ "Trying to promote an already promoted BufferSurfaceFrame from layer %s "
+ "with token %" PRId64 "",
+ mDebugName.c_str(), mToken);
+ mIsBuffer = true;
+}
+
+bool SurfaceFrame::getIsBuffer() const {
+ std::scoped_lock lock(mMutex);
+ return mIsBuffer;
+}
+
void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
std::scoped_lock lock(mMutex);
StringAppendF(&result, "%s", indent.c_str());
@@ -407,6 +422,8 @@
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
@@ -444,6 +461,21 @@
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
+std::string SurfaceFrame::miniDump() const {
+ std::scoped_lock lock(mMutex);
+ std::string result;
+ StringAppendF(&result, "Layer - %s\n", mDebugName.c_str());
+ StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+ StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+ StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ StringAppendF(&result, "Present time: %" PRId64 "", mActuals.presentTime);
+ return result;
+}
+
void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
nsecs_t& deadlineDelta) {
if (mPredictionState == PredictionState::Expired ||
@@ -744,13 +776,14 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName) {
+ std::string layerName, std::string debugName, bool isBuffer) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
}
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
@@ -759,12 +792,13 @@
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter);
+ &mTraceCookieCounter, isBuffer);
}
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
}
FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index b66e02a..3cf35f0 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -159,7 +159,7 @@
int32_t layerId, std::string layerName, std::string debugName,
PredictionState predictionState, TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter);
+ TraceCookieCounter* traceCookieCounter, bool isBuffer);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -181,6 +181,10 @@
void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
void setRenderRate(Fps renderRate);
+ // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
+ // isBuffer.
+ void promoteToBuffer();
+
// Functions called by FrameTimeline
// BaseTime is the smallest timestamp in this SurfaceFrame.
// Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -192,6 +196,8 @@
nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
// All the timestamps are dumped relative to the baseTime
void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+ // Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
+ std::string miniDump() const;
// Emits a packet for perfetto tracing. The function body will be executed only if tracing is
// enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
// DisplayFrame at the trace processor side.
@@ -206,6 +212,7 @@
FrameReadyMetadata getFrameReadyMetadata() const;
FramePresentMetadata getFramePresentMetadata() const;
nsecs_t getDropTime() const;
+ bool getIsBuffer() const;
// For prediction expired frames, this delta is subtracted from the actual end time to get a
// start time decent enough to see in traces.
@@ -253,6 +260,9 @@
// TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
// reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
TraceCookieCounter& mTraceCookieCounter;
+ // Tells if the SurfaceFrame is representing a buffer or a transaction without a
+ // buffer(animations)
+ bool mIsBuffer;
};
/*
@@ -272,7 +282,7 @@
// Debug name is the human-readable debugging string for dumpsys.
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName) = 0;
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -431,7 +441,7 @@
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName) override;
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 061ad0e..0015bf2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -102,18 +102,18 @@
mCurrentState.active_legacy.h = args.h;
mCurrentState.flags = layerFlags;
mCurrentState.active_legacy.transform.set(0, 0);
- mCurrentState.crop_legacy.makeInvalid();
- mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy;
+ mCurrentState.crop.makeInvalid();
+ mCurrentState.requestedCrop = mCurrentState.crop;
mCurrentState.z = 0;
mCurrentState.color.a = 1.0f;
mCurrentState.layerStack = 0;
mCurrentState.sequence = 0;
mCurrentState.requested_legacy = mCurrentState.active_legacy;
- mCurrentState.active.w = UINT32_MAX;
- mCurrentState.active.h = UINT32_MAX;
- mCurrentState.active.transform.set(0, 0);
+ mCurrentState.width = UINT32_MAX;
+ mCurrentState.height = UINT32_MAX;
+ mCurrentState.transform.set(0, 0);
mCurrentState.frameNumber = 0;
- mCurrentState.transform = 0;
+ mCurrentState.bufferTransform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
mCurrentState.acquireFence = new Fence(-1);
@@ -949,13 +949,12 @@
" requested={ wh={%4u,%4u} }}\n",
this, getName().c_str(), getBufferTransform(), getEffectiveScalingMode(),
stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
- stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
- stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
- stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(),
- stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h,
- s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top,
- s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(),
- s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
+ stateToCommit->crop.left, stateToCommit->crop.top, stateToCommit->crop.right,
+ stateToCommit->crop.bottom, stateToCommit->crop.getWidth(),
+ stateToCommit->crop.getHeight(), stateToCommit->requested_legacy.w,
+ stateToCommit->requested_legacy.h, s.active_legacy.w, s.active_legacy.h,
+ s.crop.left, s.crop.top, s.crop.right, s.crop.bottom, s.crop.getWidth(),
+ s.crop.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
}
// Don't let Layer::doTransaction update the drawing state
@@ -1058,6 +1057,15 @@
}
void Layer::commitTransaction(State& stateToCommit) {
+ if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+ mDrawingState.buffer != stateToCommit.buffer && bufferSurfaceFrame != nullptr &&
+ bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+ // If the previous buffer was committed but not latched (refreshPending - happens during
+ // back to back invalidates), it gets silently dropped here. Mark the corresponding
+ // SurfaceFrame as dropped to prevent it from getting stuck in the pending classification
+ // list.
+ addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
+ }
mDrawingState = stateToCommit;
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
@@ -1326,11 +1334,11 @@
return true;
}
-bool Layer::setCrop_legacy(const Rect& crop) {
- if (mCurrentState.requestedCrop_legacy == crop) return false;
+bool Layer::setCrop(const Rect& crop) {
+ if (mCurrentState.requestedCrop == crop) return false;
mCurrentState.sequence++;
- mCurrentState.requestedCrop_legacy = crop;
- mCurrentState.crop_legacy = crop;
+ mCurrentState.requestedCrop = crop;
+ mCurrentState.crop = crop;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -1537,6 +1545,7 @@
// Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
mCurrentState.bufferSurfaceFrameTX = it->second;
mCurrentState.bufferlessSurfaceFramesTX.erase(it);
+ mCurrentState.bufferSurfaceFrameTX->promoteToBuffer();
mCurrentState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
} else {
mCurrentState.bufferSurfaceFrameTX =
@@ -1596,7 +1605,8 @@
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
- mTransactionName);
+ mTransactionName,
+ /*isBuffer*/ false);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
surfaceFrame->setAcquireFenceTime(postTime);
@@ -1612,7 +1622,8 @@
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
- getSequence(), mName, debugName);
+ getSequence(), mName, debugName,
+ /*isBuffer*/ true);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -1732,7 +1743,7 @@
info.mZ = ds.z;
info.mWidth = ds.active_legacy.w;
info.mHeight = ds.active_legacy.h;
- info.mCrop = ds.crop_legacy;
+ info.mCrop = ds.crop;
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
@@ -2431,7 +2442,7 @@
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
- ui::Transform requestedTransform = state.active_legacy.transform;
+ ui::Transform requestedTransform = state.transform;
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
layerInfo->set_id(sequence);
@@ -2460,11 +2471,10 @@
return layerInfo->mutable_requested_position();
});
- LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+ LayerProtoHelper::writeSizeToProto(state.width, state.height,
[&]() { return layerInfo->mutable_size(); });
- LayerProtoHelper::writeToProto(state.crop_legacy,
- [&]() { return layerInfo->mutable_crop(); });
+ LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
layerInfo->set_is_opaque(isOpaque(state));
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 34a9f39..26d8e74 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -214,8 +214,8 @@
bool modified;
// Crop is expressed in layer space coordinate.
- Rect crop_legacy;
- Rect requestedCrop_legacy;
+ Rect crop;
+ Rect requestedCrop;
// If set, defers this state update until the identified Layer
// receives a frame with the given frameNumber
@@ -250,12 +250,13 @@
// The fields below this point are only used by BufferStateLayer
uint64_t frameNumber;
- Geometry active;
+ uint32_t width;
+ uint32_t height;
+ ui::Transform transform;
- uint32_t transform;
+ uint32_t bufferTransform;
bool transformToDisplayInverse;
- Rect crop;
Region transparentRegionHint;
sp<GraphicBuffer> buffer;
@@ -421,7 +422,7 @@
// space for top-level layers.
virtual bool setPosition(float x, float y);
// Buffer space
- virtual bool setCrop_legacy(const Rect& crop);
+ virtual bool setCrop(const Rect& crop);
// TODO(b/38182121): Could we eliminate the various latching modes by
// using the layer hierarchy?
@@ -460,7 +461,6 @@
// Used only to set BufferStateLayer state
virtual bool setTransform(uint32_t /*transform*/) { return false; };
virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
- virtual bool setCrop(const Rect& /*crop*/) { return false; };
virtual bool setFrame(const Rect& /*frame*/) { return false; };
virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
@@ -547,7 +547,7 @@
virtual Region getActiveTransparentRegion(const Layer::State& s) const {
return s.activeTransparentRegion_legacy;
}
- virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+ virtual Rect getCrop(const Layer::State& s) const { return s.crop; }
virtual bool needsFiltering(const DisplayDevice*) const { return false; }
// True if this layer requires filtering
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index 053b7f7..1c0263b 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -80,24 +80,23 @@
// recompute visible region
mRecomputeVisibleRegions = true;
- if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
- mFront.crop_legacy = mFront.requestedCrop_legacy;
- mCurrent.crop_legacy = mFront.requestedCrop_legacy;
+ if (mFront.crop != mFront.requestedCrop) {
+ mFront.crop = mFront.requestedCrop;
+ mCurrent.crop = mFront.requestedCrop;
mRecomputeVisibleRegions = true;
}
}
ALOGD_IF(DEBUG_RESIZE,
"[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active_legacy ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} "
+ " drawing={ active_legacy ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
"(%4d,%4d) "
"}\n"
" requested_legacy={ wh={%4u,%4u} }}\n",
mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
- mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
- mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
- mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
- mFront.requested_legacy.w, mFront.requested_legacy.h);
+ mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
+ mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
+ mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
}
if (!isFixedSize && !mStickyTransformSet) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ba43e70..6553efe 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -354,7 +354,7 @@
return mTokenManager->generateTokenForPredictions(
{timestamp, deadlineTimestamp, expectedVSyncTimestamp});
}
- return static_cast<int64_t>(0);
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
}();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
@@ -494,10 +494,16 @@
const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
const auto deadlineTimestamp = now + timeout.count();
const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
- // TODO(b/162890590): use TokenManager to populate vsyncId
+ const int64_t vsyncId = [&] {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {now, deadlineTimestamp, expectedVSyncTime});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+ }();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
++mVSyncState->count, expectedVSyncTime,
- deadlineTimestamp, /*vsyncId=*/0));
+ deadlineTimestamp, vsyncId));
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b4eb1af..d048380 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -914,11 +914,6 @@
}
info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
- if (display->isPrimary()) {
- if (const auto mode = getDesiredActiveMode()) {
- info->activeDisplayModeId = static_cast<int32_t>(mode->modeId.value());
- }
- }
const auto& supportedModes = display->getSupportedModes();
info->supportedDisplayModes.clear();
@@ -1881,7 +1876,12 @@
// underestimated.
mFrameStartTime = frameStart;
}
- signalRefresh();
+
+ // Run the refresh immediately after invalidate as there is no point going thru the message
+ // queue again, and to ensure that we actually refresh the screen instead of handling
+ // other messages that were queued us already in the MessageQueue.
+ mRefreshPending = true;
+ onMessageRefresh();
}
}
@@ -3043,9 +3043,8 @@
void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- mTransactionPending = false;
+ signalSynchronousTransactions();
mAnimTransactionPending = false;
- mTransactionCV.broadcast();
}
void SurfaceFlinger::commitTransactionLocked() {
@@ -3314,15 +3313,16 @@
auto& [applyToken, transactionQueue] = *it;
while (!transactionQueue.empty()) {
- const auto& transaction = transactionQueue.front();
+ auto& transaction = transactionQueue.front();
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states, pendingBuffers)) {
+ transaction.originUid, transaction.states,
+ pendingBuffers)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
- transactions.push_back(transaction);
+ transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
}
@@ -3339,17 +3339,18 @@
// Case 2: push to pending when there exist a pending queue.
// Case 3: others are ready to apply.
while (!mTransactionQueue.empty()) {
- const auto& transaction = mTransactionQueue.front();
+ auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states, pendingBuffers) ||
+ transaction.originUid, transaction.states,
+ pendingBuffers) ||
pendingTransactions) {
- mPendingTransactionQueues[transaction.applyToken].push(transaction);
+ mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
- transactions.push_back(transaction);
+ transactions.emplace_back(std::move(transaction));
}
mTransactionQueue.pop();
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
@@ -3365,25 +3366,36 @@
transaction.postTime, transaction.permissions,
transaction.hasListenerCallbacks, transaction.listenerCallbacks,
transaction.originPid, transaction.originUid, transaction.id);
+ if (transaction.transactionCommittedSignal) {
+ mTransactionCommittedSignals.emplace_back(
+ std::move(transaction.transactionCommittedSignal));
+ }
}
}
}
bool SurfaceFlinger::transactionFlushNeeded() {
Mutex::Autolock _l(mQueueLock);
- return !mPendingTransactionQueues.empty();
+ return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
}
bool SurfaceFlinger::transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
- const Vector<ComposerState>& states,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+ ATRACE_CALL();
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
bool ready = true;
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ ATRACE_NAME("not current");
+ ready = false;
+ }
+
+ if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+ ATRACE_NAME("!isVsyncValid");
ready = false;
}
@@ -3406,15 +3418,12 @@
continue;
}
+ ATRACE_NAME(layer->getName().c_str());
+
const bool frameTimelineInfoChanged = (s.what & layer_state_t::eFrameTimelineInfoChanged);
const auto vsyncId = frameTimelineInfoChanged ? s.frameTimelineInfo.vsyncId : info.vsyncId;
if (isAutoTimestamp && layer->frameIsEarly(expectedPresentTime, vsyncId)) {
ATRACE_NAME("frameIsEarly()");
- return false;
- }
-
- if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
- ATRACE_NAME("!isVsyncValidForUid");
ready = false;
}
@@ -3423,16 +3432,16 @@
// transaction in the queue.
const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
ready = false;
}
pendingBuffers.insert(s.surface);
}
- pendingBuffers.insert(s.surface);
}
return ready;
}
-void SurfaceFlinger::queueTransaction(TransactionState state) {
+void SurfaceFlinger::queueTransaction(TransactionState& state) {
Mutex::Autolock _l(mQueueLock);
// If its TransactionQueue already has a pending TransactionState or if it is pending
@@ -3452,20 +3461,15 @@
}
}
+ // Generate a CountDownLatch pending state if this is a synchronous transaction.
+ if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
+ state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
+ (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+ }
+
mTransactionQueue.emplace(state);
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
- // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
- if (state.flags & eEarlyWakeup) {
- ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
- }
-
- if (!(state.permissions & Permission::ACCESS_SURFACE_FLINGER) &&
- (state.flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
- state.flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
- }
-
const auto schedule = [](uint32_t flags) {
if (flags & eEarlyWakeup) return TransactionSchedule::Early;
if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
@@ -3476,28 +3480,23 @@
setTransactionFlags(eTransactionFlushNeeded, schedule);
}
-void SurfaceFlinger::waitForSynchronousTransaction(bool synchronous, bool syncInput) {
- Mutex::Autolock _l(mStateLock);
- if (synchronous) {
- mTransactionPending = true;
+void SurfaceFlinger::waitForSynchronousTransaction(
+ const CountDownLatch& transactionCommittedSignal) {
+ // applyTransactionState is called on the main SF thread. While a given process may wish
+ // to wait on synchronous transactions, the main SF thread should apply the transaction and
+ // set the value to notify this after committed.
+ if (!transactionCommittedSignal.wait_until(std::chrono::seconds(5))) {
+ ALOGE("setTransactionState timed out!");
}
- if (syncInput) {
- mPendingSyncInputWindows = true;
- }
+}
- // applyTransactionState can be called by either the main SF thread or by
- // another process through setTransactionState. While a given process may wish
- // to wait on synchronous transactions, the main SF thread should never
- // be blocked. Therefore, we only wait if isMainThread is false.
- while (mTransactionPending || mPendingSyncInputWindows) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // called after a few seconds.
- ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
- mTransactionPending = false;
- mPendingSyncInputWindows = false;
- break;
+void SurfaceFlinger::signalSynchronousTransactions() {
+ for (auto it = mTransactionCommittedSignals.begin();
+ it != mTransactionCommittedSignals.end();) {
+ if ((*it)->countDown() == 0) {
+ it = mTransactionCommittedSignals.erase(it);
+ } else {
+ it++;
}
}
}
@@ -3526,21 +3525,35 @@
permissions |= Permission::ROTATE_SURFACE_FLINGER;
}
+ // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
+ if (flags & eEarlyWakeup) {
+ ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+ }
+
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+ (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+ flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ }
+
const int64_t postTime = systemTime();
IPCThreadState* ipc = IPCThreadState::self();
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
+ TransactionState state{frameTimelineInfo, states,
+ displays, flags,
+ applyToken, inputWindowCommands,
+ desiredPresentTime, isAutoTimestamp,
+ uncacheBuffer, postTime,
+ permissions, hasListenerCallbacks,
+ listenerCallbacks, originPid,
+ originUid, transactionId};
+ queueTransaction(state);
- queueTransaction({frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands,
- desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, permissions,
- hasListenerCallbacks, listenerCallbacks, originPid, originUid,
- transactionId});
-
- const bool synchronous = flags & eSynchronous;
- const bool syncInput = inputWindowCommands.syncInputWindows;
- if (synchronous || syncInput) {
- waitForSynchronousTransaction(synchronous, syncInput);
+ // Check the pending state to make sure the transaction is synchronous.
+ if (state.transactionCommittedSignal) {
+ waitForSynchronousTransaction(*state.transactionCommittedSignal);
}
return NO_ERROR;
@@ -3838,9 +3851,6 @@
if (layer->setFlags(s.flags, s.mask))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eCropChanged_legacy) {
- if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eCornerRadiusChanged) {
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
@@ -6080,10 +6090,7 @@
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
-
- mPendingSyncInputWindows = false;
-
- mTransactionCV.broadcast();
+ signalSynchronousTransactions();
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7206d0c..a5b06df 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -474,6 +474,41 @@
hal::Connection connection = hal::Connection::INVALID;
};
+ class CountDownLatch {
+ public:
+ explicit CountDownLatch(int32_t count) : mCount(count) {}
+
+ int32_t countDown() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCount == 0) {
+ return 0;
+ }
+ if (--mCount == 0) {
+ mCountDownComplete.notify_all();
+ }
+ return mCount;
+ }
+
+ // Return true if triggered.
+ bool wait_until(const std::chrono::seconds& timeout) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ const auto untilTime = std::chrono::system_clock::now() + timeout;
+ while (mCount != 0) {
+ // Conditional variables can be woken up sporadically, so we check count
+ // to verify the wakeup was triggered by |countDown|.
+ if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ int32_t mCount;
+ mutable std::condition_variable mCountDownComplete;
+ mutable std::mutex mMutex;
+ };
+
struct TransactionState {
TransactionState(const FrameTimelineInfo& frameTimelineInfo,
const Vector<ComposerState>& composerStates,
@@ -517,6 +552,7 @@
int originPid;
int originUid;
uint64_t id;
+ std::shared_ptr<CountDownLatch> transactionCommittedSignal;
};
template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
@@ -802,7 +838,7 @@
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
- const Vector<ComposerState>& states,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1081,8 +1117,9 @@
status_t CheckTransactCodeCredentials(uint32_t code);
// Add transaction to the Transaction Queue
- void queueTransaction(TransactionState state) EXCLUDES(mQueueLock);
- void waitForSynchronousTransaction(bool synchronous, bool syncInput) EXCLUDES(mStateLock);
+ void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
+ void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
+ void signalSynchronousTransactions();
/*
* Generic Layer Metadata
@@ -1112,8 +1149,7 @@
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
std::atomic<int32_t> mTransactionFlags = 0;
- Condition mTransactionCV;
- bool mTransactionPending = false;
+ std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
bool mAnimTransactionPending = false;
SortedVector<sp<Layer>> mLayersPendingRemoval;
bool mForceTraversal = false;
@@ -1325,7 +1361,6 @@
sp<SetInputWindowsListener> mSetInputWindowsListener;
- bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
// This should only be accessed on the main thread.
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index b0413f1..8a3be9f 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -137,7 +137,7 @@
addTransparentRegionLocked(transaction, layerId,
layer->mCurrentState.activeTransparentRegion_legacy);
addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
- addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+ addCropLocked(transaction, layerId, layer->mCurrentState.crop);
addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
@@ -459,8 +459,8 @@
if (state.what & layer_state_t::eLayerStackChanged) {
addLayerStackLocked(transaction, layerId, state.layerStack);
}
- if (state.what & layer_state_t::eCropChanged_legacy) {
- addCropLocked(transaction, layerId, state.crop_legacy);
+ if (state.what & layer_state_t::eCropChanged) {
+ addCropLocked(transaction, layerId, state.crop);
}
if (state.what & layer_state_t::eCornerRadiusChanged) {
addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 7a3c45d..f470eda 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -55,7 +55,7 @@
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setCrop(effectLayer, Rect(0, 0, 400, 400));
t.show(effectLayer);
});
@@ -76,7 +76,7 @@
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setCrop(effectLayer, Rect(0, 0, 400, 400));
t.show(effectLayer);
});
@@ -97,7 +97,7 @@
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setCrop(effectLayer, Rect(0, 0, 400, 400));
t.setColor(effectLayer,
half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
Color::GREEN.b / 255.0f});
@@ -127,10 +127,10 @@
asTransaction([&](Transaction& t) {
t.setLayer(leftLayer, mLayerZBase + 1);
t.reparent(leftLayer, mParentLayer);
- t.setCrop_legacy(leftLayer, leftRect);
+ t.setCrop(leftLayer, leftRect);
t.setLayer(rightLayer, mLayerZBase + 2);
t.reparent(rightLayer, mParentLayer);
- t.setCrop_legacy(rightLayer, rightRect);
+ t.setCrop(rightLayer, rightRect);
t.show(leftLayer);
t.show(rightLayer);
});
@@ -148,7 +148,7 @@
t.setLayer(blurLayer, mLayerZBase + 3);
t.reparent(blurLayer, mParentLayer);
t.setBackgroundBlurRadius(blurLayer, blurRadius);
- t.setCrop_legacy(blurLayer, blurRect);
+ t.setCrop(blurLayer, blurRect);
t.setFrame(blurLayer, blurRect);
t.setAlpha(blurLayer, 0.0f);
t.show(blurLayer);
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index b35eaa9..7505e6e 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -515,7 +515,7 @@
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
@@ -554,7 +554,7 @@
case ISurfaceComposerClient::eFXSurfaceEffect:
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
Transaction()
- .setCrop_legacy(layer, Rect(0, 0, width, height))
+ .setCrop(layer, Rect(0, 0, width, height))
.setColor(layer, half3(1.0f, 0, 0))
.apply();
expectedColor = fillColor;
@@ -565,7 +565,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
expectedColor = fillColor;
}
- Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+ Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
@@ -727,7 +727,7 @@
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setColor(colorLayer, half3(2.0f, 0.0f, 0.0f))
.apply();
@@ -741,7 +741,7 @@
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setColor(colorLayer, half3(1.0f, -1.0f, 0.5f))
.apply();
@@ -756,7 +756,7 @@
ASSERT_NO_FATAL_FAILURE(colorLayer =
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
- Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+ Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
@@ -783,7 +783,7 @@
ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
- Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+ Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -940,7 +940,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
const Rect crop(8, 8, 24, 24);
- Transaction().setCrop_legacy(layer, crop).apply();
+ Transaction().setCrop(layer, crop).apply();
auto shot = getScreenCapture();
shot->expectColor(crop, Color::RED);
shot->expectBorder(crop, Color::BLACK);
@@ -966,13 +966,13 @@
{
SCOPED_TRACE("empty rect");
- Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+ Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
{
SCOPED_TRACE("negative rect");
- Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+ Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
@@ -1001,7 +1001,7 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+ Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
@@ -1056,7 +1056,7 @@
const Point position(32, 32);
const Rect crop(8, 8, 24, 24);
- Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+ Transaction().setPosition(layer, position.x, position.y).setCrop(layer, crop).apply();
auto shot = getScreenCapture();
shot->expectColor(crop + position, Color::RED);
shot->expectBorder(crop + position, Color::BLACK);
@@ -1081,10 +1081,10 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // crop_legacy is affected by matrix
+ // crop is affected by matrix
Transaction()
.setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+ .setCrop(layer, Rect(8, 8, 24, 24))
.apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
@@ -1096,8 +1096,8 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // setCrop_legacy is applied immediately by default, with or without resize pending
- Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+ // setCrop is applied immediately by default, with or without resize pending
+ Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
{
SCOPED_TRACE("resize pending");
auto shot = getScreenCapture();
@@ -1596,7 +1596,7 @@
createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
{
@@ -1653,8 +1653,8 @@
ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
Transaction()
- .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(parentLayer, mLayerZBase + 1)
.apply();
{
@@ -1714,8 +1714,8 @@
ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
Transaction()
- .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(parentLayer, mLayerZBase + 1)
.apply();
{
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index be6665b..87c7b7d 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -298,7 +298,7 @@
// set layer stack (b/68888219)
Transaction t;
t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
- t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+ t.setCrop(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
t.setColor(mBlackBgSurface, half3{0, 0, 0});
t.setLayer(mBlackBgSurface, mLayerZBase);
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 67db717..ac5e297 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -145,7 +145,7 @@
sp<SurfaceControl> parent =
LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
- Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+ Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -199,7 +199,7 @@
if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
Transaction()
.setCornerRadius(layer, cornerRadius)
- .setCrop_legacy(layer, Rect(0, 0, size, size))
+ .setCrop(layer, Rect(0, 0, size, size))
.apply();
} else {
Transaction()
@@ -236,13 +236,13 @@
auto transaction = Transaction()
.setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .setCrop(parent, Rect(0, 0, size, size))
.reparent(child, parent)
.setPosition(child, 0, size)
// Rotate by half PI
.setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
- transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+ transaction.setCrop(parent, Rect(0, 0, size, size));
} else {
transaction.setFrame(parent, Rect(0, 0, size, size));
}
@@ -278,7 +278,7 @@
if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
Transaction()
.setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .setCrop(parent, Rect(0, 0, size, size))
.reparent(child, parent)
.setPosition(child, 0, size / 2)
.apply();
@@ -351,7 +351,7 @@
Transaction()
.setLayer(blurLayer, mLayerZBase + 3)
.setBackgroundBlurRadius(blurLayer, blurRadius)
- .setCrop_legacy(blurLayer, blurRect)
+ .setCrop(blurLayer, blurRect)
.setFrame(blurLayer, blurRect)
.setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
.setAlpha(blurLayer, 0.0f)
@@ -516,7 +516,7 @@
.setLayer(layer, INT32_MAX - 1)
.show(layer)
.setLayerStack(behindLayer, mDisplayLayerStack)
- .setCrop_legacy(behindLayer, crop)
+ .setCrop(behindLayer, crop)
.setLayer(behindLayer, INT32_MAX - 2)
.show(behindLayer)
.apply();
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index f8a0bc1..4753362 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -43,7 +43,7 @@
sp<SurfaceControl> parent =
LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
- Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+ Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
sp<SurfaceControl> layerB;
@@ -84,8 +84,8 @@
.setColor(parent, half3{0.0f, 0.0f, 0.0f})
.show(childLayer)
.show(parent)
- .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+ .setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setCrop(childLayer, Rect(0, 0, 20, 30))
.apply();
Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply();
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ec826ae..e4a1f66 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -131,7 +131,7 @@
asTransaction([&](Transaction& t) {
t.setSize(mFGSurfaceControl, 64, 64);
t.setPosition(mFGSurfaceControl, 64, 64);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+ t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
});
EXPECT_INITIAL_STATE("After restoring initial state");
@@ -225,7 +225,7 @@
PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
SurfaceComposerClient::Transaction{}
- .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+ .setCrop(childNoBuffer, Rect(0, 0, 10, 10))
.show(childNoBuffer)
.show(childBuffer)
.apply(true);
@@ -234,9 +234,7 @@
sc->expectChildColor(73, 73);
sc->expectFGColor(74, 74);
}
- SurfaceComposerClient::Transaction{}
- .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
- .apply(true);
+ SurfaceComposerClient::Transaction{}.setCrop(childNoBuffer, Rect(0, 0, 20, 20)).apply(true);
{
ScreenCapture::captureScreen(&sc);
sc->expectChildColor(73, 73);
@@ -351,7 +349,7 @@
t.show(mChild);
t.setPosition(mChild, 0, 0);
t.setPosition(mFGSurfaceControl, 0, 0);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+ t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
});
{
@@ -947,7 +945,7 @@
ISurfaceComposerClient::eFXSurfaceEffect, cropLayer.get());
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
- t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+ t.setCrop(cropLayer, Rect(5, 5, 10, 10));
t.setColor(colorLayer, half3{0, 0, 0});
t.show(cropLayer);
t.show(colorLayer);
@@ -1007,7 +1005,7 @@
t.show(boundlessLayerRightShift);
t.setPosition(boundlessLayerDownShift, 0, 32);
t.show(boundlessLayerDownShift);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setCrop(colorLayer, Rect(0, 0, 64, 64));
t.setColor(colorLayer, half3{0, 0, 0});
t.show(colorLayer);
});
@@ -1043,7 +1041,7 @@
// expect the child layer to be cropped.
t.setPosition(boundlessLayer, 32, 32);
t.show(boundlessLayer);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setCrop(colorLayer, Rect(0, 0, 64, 64));
// undo shift by parent
t.setPosition(colorLayer, -32, -32);
t.setColor(colorLayer, half3{0, 0, 0});
@@ -1074,7 +1072,7 @@
t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
t.setPosition(rootBoundlessLayer, 32, 32);
t.show(rootBoundlessLayer);
- t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setCrop(colorLayer, Rect(0, 0, 64, 64));
t.setColor(colorLayer, half3{0, 0, 0});
t.show(colorLayer);
t.hide(mFGSurfaceControl);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 16826c1..613b21e 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
asTransaction([&](Transaction& t) {
t.setDisplayLayerStack(display, 0);
t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
- t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+ t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
t.setPosition(mChildLayer, 50, 50);
t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
@@ -58,7 +58,7 @@
createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
Transaction()
.setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
- .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+ .setCrop(grandchild, Rect(0, 0, 200, 200))
.show(grandchild)
.apply();
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index eaf54e3..08de01c 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -71,7 +71,7 @@
ASSERT_TRUE(mColorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setLayerStack(mColorLayer, layerStack);
- t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+ t.setCrop(mColorLayer, Rect(0, 0, 30, 40));
t.setLayer(mColorLayer, INT32_MAX - 2);
t.setColor(mColorLayer,
half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 4598f9d..b0753c8 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -328,7 +328,7 @@
TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
Rect layerCrop(0, 0, 10, 10);
- SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+ SurfaceComposerClient::Transaction().setCrop(child, layerCrop).show(child).apply(true);
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = child->getHandle();
@@ -623,7 +623,7 @@
.setLayerStack(layer, 0)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
- .setCrop_legacy(layer, bounds)
+ .setCrop(layer, bounds)
.apply();
DisplayCaptureArgs captureArgs;
@@ -671,8 +671,8 @@
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
- .setCrop_legacy(layer, bounds)
- .setCrop_legacy(childLayer, childBounds)
+ .setCrop(layer, bounds)
+ .setCrop(childLayer, childBounds)
.apply();
DisplayCaptureArgs captureArgs;
@@ -853,9 +853,7 @@
}
TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
- SurfaceComposerClient::Transaction()
- .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
- .apply(true);
+ SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
// Even though the parent is cropped out we should still capture the child.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a20d5c6..d9cab42 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -370,7 +370,7 @@
}
void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
- t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
+ t.setCrop(mBGSurfaceControl, CROP_UPDATE);
}
void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 11bd9eb..820f248 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1324,7 +1324,7 @@
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
- ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+ ts.setCrop(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
@@ -1692,7 +1692,7 @@
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
+ ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
}
// NOTE: The foreground surface would be occluded by the child
// now, but is included in the stack because the child is
@@ -1915,7 +1915,7 @@
TransactionScope ts(*Base::sFakeComposer);
ts.setColor(Base::mChild,
{LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
- ts.setCrop_legacy(Base::mChild, Rect(0, 0, 10, 10));
+ ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
}
Base::sFakeComposer->runVSyncAndWait();
@@ -2010,7 +2010,7 @@
TransactionScope ts(*Base::sFakeComposer);
ts.setSize(Base::mFGSurfaceControl, 64, 64);
ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
+ ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
}
void Test_SurfacePositionLatching() {
@@ -2043,7 +2043,7 @@
{
TransactionScope ts(*Base::sFakeComposer);
ts.setSize(Base::mFGSurfaceControl, 128, 128);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+ ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
}
auto referenceFrame1 = Base::mBaseFrame;
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6bc2318..6a7ec9b 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -31,7 +31,8 @@
class SlotGenerationTest : public testing::Test {
protected:
- BufferStateLayer::HwcSlotGenerator mHwcSlotGenerator;
+ sp<BufferStateLayer::HwcSlotGenerator> mHwcSlotGenerator =
+ sp<BufferStateLayer::HwcSlotGenerator>::make();
sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
@@ -41,7 +42,7 @@
sp<IBinder> binder = new BBinder();
// test getting invalid client_cache_id
client_cache_t id;
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
}
@@ -50,19 +51,19 @@
client_cache_t id;
id.token = binder;
id.id = 0;
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
client_cache_t idB;
idB.token = binder;
idB.id = 1;
- slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+ slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
- slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+ slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
- slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
}
@@ -77,12 +78,12 @@
id.id = cacheId;
ids.push_back(id);
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
cacheId++;
}
for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(ids[i]);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
}
@@ -90,7 +91,7 @@
client_cache_t id;
id.token = binder;
id.id = cacheId;
- uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
cacheId++;
}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 256be27..4e1c0c7 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -836,8 +836,8 @@
static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
- layerDrawingState.active.w = 100;
- layerDrawingState.active.h = 100;
+ layerDrawingState.width = 100;
+ layerDrawingState.height = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
@@ -884,7 +884,7 @@
});
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
return layer;
}
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 81efe0b..d1385c0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -193,10 +193,12 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
@@ -204,7 +206,8 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -213,7 +216,8 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -222,7 +226,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -233,7 +238,8 @@
constexpr int32_t inputEventId = 1;
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -243,7 +249,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -271,11 +278,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdTwo, sLayerNameTwo,
- sLayerNameTwo);
+ sLayerNameTwo, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -317,7 +324,8 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -339,7 +347,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -353,18 +361,20 @@
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -378,7 +388,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -394,7 +405,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -410,7 +422,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -430,7 +443,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -458,7 +471,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -484,7 +497,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -508,7 +521,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setAcquireFenceTime(20);
@@ -532,7 +545,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -558,7 +571,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -584,7 +597,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -610,7 +623,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
@@ -637,7 +650,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -665,7 +678,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
flushTokens(systemTime() + maxTokenRetentionTime);
@@ -701,7 +714,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -727,7 +741,8 @@
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -772,7 +787,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1085,11 +1101,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1245,7 +1261,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
@@ -1315,7 +1331,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1493,7 +1509,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1513,7 +1529,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1573,7 +1589,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1593,7 +1609,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1652,7 +1668,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1696,7 +1712,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1716,7 +1732,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1770,7 +1786,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1790,7 +1806,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1848,7 +1864,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1868,7 +1884,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(80);
mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
// Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
@@ -1923,7 +1939,8 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1938,7 +1955,8 @@
const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1947,7 +1965,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1962,7 +1981,8 @@
const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1971,7 +1991,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1986,7 +2007,8 @@
const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1995,7 +2017,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2013,7 +2036,8 @@
const auto sixHundredMs = std::chrono::nanoseconds(600ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2022,7 +2046,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2031,7 +2056,8 @@
auto surfaceFrame3 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame3);
@@ -2040,7 +2066,8 @@
auto surfaceFrame4 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame4);
@@ -2049,7 +2076,8 @@
auto surfaceFrame5 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
// Dropped frames will be excluded from fps computation
surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e46a270..38e503f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -64,7 +64,7 @@
}
} mExpectDisableVsync{mSchedulerCallback};
- TestableScheduler mScheduler{mConfigs, mSchedulerCallback};
+ TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
@@ -85,8 +85,10 @@
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
+ mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
+
+ mFlinger.resetScheduler(mScheduler);
}
} // namespace
@@ -94,85 +96,84 @@
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- const sp<IDisplayEventConnection> connection = mScheduler.createDisplayEventConnection(handle);
+ const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler.getEventConnection(handle));
+ EXPECT_FALSE(mScheduler->getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
+ mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- mScheduler.onScreenAcquired(handle);
+ mScheduler->onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- mScheduler.onScreenReleased(handle);
+ mScheduler->onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- mScheduler.dump(handle, output);
+ mScheduler->dump(handle, output);
EXPECT_TRUE(output.empty());
EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
- mScheduler.setDuration(handle, 10ns, 20ns);
+ mScheduler->setDuration(handle, 10ns, 20ns);
}
TEST_F(SchedulerTest, validConnectionHandle) {
const sp<IDisplayEventConnection> connection =
- mScheduler.createDisplayEventConnection(mConnectionHandle);
+ mScheduler->createDisplayEventConnection(mConnectionHandle);
ASSERT_EQ(mEventThreadConnection, connection);
- EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
+ EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
+ mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- mScheduler.onScreenAcquired(mConnectionHandle);
+ mScheduler->onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- mScheduler.onScreenReleased(mConnectionHandle);
+ mScheduler->onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- mScheduler.dump(mConnectionHandle, output);
+ mScheduler->dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
- mScheduler.setDuration(mConnectionHandle, 10ns, 20ns);
+ mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
static constexpr size_t kEventConnections = 5;
EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
+ EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
TEST_F(SchedulerTest, noLayerHistory) {
// Layer history should not be created if there is a single config.
- ASSERT_FALSE(mScheduler.hasLayerHistory());
+ ASSERT_FALSE(mScheduler->hasLayerHistory());
- TestableSurfaceFlinger flinger;
- mock::MockLayer layer(flinger.flinger());
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
// Content detection should be no-op.
- mScheduler.registerLayer(&layer);
- mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->registerLayer(layer.get());
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
constexpr bool kPowerStateNormal = true;
- mScheduler.setDisplayPowerState(kPowerStateNormal);
+ mScheduler->setDisplayPowerState(kPowerStateNormal);
constexpr uint32_t kDisplayArea = 999'999;
- mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+ mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
- mScheduler.chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent();
}
TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
// If the optional fields are cleared, the function should return before
// onModeChange is called.
- mScheduler.clearOptionalFieldsInFeatures();
- EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedMode());
+ mScheduler->clearOptionalFieldsInFeatures();
+ EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
}
@@ -183,9 +184,9 @@
// If the handle is incorrect, the function should return before
// onModeChange is called.
Scheduler::ConnectionHandle invalidHandle = {.id = 123};
- EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayModeChanged(invalidHandle,
- PHYSICAL_DISPLAY_ID, modeId,
- vsyncPeriod));
+ EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+ PHYSICAL_DISPLAY_ID, modeId,
+ vsyncPeriod));
EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 363bd80..623a5e0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -116,6 +116,7 @@
const auto surfaceFrame = layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
commitTransaction(layer.get());
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(false, surfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
}
@@ -139,6 +140,7 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
}
@@ -172,12 +174,14 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, droppedSurfaceFrame->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame->getPresentState());
EXPECT_EQ(0u, droppedSurfaceFrame->getActuals().endTime);
auto dropTime = droppedSurfaceFrame->getDropTime();
EXPECT_TRUE(dropTime > start && dropTime < end);
EXPECT_EQ(1, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
}
@@ -204,6 +208,7 @@
commitTransaction(layer.get());
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
@@ -260,12 +265,15 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(4, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(3, bufferSurfaceFrameTX->getToken());
+ EXPECT_EQ(true, bufferSurfaceFrameTX->getIsBuffer());
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
@@ -297,10 +305,12 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(10, bufferlessSurfaceFrame1->getActuals().endTime);
EXPECT_EQ(2, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
}
@@ -327,9 +337,11 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Unknown, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(1, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
}
@@ -410,20 +422,85 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame1->getPresentState());
EXPECT_EQ(0u, droppedSurfaceFrame1->getActuals().endTime);
auto dropTime1 = droppedSurfaceFrame1->getDropTime();
EXPECT_TRUE(dropTime1 > dropStartTime1 && dropTime1 < dropEndTime1);
EXPECT_EQ(FrameTimelineInfo::INVALID_VSYNC_ID, droppedSurfaceFrame2->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame2->getPresentState());
EXPECT_EQ(0u, droppedSurfaceFrame2->getActuals().endTime);
auto dropTime2 = droppedSurfaceFrame2->getDropTime();
EXPECT_TRUE(dropTime2 > dropStartTime2 && dropTime2 < dropEndTime2);
EXPECT_EQ(2, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
}
+
+ void MultipleCommitsBeforeLatch() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ uint32_t surfaceFramesPendingClassification = 0;
+ std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
+ for (int i = 0; i < 10; i += 2) {
+ sp<Fence> fence1(new Fence());
+ sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
+ /*inputEventId*/ 0},
+ 10);
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ auto& bufferlessSurfaceFrame =
+ layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2);
+ bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame);
+
+ commitTransaction(layer.get());
+ surfaceFramesPendingClassification += 2;
+ EXPECT_EQ(surfaceFramesPendingClassification,
+ layer->mPendingJankClassifications.size());
+ }
+
+ auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
+ // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
+ for (auto& surfaceFrame : bufferlessSurfaceFrames) {
+ surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+ }
+ presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0,
+ /*displayPresentDelta*/ 0);
+
+ // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame
+ ASSERT_EQ(10u, surfaceFramesPendingClassification);
+ ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size());
+
+ // For the frames upto 8, the bufferSurfaceFrame should have been dropped while the
+ // bufferlessSurfaceFrame presented
+ for (uint32_t i = 0; i < 8; i += 2) {
+ auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i];
+ auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1];
+ EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped);
+ EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+ }
+ {
+ auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u];
+ auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u];
+ EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented);
+ EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+ }
+
+ layer->releasePendingBuffer(25);
+
+ // There shouldn't be any pending classifications. Everything should have been cleared.
+ EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
+ }
};
TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) {
@@ -469,4 +546,8 @@
BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
}
+TEST_F(TransactionSurfaceFrameTest, MultipleCommitsBeforeLatch) {
+ MultipleCommitsBeforeLatch();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 7efd730..ddaa5a1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -85,6 +85,7 @@
void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
ASSERT_NE(nullptr, mOutBuffer);
+ ASSERT_NE(nullptr, mPixels);
ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
}
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 537e49b..2de2e0e 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -209,6 +209,18 @@
return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
}
+HalResult<float> HalController::getResonantFrequency() {
+ hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getResonantFrequency();
+ };
+ return apply(getResonantFrequencyFn, "getResonantFrequency");
+}
+
+HalResult<float> HalController::getQFactor() {
+ hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
+ return apply(getQFactorFn, "getQFactor");
+}
+
HalResult<milliseconds> HalController::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 6faab38..3d20fa1 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -205,6 +205,17 @@
mSupportedPrimitives);
}
+HalResult<float> AidlHalWrapper::getResonantFrequency() {
+ std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
+ return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
+ mResonantFrequency);
+}
+
+HalResult<float> AidlHalWrapper::getQFactor() {
+ std::lock_guard<std::mutex> lock(mQFactorMutex);
+ return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
+}
+
HalResult<milliseconds> AidlHalWrapper::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
HalResult<Capabilities> capabilities = getCapabilities();
@@ -283,6 +294,18 @@
return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
}
+HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
+ float f0 = 0;
+ auto result = getHal()->getResonantFrequency(&f0);
+ return HalResult<float>::fromStatus(result, f0);
+}
+
+HalResult<float> AidlHalWrapper::getQFactorInternal() {
+ float qFactor = 0;
+ auto result = getHal()->getQFactor(&qFactor);
+ return HalResult<float>::fromStatus(result, qFactor);
+}
+
sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
@@ -366,6 +389,18 @@
}
template <typename I>
+HalResult<float> HidlHalWrapper<I>::getResonantFrequency() {
+ ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
+ return HalResult<float>::unsupported();
+}
+
+template <typename I>
+HalResult<float> HidlHalWrapper<I>::getQFactor() {
+ ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
+ return HalResult<float>::unsupported();
+}
+
+template <typename I>
HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
const std::vector<CompositeEffect>&, const std::function<void()>&) {
ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 16d571d..14ec7b2 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -73,6 +73,9 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
final override;
+ HalResult<float> getResonantFrequency() final override;
+ HalResult<float> getQFactor() final override;
+
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) final override;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index e22ad34..039a2d9 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -185,6 +185,9 @@
virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
getSupportedPrimitives() = 0;
+ virtual HalResult<float> getResonantFrequency() = 0;
+ virtual HalResult<float> getQFactor() = 0;
+
virtual HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) = 0;
@@ -232,6 +235,9 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
override final;
+ HalResult<float> getResonantFrequency() override final;
+ HalResult<float> getQFactor() override final;
+
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
@@ -246,6 +252,8 @@
std::mutex mCapabilitiesMutex;
std::mutex mSupportedEffectsMutex;
std::mutex mSupportedPrimitivesMutex;
+ std::mutex mResonantFrequencyMutex;
+ std::mutex mQFactorMutex;
sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
@@ -254,6 +262,8 @@
GUARDED_BY(mSupportedPrimitivesMutex);
std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
GUARDED_BY(mSupportedPrimitivesMutex);
+ std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
+ std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
// Loads and caches from IVibrator.
HalResult<std::chrono::milliseconds> getPrimitiveDuration(
@@ -263,6 +273,10 @@
HalResult<Capabilities> getCapabilitiesInternal();
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
+
+ HalResult<float> getResonantFrequencyInternal();
+ HalResult<float> getQFactorInternal();
+
sp<hardware::vibrator::IVibrator> getHal();
};
@@ -293,6 +307,9 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
override final;
+ HalResult<float> getResonantFrequency() override final;
+ HalResult<float> getQFactor() override final;
+
HalResult<std::chrono::milliseconds> performComposedEffect(
const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
const std::function<void()>& completionCallback) override final;
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index c4b39ed..0c39247 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -67,6 +67,10 @@
MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
(override));
+
+ MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override));
+ MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
+
MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
(Effect effect, EffectStrength strength,
const std::function<void()>& completionCallback),
@@ -106,6 +110,8 @@
vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
vibrator::HalResult<std::vector<Effect>> effectsResult,
vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
+ vibrator::HalResult<float> resonantFrequencyResult,
+ vibrator::HalResult<float> qFactorResult,
vibrator::HalResult<milliseconds> durationResult) {
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(cardinality))
@@ -138,6 +144,12 @@
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
.Times(Exactly(cardinality))
.WillRepeatedly(Return(primitivesResult));
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(resonantFrequencyResult));
+ EXPECT_CALL(*mMockHal.get(), getQFactor())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(qFactorResult));
EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
.Times(Exactly(cardinality))
.WillRepeatedly(Return(durationResult));
@@ -147,7 +159,7 @@
if (cardinality > 1) {
// One reconnection call after each failure.
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
}
}
};
@@ -164,23 +176,21 @@
}
TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
- std::vector<Effect> effects;
- effects.push_back(Effect::CLICK);
- effects.push_back(Effect::TICK);
- std::vector<CompositePrimitive> primitives;
- primitives.push_back(CompositePrimitive::CLICK);
- primitives.push_back(CompositePrimitive::THUD);
- std::vector<CompositeEffect> compositeEffects;
- compositeEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
- compositeEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+ std::vector<Effect> effects = {Effect::CLICK, Effect::TICK};
+ std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
+ CompositePrimitive::THUD};
+ constexpr float F0 = 123.f;
+ constexpr float Q_FACTOR = 12.f;
+ const std::vector<CompositeEffect> compositeEffects =
+ {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)};
setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
vibrator::HalResult<vibrator::Capabilities>::ok(
vibrator::Capabilities::ON_CALLBACK),
vibrator::HalResult<std::vector<Effect>>::ok(effects),
vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
+ vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
vibrator::HalResult<milliseconds>::ok(100ms));
ASSERT_TRUE(mController->ping().isOk());
@@ -203,6 +213,14 @@
ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
+ auto getResonantFrequencyResult = mController->getResonantFrequency();
+ ASSERT_TRUE(getResonantFrequencyResult.isOk());
+ ASSERT_EQ(F0, getResonantFrequencyResult.value());
+
+ auto getQFactorResult = mController->getQFactor();
+ ASSERT_TRUE(getQFactorResult.isOk());
+ ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
+
auto performEffectResult =
mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
ASSERT_TRUE(performEffectResult.isOk());
@@ -222,6 +240,8 @@
vibrator::HalResult<vibrator::Capabilities>::unsupported(),
vibrator::HalResult<std::vector<Effect>>::unsupported(),
vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
+ vibrator::HalResult<float>::unsupported(),
+ vibrator::HalResult<float>::unsupported(),
vibrator::HalResult<milliseconds>::unsupported());
ASSERT_EQ(0, mConnectCounter);
@@ -237,6 +257,8 @@
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+ ASSERT_TRUE(mController->getQFactor().isUnsupported());
ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
.isUnsupported());
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
@@ -251,6 +273,8 @@
vibrator::HalResult<vibrator::Capabilities>::failed("message"),
vibrator::HalResult<std::vector<Effect>>::failed("message"),
vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+ vibrator::HalResult<float>::failed("message"),
+ vibrator::HalResult<float>::failed("message"),
vibrator::HalResult<milliseconds>::failed("message"));
ASSERT_EQ(0, mConnectCounter);
@@ -265,6 +289,8 @@
ASSERT_TRUE(mController->getCapabilities().isFailed());
ASSERT_TRUE(mController->getSupportedEffects().isFailed());
ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
+ ASSERT_TRUE(mController->getResonantFrequency().isFailed());
+ ASSERT_TRUE(mController->getQFactor().isFailed());
ASSERT_TRUE(
mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
ASSERT_TRUE(
@@ -327,13 +353,15 @@
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+ ASSERT_TRUE(mController->getQFactor().isUnsupported());
ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
.isUnsupported());
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
.isUnsupported());
// One connection attempt per api call.
- ASSERT_EQ(13, mConnectCounter);
+ ASSERT_EQ(15, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 8b5caa5..5d77595 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -413,6 +413,82 @@
ASSERT_EQ(supportedPrimitives, result.value());
}
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
+ constexpr float F0 = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
+ ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
+
+ auto result = mWrapper->getResonantFrequency();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
+ constexpr float F0 = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getResonantFrequency();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(F0, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getResonantFrequency();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
+ constexpr float Q_FACTOR = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
+ ASSERT_TRUE(mWrapper->getQFactor().isFailed());
+
+ auto result = mWrapper->getQFactor();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(Q_FACTOR, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
+ constexpr float Q_FACTOR = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getQFactor();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(Q_FACTOR, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getQFactor();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(Q_FACTOR, result.value());
+}
+
TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
{
InSequence seq;
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index a513239..438e5dd 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -842,6 +842,8 @@
bool ret = true;
switch (device->properties.apiVersion ^
VK_VERSION_PATCH(device->properties.apiVersion)) {
+ case VK_API_VERSION_1_2:
+ FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_1:
ret &=
visitor->Visit("subgroupProperties", &device->subgroup_properties) &&
@@ -896,6 +898,8 @@
inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
bool ret = true;
switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) {
+ case VK_API_VERSION_1_2:
+ FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_1:
ret &= visitor->Visit("deviceGroups", &instance->device_groups);
FALLTHROUGH_INTENDED;