Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29354ec5 authored by Vishnu Nair's avatar Vishnu Nair
Browse files

[sf] Update touchable region crop correctly

We need an extra pass to crop touchable region since we may need
to crop the region with bounds of another layer. So track snapshots
requiring a touchable region crop and updated them after the
all the snapshots have been updated.

Also make the snapshot ids unique for non clone layers for input.
Before this change the ids could change if the snapshot went
offscreen.

Test: presubmit
Test: atest com.android.launcher3.jank.BinderTests
Bug: 238781169

Change-Id: I323168bd4546813acbe7fa841b26cd390b83b951
parent 7b1cf06a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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();
}

+1 −0
Original line number Diff line number Diff line
@@ -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;
+101 −30
Original line number Diff line number Diff line
@@ -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);
@@ -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;
@@ -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;
@@ -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;
    }
@@ -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
@@ -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);
    }
}

@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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;
+19 −0
Original line number Diff line number Diff line
@@ -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