SF: More frontend fixes
- Crop mirrored layers with the mirror root. Previously we were
cropping the layers with the original layer and not the clone.
- Explicitly make unreachable nodes invisible. Fixes an issue where
we accessed stale snapshots that were no longer reachable.
- Update buffer geometry (inverseTransformDisplay) when display state
changes.
- Blur fixes.
Test: presubmit
Bug: 238781169
Change-Id: I6f88f2456c3fd15c9d819ec2272aee639badcd19
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index a4fac1c..afe557e 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "LayerHierarchy"
#include "LayerHierarchy.h"
+#include "LayerLog.h"
#include "SwapErase.h"
namespace android::surfaceflinger::frontend {
@@ -259,6 +260,7 @@
}
void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+ LLOGV(layer->id, "");
LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
if (!hierarchy) {
// Layer was never part of the hierarchy if it was created and destroyed in the same
@@ -408,29 +410,32 @@
if (id == UNASSIGNED_LAYER_ID) {
return "TraversalPath{ROOT}";
}
- std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+ std::stringstream ss;
+ ss << "TraversalPath{.id = " << id;
- if (!mirrorRootIds.empty()) {
- debugString += ", .mirrorRootIds=";
- for (auto rootId : mirrorRootIds) {
- debugString += std::to_string(rootId) + ",";
- }
+ if (mirrorRootId != UNASSIGNED_LAYER_ID) {
+ ss << ", .mirrorRootId=" << mirrorRootId;
}
if (!relativeRootIds.empty()) {
- debugString += ", .relativeRootIds=";
+ ss << ", .relativeRootIds=";
for (auto rootId : relativeRootIds) {
- debugString += std::to_string(rootId) + ",";
+ ss << rootId << ",";
}
}
if (hasRelZLoop()) {
- debugString += ", hasRelZLoop=true invalidRelativeRootId=";
- debugString += std::to_string(invalidRelativeRootId) + ",";
+ ss << "hasRelZLoop=true invalidRelativeRootId=" << invalidRelativeRootId << ",";
}
+ ss << "}";
+ return ss.str();
+}
- debugString += "}";
- return debugString;
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::getMirrorRoot() const {
+ LOG_ALWAYS_FATAL_IF(!isClone(), "Cannot get mirror root of a non cloned node");
+ TraversalPath mirrorRootPath = *this;
+ mirrorRootPath.id = mirrorRootId;
+ return mirrorRootPath;
}
// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
@@ -438,16 +443,13 @@
LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
uint32_t layerId,
LayerHierarchy::Variant variant)
- : mTraversalPath(traversalPath),
- mParentId(traversalPath.id),
- mParentVariant(traversalPath.variant),
- mParentDetached(traversalPath.detached) {
+ : mTraversalPath(traversalPath), mParentPath(traversalPath) {
// Update the traversal id with the child layer id and variant. Parent id and variant are
// stored to reset the id upon destruction.
traversalPath.id = layerId;
traversalPath.variant = variant;
if (variant == LayerHierarchy::Variant::Mirror) {
- traversalPath.mirrorRootIds.emplace_back(layerId);
+ traversalPath.mirrorRootId = layerId;
} else if (variant == LayerHierarchy::Variant::Relative) {
if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
layerId) != traversalPath.relativeRootIds.end()) {
@@ -462,16 +464,16 @@
// Reset the traversal id to its original parent state using the state that was saved in
// the constructor.
if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
- mTraversalPath.mirrorRootIds.pop_back();
+ mTraversalPath.mirrorRootId = mParentPath.mirrorRootId;
} else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
mTraversalPath.relativeRootIds.pop_back();
}
if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
}
- mTraversalPath.id = mParentId;
- mTraversalPath.variant = mParentVariant;
- mTraversalPath.detached = mParentDetached;
+ mTraversalPath.id = mParentPath.id;
+ mTraversalPath.variant = mParentPath.variant;
+ mTraversalPath.detached = mParentPath.detached;
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index ca8d301..2ab897b 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -50,12 +50,32 @@
ftl_last = Mirror,
};
// Represents a unique path to a node.
+ // The layer hierarchy is represented as a graph. Each node can be visited by multiple parents.
+ // This allows us to represent mirroring in an efficient way. See the example below:
+ // root
+ // ├─ A {Traversal path id = 1}
+ // ├─ B {Traversal path id = 2}
+ // │ ├─ C {Traversal path id = 3}
+ // │ ├─ D {Traversal path id = 4}
+ // │ └─ E {Traversal path id = 5}
+ // ├─ F (Mirrors B) {Traversal path id = 6}
+ // └─ G (Mirrors F) {Traversal path id = 7}
+ //
+ // C, D and E can be traversed via B or via F then B or via G then F then B.
+ // Depending on how the node is reached, its properties such as geometry or visibility might be
+ // different. And we can uniquely identify the node by keeping track of the nodes leading up to
+ // it. But to be more efficient we only need to track the nodes id and the top mirror root path.
+ // So C for example, would have the following unique traversal paths:
+ // - {Traversal path id = 3}
+ // - {Traversal path id = 3, mirrorRootId = 6}
+ // - {Traversal path id = 3, mirrorRootId = 7}
+
struct TraversalPath {
uint32_t id;
LayerHierarchy::Variant variant;
// Mirrored layers can have a different geometry than their parents so we need to track
// the mirror roots in the traversal.
- ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+ uint32_t mirrorRootId = UNASSIGNED_LAYER_ID;
// Relative layers can be visited twice, once by their parent and then once again by
// their relative parent. We keep track of the roots here to detect any loops in the
// hierarchy. If a relative root already exists in the list while building the
@@ -73,10 +93,11 @@
// Returns true if the node or its parents are not Detached.
bool isAttached() const { return !detached; }
// Returns true if the node is a clone.
- bool isClone() const { return !mirrorRootIds.empty(); }
+ bool isClone() const { return mirrorRootId != UNASSIGNED_LAYER_ID; }
+ TraversalPath getMirrorRoot() const;
bool operator==(const TraversalPath& other) const {
- return id == other.id && mirrorRootIds == other.mirrorRootIds;
+ return id == other.id && mirrorRootId == other.mirrorRootId;
}
std::string toString() const;
@@ -93,9 +114,7 @@
private:
TraversalPath& mTraversalPath;
- uint32_t mParentId;
- LayerHierarchy::Variant mParentVariant;
- bool mParentDetached;
+ TraversalPath mParentPath;
};
LayerHierarchy(RequestedLayerState* layer);
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 547a852..66197be 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -22,6 +22,7 @@
#include "LayerLifecycleManager.h"
#include "Layer.h" // temporarily needed for LayerHandle
#include "LayerHandle.h"
+#include "LayerLog.h"
#include "SwapErase.h"
namespace android::surfaceflinger::frontend {
@@ -36,6 +37,7 @@
mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
for (auto& newLayer : newLayers) {
RequestedLayerState& layer = *newLayer.get();
+ LLOGV(layer.id, "%s layer %s", __func__, layer.getDebugStringShort().c_str());
auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
if (!inserted) {
LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
@@ -146,7 +148,7 @@
while (it != mLayers.end()) {
RequestedLayerState* layer = it->get();
if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
- ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str());
+ LLOGV(layer->id, "destroyed layer %s", layer->getDebugStringShort().c_str());
std::iter_swap(it, mLayers.end() - 1);
mDestroyedLayers.emplace_back(std::move(mLayers.back()));
if (it == mLayers.end() - 1) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 3ed24b2..c9aeb24 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -259,8 +259,8 @@
}
}
-void updateVisibility(LayerSnapshot& snapshot) {
- snapshot.isVisible = snapshot.getIsVisible();
+void updateVisibility(LayerSnapshot& snapshot, bool visible) {
+ snapshot.isVisible = visible;
// TODO(b/238781169) we are ignoring this compat for now, since we will have
// to remove any optimization based on visibility.
@@ -273,9 +273,9 @@
// We are just using these layers for occlusion detection in
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
- const bool visible =
+ const bool visibleForInput =
(snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+ snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
}
bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
@@ -521,7 +521,7 @@
}
if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
- updateVisibility(*snapshot);
+ updateVisibility(*snapshot, snapshot->getIsVisible());
size_t oldZ = snapshot->globalZ;
size_t newZ = globalZ++;
snapshot->globalZ = newZ;
@@ -539,7 +539,8 @@
mNumInterestingSnapshots = (int)globalZ;
while (globalZ < mSnapshots.size()) {
mSnapshots[globalZ]->globalZ = globalZ;
- updateVisibility(*mSnapshots[globalZ]);
+ /* mark unreachable snapshots as explicitly invisible */
+ updateVisibility(*mSnapshots[globalZ], false);
globalZ++;
}
}
@@ -634,11 +635,15 @@
const bool forceUpdate = newSnapshot || args.forceUpdate ||
snapshot.changes.any(RequestedLayerState::Changes::Visibility |
RequestedLayerState::Changes::Created);
+ snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID
+ ? parentSnapshot.outputFilter.layerStack
+ : requested.layerStack;
+
uint32_t displayRotationFlags =
getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
// always update the buffer regardless of visibility
- if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) {
+ if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
snapshot.acquireFence =
(requested.externalTexture &&
requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
@@ -727,9 +732,13 @@
if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
snapshot.color.rgb = requested.getColor().rgb;
snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
- snapshot.backgroundBlurRadius =
- args.supportsBlur ? static_cast<int>(requested.backgroundBlurRadius) : 0;
+ snapshot.backgroundBlurRadius = args.supportsBlur
+ ? static_cast<int>(parentSnapshot.color.a * (float)requested.backgroundBlurRadius)
+ : 0;
snapshot.blurRegions = requested.blurRegions;
+ for (auto& region : snapshot.blurRegions) {
+ region.alpha = region.alpha * snapshot.color.a;
+ }
snapshot.hdrMetadata = requested.hdrMetadata;
}
@@ -965,7 +974,7 @@
// touches from going outside the cloned area.
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
- auto clonedRootSnapshot = getSnapshot(path.mirrorRootIds.back());
+ auto clonedRootSnapshot = getSnapshot(path.getMirrorRoot());
if (clonedRootSnapshot) {
const Rect rect =
displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index f4544fd..0902ab8 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -115,8 +115,8 @@
struct TraversalPathHash {
std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
uint32_t hashCode = key.id * 31;
- for (auto mirrorRoot : key.mirrorRootIds) {
- hashCode += mirrorRoot * 31;
+ if (key.mirrorRootId != UNASSIGNED_LAYER_ID) {
+ hashCode += key.mirrorRootId * 31;
}
return std::hash<size_t>{}(hashCode);
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index d63b126..2834084 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -144,7 +144,7 @@
const half oldAlpha = color.a;
const bool hadBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
-
+ const bool hadBlur = hasBlur();
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
@@ -174,6 +174,12 @@
RequestedLayerState::Changes::VisibleRegion;
}
}
+ if (what & (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged)) {
+ if (hadBlur != hasBlur()) {
+ changes |= RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::VisibleRegion;
+ }
+ }
if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
changes |= RequestedLayerState::Changes::Hierarchy;
if (clientChanges & layer_state_t::CONTENT_CHANGES)
@@ -319,11 +325,11 @@
}
std::string RequestedLayerState::getDebugString() const {
- return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) +
- ",relativeParent=" + layerIdToString(relativeParentId) +
- ",isRelativeOf=" + std::to_string(isRelativeOf) +
- ",mirrorIds=" + layerIdsToString(mirrorIds) +
- ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
+ std::stringstream debug;
+ debug << "RequestedLayerState{" << name << " parent=" << layerIdToString(parentId)
+ << " relativeParent=" << layerIdToString(relativeParentId)
+ << " mirrorId=" << layerIdsToString(mirrorIds) << " handle=" << handleAlive << " z=" << z;
+ return debug.str();
}
std::string RequestedLayerState::getDebugStringShort() const {
@@ -442,4 +448,8 @@
windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
+bool RequestedLayerState::hasBlur() const {
+ return backgroundBlurRadius > 0 || blurRegions.size() > 0;
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 6317b95..6840b25 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -71,6 +71,7 @@
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
+ bool hasBlur() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are