SurfaceFlinger: Add TrustedPresentationListener API
TrustedPresentationListener is intended to allow the producer
of a buffer layer to gain a trusted signal on whether and to
what extent a layer is presented. A strawman design would be to
provide the producer details on it's presentation (alpha,
position, scale, final crop, covered region, etc). In the strawman
design the client end would then decide itself whether each of these
parameters were in an acceptable range.
However providing the client feedback on it's per frame position would have
a negative system health impact. Furthermore in some of the target use cases
we can't even be sure the layer of interest is actively producing buffers
and so there may be no existing callback to piggy-back on.
Because of this we use a server side thresholding approach, where the client
expresses some visibility threshold and a time stability constraint. See
SurfaceComposerClient.h comment for details on these thresholds and their
computation.
Bug: 256993331
Test: LayerTrustedPresentationListener_test.cpp
Change-Id: If4bef60dc6b22ce4959c353fa2a19b0994a00f5c
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index de9ea04..6197e9b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -250,6 +250,10 @@
if (mHadClonedChild) {
mFlinger->mNumClones--;
}
+ if (hasTrustedPresentationListener()) {
+ mFlinger->mNumTrustedPresentationListeners--;
+ updateTrustedPresentationState(nullptr, -1 /* time_in_ms */, true /* leaveState*/);
+ }
}
// ---------------------------------------------------------------------------
@@ -279,6 +283,7 @@
mRemovedFromDrawingState = true;
mFlinger->mScheduler->deregisterLayer(this);
}
+ updateTrustedPresentationState(nullptr, -1 /* time_in_ms */, true /* leaveState*/);
mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this));
}
@@ -376,6 +381,92 @@
return reduce(mBounds, activeTransparentRegion);
}
+// No early returns.
+void Layer::updateTrustedPresentationState(const DisplayDevice* display, int64_t time_in_ms,
+ bool leaveState) {
+ if (!hasTrustedPresentationListener()) {
+ return;
+ }
+ const bool lastState = mLastComputedTrustedPresentationState;
+ mLastComputedTrustedPresentationState = false;
+
+ if (!leaveState) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer != nullptr) {
+ mLastComputedTrustedPresentationState =
+ computeTrustedPresentationState(mBounds, mSourceBounds,
+ outputLayer->getState().coveredRegion,
+ mScreenBounds, getCompositionState()->alpha,
+ getCompositionState()->geomLayerTransform,
+ mTrustedPresentationThresholds);
+ }
+ }
+ const bool newState = mLastComputedTrustedPresentationState;
+ if (lastState && !newState) {
+ // We were in the trusted presentation state, but now we left it,
+ // emit the callback if needed
+ if (mLastReportedTrustedPresentationState) {
+ mLastReportedTrustedPresentationState = false;
+ mTrustedPresentationListener.invoke(false);
+ }
+ // Reset the timer
+ mEnteredTrustedPresentationStateTime = -1;
+ } else if (!lastState && newState) {
+ // We were not in the trusted presentation state, but we entered it, begin the timer
+ // and make sure this gets called at least once more!
+ mEnteredTrustedPresentationStateTime = time_in_ms;
+ mFlinger->forceFutureUpdate(mTrustedPresentationThresholds.stabilityRequirementMs * 1.5);
+ }
+
+ // Has the timer elapsed, but we are still in the state? Emit a callback if needed
+ if (!mLastReportedTrustedPresentationState && newState &&
+ (time_in_ms - mEnteredTrustedPresentationStateTime >
+ mTrustedPresentationThresholds.stabilityRequirementMs)) {
+ mLastReportedTrustedPresentationState = true;
+ mTrustedPresentationListener.invoke(true);
+ }
+}
+
+/**
+ * See SurfaceComposerClient.h: setTrustedPresentationCallback for discussion
+ * of how the parameters and thresholds are interpreted. The general spirit is
+ * to produce an upper bound on the amount of the buffer which was presented.
+ */
+bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds,
+ const Region& coveredRegion,
+ const FloatRect& screenBounds, float alpha,
+ const ui::Transform& effectiveTransform,
+ const TrustedPresentationThresholds& thresholds) {
+ if (alpha < thresholds.minAlpha) {
+ return false;
+ }
+ if (sourceBounds.getWidth() == 0 || sourceBounds.getHeight() == 0) {
+ return false;
+ }
+ if (screenBounds.getWidth() == 0 || screenBounds.getHeight() == 0) {
+ return false;
+ }
+
+ const float sx = effectiveTransform.dsdx();
+ const float sy = effectiveTransform.dsdy();
+ float fractionRendered = std::min(sx * sy, 1.0f);
+
+ float boundsOverSourceW = bounds.getWidth() / (float)sourceBounds.getWidth();
+ float boundsOverSourceH = bounds.getHeight() / (float)sourceBounds.getHeight();
+ fractionRendered *= boundsOverSourceW * boundsOverSourceH;
+
+ Rect coveredBounds = coveredRegion.bounds();
+ fractionRendered *= (1 -
+ ((coveredBounds.width() / (float)screenBounds.getWidth()) *
+ coveredBounds.height() / (float)screenBounds.getHeight()));
+
+ if (fractionRendered < thresholds.minFractionRendered) {
+ return false;
+ }
+
+ return true;
+}
+
void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
float parentShadowRadius) {
const State& s(getDrawingState());
@@ -3988,6 +4079,19 @@
return *this;
}
+void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+ TrustedPresentationListener const& listener) {
+ bool hadTrustedPresentationListener = hasTrustedPresentationListener();
+ mTrustedPresentationListener = listener;
+ mTrustedPresentationThresholds = thresholds;
+ bool haveTrustedPresentationListener = hasTrustedPresentationListener();
+ if (!hadTrustedPresentationListener && haveTrustedPresentationListener) {
+ mFlinger->mNumTrustedPresentationListeners++;
+ } else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) {
+ mFlinger->mNumTrustedPresentationListeners--;
+ }
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {