Loading services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -187,6 +187,8 @@ std::string LayerSnapshot::getDebugString() const { << " geomLayerTransform={tx=" << geomLayerTransform.tx() << ",ty=" << geomLayerTransform.ty() << "}" << "}"; debug << " input{ touchCropId=" << touchCropId << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}"; return debug.str(); } Loading services/surfaceflinger/FrontEnd/LayerSnapshot.h +1 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { int32_t frameRateSelectionPriority; LayerHierarchy::TraversalPath mirrorRootPath; bool unreachable = true; uint32_t touchCropId; uid_t uid; pid_t pid; ChildState childState; Loading services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +101 −30 Original line number Diff line number Diff line Loading @@ -447,23 +447,36 @@ void LayerSnapshotBuilder::updateSnapshots(const Args& args) { } } // Update touchable region crops outside the main update pass. This is because a layer could be // cropped by any other layer and it requires both snapshots to be updated. updateTouchableRegionCrop(args); const bool hasUnreachableSnapshots = sortSnapshotsByZ(args); clearChanges(mRootSnapshot); // Destroy unreachable snapshots if (!hasUnreachableSnapshots) { // Destroy unreachable snapshots for clone layers. And destroy snapshots for non-clone // layers if the layer have been destroyed. // TODO(b/238781169) consider making clone layer ids stable as well if (!hasUnreachableSnapshots && args.layerLifecycleManager.getDestroyedLayers().empty()) { return; } std::unordered_set<uint32_t> destroyedLayerIds; for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) { destroyedLayerIds.insert(destroyedLayer->id); } auto it = mSnapshots.begin(); while (it < mSnapshots.end()) { auto& traversalPath = it->get()->path; if (!it->get()->unreachable) { if (!it->get()->unreachable && destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) { it++; continue; } mIdToSnapshot.erase(traversalPath); mNeedsTouchableRegionCrop.erase(traversalPath); mSnapshots.back()->globalZ = it->get()->globalZ; std::iter_swap(it, mSnapshots.end() - 1); mSnapshots.erase(mSnapshots.end() - 1); Loading Loading @@ -554,7 +567,7 @@ bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { mResortSnapshots = false; for (auto& snapshot : mSnapshots) { snapshot->unreachable = true; snapshot->unreachable = snapshot->path.isClone(); } size_t globalZ = 0; Loading Loading @@ -739,6 +752,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a // If root layer, use the layer stack otherwise get the parent's layer stack. snapshot.color.a = parentSnapshot.color.a * requested.color.a; snapshot.alpha = snapshot.color.a; snapshot.inputInfo.alpha = snapshot.color.a; snapshot.isSecure = parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure); snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay; Loading Loading @@ -986,9 +1001,11 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid); snapshot.inputInfo.ownerPid = requested.ownerPid; } snapshot.touchCropId = requested.touchCropId; snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence); snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id); updateVisibility(snapshot, snapshot.isVisible); if (!needsInputInfo(snapshot, requested)) { return; } Loading Loading @@ -1033,27 +1050,13 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } auto cropLayerSnapshot = getSnapshot(requested.touchCropId); if (snapshot.inputInfo.replaceTouchableRegionWithCrop) { Rect inputBoundsInDisplaySpace; if (!cropLayerSnapshot) { if (cropLayerSnapshot) { mNeedsTouchableRegionCrop.insert(path); } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) { FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform); } else { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); } snapshot.inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace); } else if (cropLayerSnapshot) { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect( displayInfo.transform.transform(inputBoundsInDisplaySpace)); } // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state Loading @@ -1066,12 +1069,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, // touches from going outside the cloned area. if (path.isClone()) { snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE; auto clonedRootSnapshot = getSnapshot(snapshot.mirrorRootPath); if (clonedRootSnapshot) { const Rect rect = displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds}); snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect(rect); } mNeedsTouchableRegionCrop.insert(path); } } Loading Loading @@ -1117,4 +1115,77 @@ void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) con } } void LayerSnapshotBuilder::updateTouchableRegionCrop(const Args& args) { if (mNeedsTouchableRegionCrop.empty()) { return; } static constexpr ftl::Flags<RequestedLayerState::Changes> AFFECTS_INPUT = RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Created | RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry | RequestedLayerState::Changes::Input; if (args.forceUpdate != ForceUpdateFlags::ALL && !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) { return; } for (auto& path : mNeedsTouchableRegionCrop) { frontend::LayerSnapshot* snapshot = getSnapshot(path); if (!snapshot) { continue; } const std::optional<frontend::DisplayInfo> displayInfoOpt = args.displays.get(snapshot->outputFilter.layerStack); static frontend::DisplayInfo sDefaultInfo = {.isSecure = false}; auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); bool needsUpdate = args.forceUpdate == ForceUpdateFlags::ALL || snapshot->changes.any(AFFECTS_INPUT); auto cropLayerSnapshot = getSnapshot(snapshot->touchCropId); needsUpdate = needsUpdate || (cropLayerSnapshot && cropLayerSnapshot->changes.any(AFFECTS_INPUT)); auto clonedRootSnapshot = path.isClone() ? getSnapshot(snapshot->mirrorRootPath) : nullptr; needsUpdate = needsUpdate || (clonedRootSnapshot && clonedRootSnapshot->changes.any(AFFECTS_INPUT)); if (!needsUpdate) { continue; } if (snapshot->inputInfo.replaceTouchableRegionWithCrop) { Rect inputBoundsInDisplaySpace; if (!cropLayerSnapshot) { FloatRect inputBounds = getInputBounds(*snapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*snapshot, inputBounds, displayInfo.transform); } else { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); } snapshot->inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace); } else if (cropLayerSnapshot) { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect( displayInfo.transform.transform(inputBoundsInDisplaySpace)); } // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. if (clonedRootSnapshot) { const Rect rect = displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds}); snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect(rect); } } } } // namespace android::surfaceflinger::frontend services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +4 −0 Original line number Diff line number Diff line Loading @@ -119,10 +119,14 @@ private: const LayerSnapshot& parentSnapshot); void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot, const Args& args); void updateTouchableRegionCrop(const Args& args); std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*, LayerHierarchy::TraversalPathHash> mIdToSnapshot; // Track snapshots that needs touchable region crop from other snapshots std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash> mNeedsTouchableRegionCrop; std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots; LayerSnapshot mRootSnapshot; bool mResortSnapshots = false; Loading services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +19 −0 Original line number Diff line number Diff line Loading @@ -278,6 +278,25 @@ protected: mLifecycleManager.applyTransactions(transactions); } void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId, bool replaceTouchableRegionWithCrop) { std::vector<TransactionState> transactions; transactions.emplace_back(); transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; transactions.back().states.front().layerId = id; transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make(); auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); inputInfo->touchableRegion = region; inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop; transactions.back().states.front().touchCropId = touchCropId; inputInfo->token = sp<BBinder>::make(); mLifecycleManager.applyTransactions(transactions); } LayerLifecycleManager mLifecycleManager; }; Loading Loading
services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -187,6 +187,8 @@ std::string LayerSnapshot::getDebugString() const { << " geomLayerTransform={tx=" << geomLayerTransform.tx() << ",ty=" << geomLayerTransform.ty() << "}" << "}"; debug << " input{ touchCropId=" << touchCropId << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}"; return debug.str(); } Loading
services/surfaceflinger/FrontEnd/LayerSnapshot.h +1 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { int32_t frameRateSelectionPriority; LayerHierarchy::TraversalPath mirrorRootPath; bool unreachable = true; uint32_t touchCropId; uid_t uid; pid_t pid; ChildState childState; Loading
services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +101 −30 Original line number Diff line number Diff line Loading @@ -447,23 +447,36 @@ void LayerSnapshotBuilder::updateSnapshots(const Args& args) { } } // Update touchable region crops outside the main update pass. This is because a layer could be // cropped by any other layer and it requires both snapshots to be updated. updateTouchableRegionCrop(args); const bool hasUnreachableSnapshots = sortSnapshotsByZ(args); clearChanges(mRootSnapshot); // Destroy unreachable snapshots if (!hasUnreachableSnapshots) { // Destroy unreachable snapshots for clone layers. And destroy snapshots for non-clone // layers if the layer have been destroyed. // TODO(b/238781169) consider making clone layer ids stable as well if (!hasUnreachableSnapshots && args.layerLifecycleManager.getDestroyedLayers().empty()) { return; } std::unordered_set<uint32_t> destroyedLayerIds; for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) { destroyedLayerIds.insert(destroyedLayer->id); } auto it = mSnapshots.begin(); while (it < mSnapshots.end()) { auto& traversalPath = it->get()->path; if (!it->get()->unreachable) { if (!it->get()->unreachable && destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) { it++; continue; } mIdToSnapshot.erase(traversalPath); mNeedsTouchableRegionCrop.erase(traversalPath); mSnapshots.back()->globalZ = it->get()->globalZ; std::iter_swap(it, mSnapshots.end() - 1); mSnapshots.erase(mSnapshots.end() - 1); Loading Loading @@ -554,7 +567,7 @@ bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { mResortSnapshots = false; for (auto& snapshot : mSnapshots) { snapshot->unreachable = true; snapshot->unreachable = snapshot->path.isClone(); } size_t globalZ = 0; Loading Loading @@ -739,6 +752,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a // If root layer, use the layer stack otherwise get the parent's layer stack. snapshot.color.a = parentSnapshot.color.a * requested.color.a; snapshot.alpha = snapshot.color.a; snapshot.inputInfo.alpha = snapshot.color.a; snapshot.isSecure = parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure); snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay; Loading Loading @@ -986,9 +1001,11 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid); snapshot.inputInfo.ownerPid = requested.ownerPid; } snapshot.touchCropId = requested.touchCropId; snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence); snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id); updateVisibility(snapshot, snapshot.isVisible); if (!needsInputInfo(snapshot, requested)) { return; } Loading Loading @@ -1033,27 +1050,13 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } auto cropLayerSnapshot = getSnapshot(requested.touchCropId); if (snapshot.inputInfo.replaceTouchableRegionWithCrop) { Rect inputBoundsInDisplaySpace; if (!cropLayerSnapshot) { if (cropLayerSnapshot) { mNeedsTouchableRegionCrop.insert(path); } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) { FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform); } else { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); } snapshot.inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace); } else if (cropLayerSnapshot) { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect( displayInfo.transform.transform(inputBoundsInDisplaySpace)); } // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state Loading @@ -1066,12 +1069,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, // touches from going outside the cloned area. if (path.isClone()) { snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE; auto clonedRootSnapshot = getSnapshot(snapshot.mirrorRootPath); if (clonedRootSnapshot) { const Rect rect = displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds}); snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect(rect); } mNeedsTouchableRegionCrop.insert(path); } } Loading Loading @@ -1117,4 +1115,77 @@ void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) con } } void LayerSnapshotBuilder::updateTouchableRegionCrop(const Args& args) { if (mNeedsTouchableRegionCrop.empty()) { return; } static constexpr ftl::Flags<RequestedLayerState::Changes> AFFECTS_INPUT = RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Created | RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry | RequestedLayerState::Changes::Input; if (args.forceUpdate != ForceUpdateFlags::ALL && !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) { return; } for (auto& path : mNeedsTouchableRegionCrop) { frontend::LayerSnapshot* snapshot = getSnapshot(path); if (!snapshot) { continue; } const std::optional<frontend::DisplayInfo> displayInfoOpt = args.displays.get(snapshot->outputFilter.layerStack); static frontend::DisplayInfo sDefaultInfo = {.isSecure = false}; auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); bool needsUpdate = args.forceUpdate == ForceUpdateFlags::ALL || snapshot->changes.any(AFFECTS_INPUT); auto cropLayerSnapshot = getSnapshot(snapshot->touchCropId); needsUpdate = needsUpdate || (cropLayerSnapshot && cropLayerSnapshot->changes.any(AFFECTS_INPUT)); auto clonedRootSnapshot = path.isClone() ? getSnapshot(snapshot->mirrorRootPath) : nullptr; needsUpdate = needsUpdate || (clonedRootSnapshot && clonedRootSnapshot->changes.any(AFFECTS_INPUT)); if (!needsUpdate) { continue; } if (snapshot->inputInfo.replaceTouchableRegionWithCrop) { Rect inputBoundsInDisplaySpace; if (!cropLayerSnapshot) { FloatRect inputBounds = getInputBounds(*snapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*snapshot, inputBounds, displayInfo.transform); } else { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); } snapshot->inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace); } else if (cropLayerSnapshot) { FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first; Rect inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds, displayInfo.transform); snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect( displayInfo.transform.transform(inputBoundsInDisplaySpace)); } // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. if (clonedRootSnapshot) { const Rect rect = displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds}); snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect(rect); } } } } // namespace android::surfaceflinger::frontend
services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +4 −0 Original line number Diff line number Diff line Loading @@ -119,10 +119,14 @@ private: const LayerSnapshot& parentSnapshot); void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot, const Args& args); void updateTouchableRegionCrop(const Args& args); std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*, LayerHierarchy::TraversalPathHash> mIdToSnapshot; // Track snapshots that needs touchable region crop from other snapshots std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash> mNeedsTouchableRegionCrop; std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots; LayerSnapshot mRootSnapshot; bool mResortSnapshots = false; Loading
services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +19 −0 Original line number Diff line number Diff line Loading @@ -278,6 +278,25 @@ protected: mLifecycleManager.applyTransactions(transactions); } void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId, bool replaceTouchableRegionWithCrop) { std::vector<TransactionState> transactions; transactions.emplace_back(); transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; transactions.back().states.front().layerId = id; transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make(); auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); inputInfo->touchableRegion = region; inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop; transactions.back().states.front().touchCropId = touchCropId; inputInfo->token = sp<BBinder>::make(); mLifecycleManager.applyTransactions(transactions); } LayerLifecycleManager mLifecycleManager; }; Loading