diff options
author | 2023-01-18 16:35:03 +0000 | |
---|---|---|
committer | 2023-01-18 16:35:03 +0000 | |
commit | 1e1a387877dcee28fa1a8d80c0745b9ec7174dea (patch) | |
tree | 70e89a94f4916b41019c39a7bfbf0815ea099c07 | |
parent | 39bf1d612d52c444f3e28f399f688e5b2f6d74ab (diff) | |
parent | 8fc721bb338c6d13e8758c2139d073acab86e3e2 (diff) |
Merge "SF: Introduce LayerSnapshot and LayerSnapshotBuilder"
19 files changed, 1855 insertions, 305 deletions
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 45a84f6c66..ecde47fed5 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -206,26 +206,46 @@ struct layer_state_t { uint64_t diff(const layer_state_t& other) const; bool hasBufferChanges() const; - // Changes to the tree structure. - static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged | - layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent | - layer_state_t::eBackgroundColorChanged; + // Layer hierarchy updates. + static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eBackgroundColorChanged | + layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged | + layer_state_t::eReparent; + + // Geometry updates. + static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged | + layer_state_t::eBufferTransformChanged | layer_state_t::eCropChanged | + layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged | + layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged | + layer_state_t::eTransparentRegionChanged; + + // Buffer and related updates. + static constexpr uint64_t BUFFER_CHANGES = layer_state_t::eApiChanged | + layer_state_t::eBufferChanged | layer_state_t::eBufferCropChanged | + layer_state_t::eBufferTransformChanged | layer_state_t::eDataspaceChanged | + layer_state_t::eSidebandStreamChanged | layer_state_t::eSurfaceDamageRegionChanged | + layer_state_t::eTransformToDisplayInverseChanged | + layer_state_t::eTransparentRegionChanged; + // Content updates. - static constexpr uint64_t CONTENT_CHANGES = layer_state_t::eAlphaChanged | - layer_state_t::eTransparentRegionChanged | layer_state_t::eShadowRadiusChanged | - layer_state_t::eRenderBorderChanged | layer_state_t::eColorChanged | - layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged | - layer_state_t::eApiChanged | layer_state_t::eSidebandStreamChanged | + static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES | + layer_state_t::eAlphaChanged | layer_state_t::eAutoRefreshChanged | + layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBackgroundColorChanged | + layer_state_t::eBlurRegionsChanged | layer_state_t::eColorChanged | + layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged | + layer_state_t::eCornerRadiusChanged | layer_state_t::eHdrMetadataChanged | + layer_state_t::eRenderBorderChanged | layer_state_t::eShadowRadiusChanged | + layer_state_t::eStretchChanged; + + // Changes which invalidates the layer's visible region in CE. + static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES | + layer_state_t::GEOMETRY_CHANGES | layer_state_t::HIERARCHY_CHANGES; + + // Changes affecting child states. + static constexpr uint64_t AFFECTS_CHILDREN = layer_state_t::GEOMETRY_CHANGES | + layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | - layer_state_t::eBackgroundColorChanged | layer_state_t::eColorSpaceAgnosticChanged | - layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged | - layer_state_t::eAutoRefreshChanged | layer_state_t::eStretchChanged; - // Changes to content or children size. - static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::ePositionChanged | - layer_state_t::eMatrixChanged | layer_state_t::eTransparentRegionChanged | - layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged | - layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged | - layer_state_t::eDestinationFrameChanged; + layer_state_t::eFlagsChanged | layer_state_t::eLayerStackChanged | + layer_state_t::eTrustedOverlayChanged; bool hasValidBuffer() const; void sanitize(int32_t permissions); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 5647b73a6a..fe7cff7577 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -157,6 +157,8 @@ filegroup { "EventLog/EventLog.cpp", "FrontEnd/LayerCreationArgs.cpp", "FrontEnd/LayerHandle.cpp", + "FrontEnd/LayerSnapshot.cpp", + "FrontEnd/LayerSnapshotBuilder.cpp", "FrontEnd/LayerHierarchy.cpp", "FrontEnd/LayerLifecycleManager.cpp", "FrontEnd/RequestedLayerState.cpp", diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index db4e8af484..514a642bdc 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -131,7 +131,7 @@ const RequestedLayerState* LayerHierarchy::getLayer() const { std::string LayerHierarchy::getDebugStringShort() const { std::string debug = "LayerHierarchy{"; - debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " "; + debug += ((mLayer) ? mLayer->getDebugString() : "root") + " "; if (mChildren.empty()) { debug += "no children"; } else { @@ -401,10 +401,13 @@ LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool return it->second; } -LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID = +const LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT = {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached}; std::string LayerHierarchy::TraversalPath::toString() const { + if (id == UNASSIGNED_LAYER_ID) { + return "TraversalPath{ROOT}"; + } std::string debugString = "TraversalPath{.id = " + std::to_string(id); if (!mirrorRootIds.empty()) { @@ -437,20 +440,22 @@ LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath LayerHierarchy::Variant variant) : mTraversalPath(traversalPath), mParentId(traversalPath.id), - mParentVariant(traversalPath.variant) { + mParentVariant(traversalPath.variant), + mParentDetached(traversalPath.detached) { // 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); - } - if (variant == LayerHierarchy::Variant::Relative) { + } else if (variant == LayerHierarchy::Variant::Relative) { if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(), layerId) != traversalPath.relativeRootIds.end()) { traversalPath.invalidRelativeRootId = layerId; } traversalPath.relativeRootIds.emplace_back(layerId); + } else if (variant == LayerHierarchy::Variant::Detached) { + traversalPath.detached = true; } } LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() { @@ -458,8 +463,7 @@ LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() { // the constructor. if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) { mTraversalPath.mirrorRootIds.pop_back(); - } - if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) { + } else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) { mTraversalPath.relativeRootIds.pop_back(); } if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) { @@ -467,6 +471,7 @@ LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() { } mTraversalPath.id = mParentId; mTraversalPath.variant = mParentVariant; + mTraversalPath.detached = mParentDetached; } } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index f83a859de7..8cdc24062b 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -63,15 +63,22 @@ public: // First duplicate relative root id found. If this is a valid layer id that means we are // in a loop. uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID; + // See isAttached() + bool detached = false; bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; } - bool isRelative() { return !relativeRootIds.empty(); } + // Returns true if this node is reached via one or more relative parents. + bool isRelative() const { return !relativeRootIds.empty(); } + // 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 operator==(const TraversalPath& other) const { return id == other.id && mirrorRootIds == other.mirrorRootIds; } std::string toString() const; - static TraversalPath ROOT_TRAVERSAL_ID; + static const TraversalPath ROOT; }; // Helper class to add nodes to an existing traversal id and removes the @@ -86,6 +93,7 @@ public: TraversalPath& mTraversalPath; uint32_t mParentId; LayerHierarchy::Variant mParentVariant; + bool mParentDetached; }; LayerHierarchy(RequestedLayerState* layer); @@ -98,12 +106,14 @@ public: // Traverse the hierarchy and visit all child variants. void traverse(const Visitor& visitor) const { - traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID); + TraversalPath root = TraversalPath::ROOT; + traverse(visitor, root); } // Traverse the hierarchy in z-order, skipping children that have relative parents. void traverseInZOrder(const Visitor& visitor) const { - traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID); + TraversalPath root = TraversalPath::ROOT; + traverseInZOrder(visitor, root); } const RequestedLayerState* getLayer() const; diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp index fdf60b3dfc..5514c06c84 100644 --- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp @@ -210,10 +210,7 @@ void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState layer->touchCropId = linkLayer(layer->touchCropId, layer->id); } - mGlobalChanges |= layer->changes & - (RequestedLayerState::Changes::Hierarchy | - RequestedLayerState::Changes::Geometry | - RequestedLayerState::Changes::Content); + mGlobalChanges |= layer->changes; } } } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp new file mode 100644 index 0000000000..d483a99824 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 2022 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#undef LOG_TAG +#define LOG_TAG "LayerSnapshot" + +#include "LayerSnapshot.h" + +namespace android::surfaceflinger::frontend { + +using namespace ftl::flag_operators; + +LayerSnapshot::LayerSnapshot(const RequestedLayerState& state, + const LayerHierarchy::TraversalPath& path) + : path(path) { + sequence = static_cast<int32_t>(state.id); + name = state.name; + textureName = state.textureName; + premultipliedAlpha = state.premultipliedAlpha; + inputInfo.name = state.name; + inputInfo.id = static_cast<int32_t>(state.id); + inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid); + inputInfo.ownerPid = state.ownerPid; +} + +// As documented in libhardware header, formats in the range +// 0x100 - 0x1FF are specific to the HAL implementation, and +// are known to have no alpha channel +// TODO: move definition for device-specific range into +// hardware.h, instead of using hard-coded values here. +#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) + +bool LayerSnapshot::isOpaqueFormat(PixelFormat format) { + if (HARDWARE_IS_DEVICE_FORMAT(format)) { + return true; + } + switch (format) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_R_8: + return false; + } + // in all other case, we have no blending (also for unknown formats) + return true; +} + +bool LayerSnapshot::hasBufferOrSidebandStream() const { + return ((sidebandStream != nullptr) || (buffer != nullptr)); +} + +bool LayerSnapshot::drawShadows() const { + return shadowSettings.length > 0.f; +} + +bool LayerSnapshot::fillsColor() const { + return !hasBufferOrSidebandStream() && color.r >= 0.0_hf && color.g >= 0.0_hf && + color.b >= 0.0_hf; +} + +bool LayerSnapshot::hasBlur() const { + return backgroundBlurRadius > 0 || blurRegions.size() > 0; +} + +bool LayerSnapshot::hasEffect() const { + return fillsColor() || drawShadows() || hasBlur(); +} + +bool LayerSnapshot::hasSomethingToDraw() const { + return hasEffect() || hasBufferOrSidebandStream(); +} + +bool LayerSnapshot::isContentOpaque() const { + // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the + // layer's opaque flag. + if (!hasSomethingToDraw()) { + return false; + } + + // if the layer has the opaque flag, then we're always opaque + if (layerOpaqueFlagSet) { + return true; + } + + // If the buffer has no alpha channel, then we are opaque + if (hasBufferOrSidebandStream() && + isOpaqueFormat(buffer ? buffer->getPixelFormat() : PIXEL_FORMAT_NONE)) { + return true; + } + + // Lastly consider the layer opaque if drawing a color with alpha == 1.0 + return fillsColor() && color.a == 1.0_hf; +} + +bool LayerSnapshot::isHiddenByPolicy() const { + if (CC_UNLIKELY(invalidTransform)) { + ALOGW("Hide layer %s because it has invalid transformation.", name.c_str()); + return true; + } + return isHiddenByPolicyFromParent || isHiddenByPolicyFromRelativeParent; +} + +bool LayerSnapshot::getIsVisible() const { + if (!hasSomethingToDraw()) { + return false; + } + + if (isHiddenByPolicy()) { + return false; + } + + return color.a > 0.0f || hasBlur(); +} + +std::string LayerSnapshot::getIsVisibleReason() const { + if (!hasSomethingToDraw()) { + return "!hasSomethingToDraw"; + } + + if (isHiddenByPolicy()) { + return "isHiddenByPolicy"; + } + + if (color.a > 0.0f || hasBlur()) { + return ""; + } + + return "alpha = 0 and !hasBlur"; +} + +bool LayerSnapshot::canReceiveInput() const { + return !isHiddenByPolicy() && (!hasBufferOrSidebandStream() || color.a > 0.0f); +} + +bool LayerSnapshot::isTransformValid(const ui::Transform& t) { + float transformDet = t.det(); + return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet); +} + +std::string LayerSnapshot::getDebugString() const { + return "Snapshot(" + base::StringPrintf("%p", this) + "){" + path.toString() + name + + " isHidden=" + std::to_string(isHiddenByPolicyFromParent) + + " isHiddenRelative=" + std::to_string(isHiddenByPolicyFromRelativeParent) + + " isVisible=" + std::to_string(isVisible) + " " + getIsVisibleReason() + "}"; +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h new file mode 100644 index 0000000000..d14bd3ae8b --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -0,0 +1,96 @@ +/* + * Copyright 2022 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 + +#include <compositionengine/LayerFECompositionState.h> +#include <renderengine/LayerSettings.h> +#include "LayerHierarchy.h" +#include "RequestedLayerState.h" +#include "android-base/stringprintf.h" + +namespace android::surfaceflinger::frontend { + +struct RoundedCornerState { + RoundedCornerState() = default; + RoundedCornerState(const FloatRect& cropRect, const vec2& radius) + : cropRect(cropRect), radius(radius) {} + + // Rounded rectangle in local layer coordinate space. + FloatRect cropRect = FloatRect(); + // Radius of the rounded rectangle. + vec2 radius; + bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; } + bool operator==(RoundedCornerState const& rhs) const { + return cropRect == rhs.cropRect && radius == rhs.radius; + } +}; + +// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition +// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings +// passed to Render Engine are created using properties stored on this struct. +struct LayerSnapshot : public compositionengine::LayerFECompositionState { + LayerSnapshot() = default; + LayerSnapshot(const RequestedLayerState&, const LayerHierarchy::TraversalPath&); + + LayerHierarchy::TraversalPath path; + size_t globalZ = std::numeric_limits<ssize_t>::max(); + bool invalidTransform = false; + bool isHiddenByPolicyFromParent = false; + bool isHiddenByPolicyFromRelativeParent = false; + ftl::Flags<RequestedLayerState::Changes> changes; + int32_t sequence; + std::string name; + uint32_t textureName; + bool contentOpaque; + bool layerOpaqueFlagSet; + RoundedCornerState roundedCorner; + FloatRect transformedBounds; + renderengine::ShadowSettings shadowSettings; + bool premultipliedAlpha; + bool isHdrY410; + bool bufferNeedsFiltering; + ui::Transform parentTransform; + Rect bufferSize; + Rect croppedBufferSize; + std::shared_ptr<renderengine::ExternalTexture> externalTexture; + gui::LayerMetadata layerMetadata; + gui::LayerMetadata relativeLayerMetadata; + bool hasReadyFrame; + ui::Transform localTransformInverse; + gui::WindowInfo inputInfo; + ui::Transform localTransform; + gui::DropInputMode dropInputMode; + bool isTrustedOverlay; + + static bool isOpaqueFormat(PixelFormat format); + static bool isTransformValid(const ui::Transform& t); + + bool canReceiveInput() const; + bool drawShadows() const; + bool fillsColor() const; + bool getIsVisible() const; + bool hasBlur() const; + bool hasBufferOrSidebandStream() const; + bool hasEffect() const; + bool hasSomethingToDraw() const; + bool isContentOpaque() const; + bool isHiddenByPolicy() const; + std::string getDebugString() const; + std::string getIsVisibleReason() const; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp new file mode 100644 index 0000000000..bff12d7b5a --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -0,0 +1,833 @@ +/* + * Copyright 2022 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. + */ + +// #define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#undef LOG_TAG +#define LOG_TAG "LayerSnapshotBuilder" + +#include "LayerSnapshotBuilder.h" +#include <gui/TraceUtils.h> +#include <numeric> +#include "DisplayHardware/HWC2.h" +#include "DisplayHardware/Hal.h" +#include "ftl/small_map.h" + +namespace android::surfaceflinger::frontend { + +using namespace ftl::flag_operators; + +namespace { +FloatRect getMaxDisplayBounds( + const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) { + const ui::Size maxSize = [&displays] { + if (displays.empty()) return ui::Size{5000, 5000}; + + return std::accumulate(displays.begin(), displays.end(), ui::kEmptySize, + [](ui::Size size, const auto& pair) -> ui::Size { + const auto& display = pair.second; + return {std::max(size.getWidth(), display.info.logicalWidth), + std::max(size.getHeight(), display.info.logicalHeight)}; + }); + }(); + + // Ignore display bounds for now since they will be computed later. Use a large Rect bound + // to ensure it's bigger than an actual display will be. + const float xMax = static_cast<float>(maxSize.getWidth()) * 10.f; + const float yMax = static_cast<float>(maxSize.getHeight()) * 10.f; + + return {-xMax, -yMax, xMax, yMax}; +} + +// Applies the given transform to the region, while protecting against overflows caused by any +// offsets. If applying the offset in the transform to any of the Rects in the region would result +// in an overflow, they are not added to the output Region. +Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r, + const std::string& debugWindowName) { + // Round the translation using the same rounding strategy used by ui::Transform. + const auto tx = static_cast<int32_t>(t.tx() + 0.5); + const auto ty = static_cast<int32_t>(t.ty() + 0.5); + + ui::Transform transformWithoutOffset = t; + transformWithoutOffset.set(0.f, 0.f); + + const Region transformed = transformWithoutOffset.transform(r); + + // Apply the translation to each of the Rects in the region while discarding any that overflow. + Region ret; + for (const auto& rect : transformed) { + Rect newRect; + if (__builtin_add_overflow(rect.left, tx, &newRect.left) || + __builtin_add_overflow(rect.top, ty, &newRect.top) || + __builtin_add_overflow(rect.right, tx, &newRect.right) || + __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) { + ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.", + debugWindowName.c_str()); + continue; + } + ret.orSelf(newRect); + } + return ret; +} + +/* + * We don't want to send the layer's transform to input, but rather the + * parent's transform. This is because Layer's transform is + * information about how the buffer is placed on screen. The parent's + * transform makes more sense to send since it's information about how the + * layer is placed on screen. This transform is used by input to determine + * how to go from screen space back to window space. + */ +ui::Transform getInputTransform(const LayerSnapshot& snapshot) { + if (!snapshot.hasBufferOrSidebandStream()) { + return snapshot.geomLayerTransform; + } + return snapshot.parentTransform; +} + +/** + * Similar to getInputTransform, we need to update the bounds to include the transform. + * This is because bounds don't include the buffer transform, where the input assumes + * that's already included. + */ +Rect getInputBounds(const LayerSnapshot& snapshot) { + if (!snapshot.hasBufferOrSidebandStream()) { + return snapshot.croppedBufferSize; + } + + if (snapshot.localTransform.getType() == ui::Transform::IDENTITY || + !snapshot.croppedBufferSize.isValid()) { + return snapshot.croppedBufferSize; + } + return snapshot.localTransform.transform(snapshot.croppedBufferSize); +} + +void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& screenToDisplay, + const LayerSnapshot& snapshot) { + Rect tmpBounds = getInputBounds(snapshot); + if (!tmpBounds.isValid()) { + info.touchableRegion.clear(); + // A layer could have invalid input bounds and still expect to receive touch input if it has + // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated + // correctly to determine the coordinate space for input events. Use an empty rect so that + // the layer will receive input in its own layer space. + tmpBounds = Rect::EMPTY_RECT; + } + + // InputDispatcher works in the display device's coordinate space. Here, we calculate the + // frame and transform used for the layer, which determines the bounds and the coordinate space + // within which the layer will receive input. + // + // The coordinate space within which each of the bounds are specified is explicitly documented + // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A + // Transform converts one coordinate space to another, which is apparent in its naming. For + // example, "layerToDisplay" transforms layer space to display space. + // + // Coordinate space definitions: + // - display: The display device's coordinate space. Correlates to pixels on the display. + // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space. + // - layer: The coordinate space of this layer. + // - input: The coordinate space in which this layer will receive input events. This could be + // different than layer space if a surfaceInset is used, which changes the origin + // of the input space. + const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect(); + + // Clamp surface inset to the input bounds. + const auto surfaceInset = static_cast<float>(info.surfaceInset); + const float xSurfaceInset = + std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f)); + const float ySurfaceInset = + std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f)); + + // Apply the insets to the input bounds. + const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset, + inputBoundsInLayer.top + ySurfaceInset, + inputBoundsInLayer.right - xSurfaceInset, + inputBoundsInLayer.bottom - ySurfaceInset); + + // Crop the input bounds to ensure it is within the parent's bounds. + const FloatRect croppedInsetBoundsInLayer = + snapshot.geomLayerBounds.intersect(insetBoundsInLayer); + + const ui::Transform layerToScreen = getInputTransform(snapshot); + const ui::Transform layerToDisplay = screenToDisplay * layerToScreen; + + const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)}; + info.frameLeft = roundedFrameInDisplay.left; + info.frameTop = roundedFrameInDisplay.top; + info.frameRight = roundedFrameInDisplay.right; + info.frameBottom = roundedFrameInDisplay.bottom; + + ui::Transform inputToLayer; + inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top); + const ui::Transform inputToDisplay = layerToDisplay * inputToLayer; + + // InputDispatcher expects a display-to-input transform. + info.transform = inputToDisplay.inverse(); + + // The touchable region is specified in the input coordinate space. Change it to display space. + info.touchableRegion = + transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, snapshot.name); +} + +void handleDropInputMode(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot) { + if (snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { + return; + } + + // Check if we need to drop input unconditionally + const gui::DropInputMode dropInputMode = snapshot.dropInputMode; + if (dropInputMode == gui::DropInputMode::ALL) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT; + ALOGV("Dropping input for %s as requested by policy.", snapshot.name.c_str()); + return; + } + + // Check if we need to check if the window is obscured by parent + if (dropInputMode != gui::DropInputMode::OBSCURED) { + return; + } + + // Check if the parent has set an alpha on the layer + if (parentSnapshot.color.a != 1.0_hf) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT; + ALOGV("Dropping input for %s as requested by policy because alpha=%f", + snapshot.name.c_str(), static_cast<float>(parentSnapshot.color.a)); + } + + // Check if the parent has cropped the buffer + Rect bufferSize = snapshot.croppedBufferSize; + if (!bufferSize.isValid()) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; + return; + } + + // Screenbounds are the layer bounds cropped by parents, transformed to screenspace. + // To check if the layer has been cropped, we take the buffer bounds, apply the local + // layer crop and apply the same set of transforms to move to screenspace. If the bounds + // match then the layer has not been cropped by its parents. + Rect bufferInScreenSpace(snapshot.geomLayerTransform.transform(bufferSize)); + bool croppedByParent = bufferInScreenSpace != Rect{snapshot.transformedBounds}; + + if (croppedByParent) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT; + ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent", + snapshot.name.c_str()); + } else { + // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop + // input if the window is obscured. This check should be done in surfaceflinger but the + // logic currently resides in inputflinger. So pass the if_obscured check to input to only + // drop input events if the window is obscured. + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; + } +} + +bool getBufferNeedsFiltering(const LayerSnapshot& snapshot, const ui::Size& unrotatedBufferSize) { + const int32_t layerWidth = static_cast<int32_t>(snapshot.geomLayerBounds.getWidth()); + const int32_t layerHeight = static_cast<int32_t>(snapshot.geomLayerBounds.getHeight()); + return layerWidth != unrotatedBufferSize.width || layerHeight != unrotatedBufferSize.height; +} + +auto getBlendMode(const LayerSnapshot& snapshot, const RequestedLayerState& requested) { + auto blendMode = Hwc2::IComposerClient::BlendMode::NONE; + if (snapshot.alpha != 1.0f || !snapshot.isContentOpaque()) { + blendMode = requested.premultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED + : Hwc2::IComposerClient::BlendMode::COVERAGE; + } + return blendMode; +} + +} // namespace + +LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() { + LayerSnapshot snapshot; + snapshot.changes = ftl::Flags<RequestedLayerState::Changes>(); + snapshot.isHiddenByPolicyFromParent = false; + snapshot.isHiddenByPolicyFromRelativeParent = false; + snapshot.parentTransform.reset(); + snapshot.geomLayerTransform.reset(); + snapshot.geomInverseLayerTransform.reset(); + snapshot.geomLayerBounds = getMaxDisplayBounds({}); + snapshot.roundedCorner = RoundedCornerState(); + snapshot.stretchEffect = {}; + snapshot.outputFilter.layerStack = ui::DEFAULT_LAYER_STACK; + snapshot.outputFilter.toInternalDisplay = false; + snapshot.isSecure = false; + snapshot.color.a = 1.0_hf; + snapshot.colorTransformIsIdentity = true; + snapshot.shadowRadius = 0.f; + snapshot.layerMetadata.mMap.clear(); + snapshot.relativeLayerMetadata.mMap.clear(); + snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED; + snapshot.dropInputMode = gui::DropInputMode::NONE; + snapshot.isTrustedOverlay = false; + return snapshot; +} + +LayerSnapshotBuilder::LayerSnapshotBuilder() : mRootSnapshot(getRootSnapshot()) {} + +LayerSnapshotBuilder::LayerSnapshotBuilder(Args args) : LayerSnapshotBuilder() { + args.forceUpdate = true; + updateSnapshots(args); +} + +bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) { + if (args.forceUpdate) { + // force update requested, so skip the fast path + return false; + } + + if (args.layerLifecycleManager.getGlobalChanges().get() == 0) { + // there are no changes, so just clear the change flags from before. + for (auto& snapshot : mSnapshots) { + snapshot->changes.clear(); + snapshot->contentDirty = false; + } + return true; + } + + if (args.layerLifecycleManager.getGlobalChanges() != RequestedLayerState::Changes::Content) { + // We have changes that require us to walk the hierarchy and update child layers. + // No fast path for you. + return false; + } + + // There are only content changes which do not require any child layer snapshots to be updated. + ALOGV("%s", __func__); + ATRACE_NAME("FastPath"); + + // Collect layers with changes + ftl::SmallMap<uint32_t, RequestedLayerState*, 10> layersWithChanges; + for (auto& layer : args.layerLifecycleManager.getLayers()) { + if (layer->changes.test(RequestedLayerState::Changes::Content)) { + layersWithChanges.emplace_or_replace(layer->id, layer.get()); + } + } + + // Walk through the snapshots, clearing previous change flags and updating the snapshots + // if needed. + for (auto& snapshot : mSnapshots) { + snapshot->changes.clear(); + snapshot->contentDirty = false; + auto it = layersWithChanges.find(snapshot->path.id); + if (it != layersWithChanges.end()) { + ALOGV("%s fast path snapshot changes = %s", __func__, + mRootSnapshot.changes.string().c_str()); + LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT; + updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root); + } + } + return true; +} + +void LayerSnapshotBuilder::updateSnapshots(const Args& args) { + ATRACE_NAME("UpdateSnapshots"); + ALOGV("%s updateSnapshots force = %s", __func__, std::to_string(args.forceUpdate).c_str()); + if (args.forceUpdate || args.displayChanges) { + mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays); + } + if (args.displayChanges) { + mRootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren | + RequestedLayerState::Changes::Geometry; + } + LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT; + for (auto& [childHierarchy, variant] : args.root.mChildren) { + LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, + childHierarchy->getLayer()->id, + variant); + updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot); + } + + sortSnapshotsByZ(args); + mRootSnapshot.changes.clear(); + + // Destroy unreachable snapshots + if (args.layerLifecycleManager.getDestroyedLayers().empty()) { + return; + } + + std::unordered_set<uint32_t> destroyedLayerIds; + for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) { + destroyedLayerIds.emplace(destroyedLayer->id); + } + auto it = mSnapshots.begin(); + while (it < mSnapshots.end()) { + auto& traversalPath = it->get()->path; + if (destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) { + it++; + continue; + } + + mIdToSnapshot.erase(traversalPath); + std::iter_swap(it, mSnapshots.end() - 1); + mSnapshots.erase(mSnapshots.end() - 1); + } +} + +void LayerSnapshotBuilder::update(const Args& args) { + if (tryFastUpdate(args)) { + return; + } + updateSnapshots(args); +} + +void LayerSnapshotBuilder::updateSnapshotsInHierarchy(const Args& args, + const LayerHierarchy& hierarchy, + LayerHierarchy::TraversalPath& traversalPath, + const LayerSnapshot& parentSnapshot) { + const RequestedLayerState* layer = hierarchy.getLayer(); + LayerSnapshot* snapshot = getOrCreateSnapshot(traversalPath, *layer); + if (traversalPath.isRelative()) { + bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative; + updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args); + } else { + if (traversalPath.isAttached()) { + resetRelativeState(*snapshot); + } + updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath); + } + + // If layer is hidden by policy we can avoid update its children. If the visibility + // changed this update, then we still need to set the visibility on all the children. + if (snapshot->isHiddenByPolicy() && + (!snapshot->changes.any(RequestedLayerState::Changes::Visibility | + RequestedLayerState::Changes::Hierarchy))) { + return; + } + + for (auto& [childHierarchy, variant] : hierarchy.mChildren) { + LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath, + childHierarchy->getLayer()->id, + variant); + updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot); + } +} + +LayerSnapshot* LayerSnapshotBuilder::getSnapshot(uint32_t layerId) const { + if (layerId == UNASSIGNED_LAYER_ID) { + return nullptr; + } + LayerHierarchy::TraversalPath path{.id = layerId}; + return getSnapshot(path); +} + +LayerSnapshot* LayerSnapshotBuilder::getSnapshot(const LayerHierarchy::TraversalPath& id) const { + auto it = mIdToSnapshot.find(id); + return it == mIdToSnapshot.end() ? nullptr : it->second; +} + +LayerSnapshot* LayerSnapshotBuilder::getOrCreateSnapshot(const LayerHierarchy::TraversalPath& id, + const RequestedLayerState& layer) { + auto snapshot = getSnapshot(id); + if (snapshot) { + return snapshot; + } + + mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, id)); + snapshot = mSnapshots.back().get(); + snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1; + mIdToSnapshot[id] = snapshot; + return snapshot; +} + +void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { + if (!args.forceUpdate && + !args.layerLifecycleManager.getGlobalChanges().any( + RequestedLayerState::Changes::Hierarchy | + RequestedLayerState::Changes::Visibility)) { + // We are not force updating and there are no hierarchy or visibility changes. Avoid sorting + // the snapshots. + return; + } + + size_t globalZ = 0; + args.root.traverseInZOrder( + [this, &globalZ](const LayerHierarchy&, + const LayerHierarchy::TraversalPath& traversalPath) -> bool { + LayerSnapshot* snapshot = getSnapshot(traversalPath); + if (!snapshot) { + return false; + } + + if (snapshot->isHiddenByPolicy() && + !snapshot->changes.test(RequestedLayerState::Changes::Visibility)) { + return false; + } + + if (snapshot->isVisible) { + size_t oldZ = snapshot->globalZ; + size_t newZ = globalZ++; + snapshot->globalZ = newZ; + if (oldZ == newZ) { + return true; + } + mSnapshots[newZ]->globalZ = oldZ; + std::iter_swap(mSnapshots.begin() + static_cast<ssize_t>(oldZ), + mSnapshots.begin() + static_cast<ssize_t>(newZ)); + } + + return true; + }); + + while (globalZ < mSnapshots.size()) { + mSnapshots[globalZ]->globalZ = globalZ; + mSnapshots[globalZ]->isVisible = false; + globalZ++; + } +} + +void LayerSnapshotBuilder::updateRelativeState(LayerSnapshot& snapshot, + const LayerSnapshot& parentSnapshot, + bool parentIsRelative, const Args& args) { + if (parentIsRelative) { + snapshot.isHiddenByPolicyFromRelativeParent = parentSnapshot.isHiddenByPolicyFromParent; + if (args.includeMetadata) { + snapshot.relativeLayerMetadata = parentSnapshot.layerMetadata; + } + } else { + snapshot.isHiddenByPolicyFromRelativeParent = + parentSnapshot.isHiddenByPolicyFromRelativeParent; + if (args.includeMetadata) { + snapshot.relativeLayerMetadata = parentSnapshot.relativeLayerMetadata; + } + } + snapshot.isVisible = snapshot.getIsVisible(); +} + +void LayerSnapshotBuilder::resetRelativeState(LayerSnapshot& snapshot) { + snapshot.isHiddenByPolicyFromRelativeParent = false; + snapshot.relativeLayerMetadata.mMap.clear(); +} + +uint32_t getDisplayRotationFlags( + const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays, + const ui::LayerStack& layerStack) { + static frontend::DisplayInfo sDefaultDisplayInfo = {.isPrimary = false}; + auto display = displays.get(layerStack).value_or(sDefaultDisplayInfo).get(); + return display.isPrimary ? display.rotationFlags : 0; +} + +void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args, + const RequestedLayerState& requested, + const LayerSnapshot& parentSnapshot, + const LayerHierarchy::TraversalPath& path) { + // Always update flags and visibility + ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes & + (RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry | + RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata | + RequestedLayerState::Changes::AffectsChildren); + snapshot.changes = parentChanges | requested.changes; + snapshot.isHiddenByPolicyFromParent = + parentSnapshot.isHiddenByPolicyFromParent || requested.isHiddenByPolicy(); + snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY; + if (snapshot.isHiddenByPolicyFromParent) { + snapshot.isVisible = false; + return; + } + + uint32_t displayRotationFlags = + getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack); + + const bool forceUpdate = args.forceUpdate || + snapshot.changes.any(RequestedLayerState::Changes::Visibility | + RequestedLayerState::Changes::Created); + + if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) { + // 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.isSecure = + parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure); + snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay; + snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID + ? parentSnapshot.outputFilter.layerStack + : requested.layerStack; + snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay || + (requested.flags & layer_state_t::eLayerSkipScreenshot); + snapshot.stretchEffect = (requested.stretchEffect.hasEffect()) + ? requested.stretchEffect + : parentSnapshot.stretchEffect; + if (!parentSnapshot.colorTransformIsIdentity) { + snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform; + snapshot.colorTransformIsIdentity = false; + } else { + snapshot.colorTransform = requested.colorTransform; + snapshot.colorTransformIsIdentity = !requested.hasColorTransform; + } + } + + if (forceUpdate || requested.changes.get() != 0) { + snapshot.compositionType = requested.getCompositionType(); + snapshot.dimmingEnabled = requested.dimmingEnabled; + snapshot.layerOpaqueFlagSet = + (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque; + } + + if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) { + snapshot.acquireFence = + (requested.bufferData) ? requested.bufferData->acquireFence : Fence::NO_FENCE; + snapshot.buffer = + requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr; + snapshot.bufferSize = requested.getBufferSize(displayRotationFlags); + snapshot.geomBufferSize = snapshot.bufferSize; + snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize); + snapshot.dataspace = requested.dataspace; + snapshot.externalTexture = requested.externalTexture; + snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0; + snapshot.geomBufferTransform = requested.bufferTransform; + snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse; + snapshot.geomContentCrop = requested.getBufferCrop(); + snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream(); + snapshot.hasProtectedContent = requested.externalTexture && + requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED; + snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ && + requested.api == NATIVE_WINDOW_API_MEDIA && + requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102; + snapshot.sidebandStream = requested.sidebandStream; + snapshot.surfaceDamage = requested.surfaceDamageRegion; + snapshot.transparentRegionHint = requested.transparentRegion; + } + + if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) { + snapshot.color.rgb = requested.getColor().rgb; + snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic; + snapshot.backgroundBlurRadius = static_cast<int>(requested.backgroundBlurRadius); + snapshot.blurRegions = requested.blurRegions; + snapshot.hdrMetadata = requested.hdrMetadata; + } + + if (forceUpdate || + snapshot.changes.any(RequestedLayerState::Changes::Hierarchy | + RequestedLayerState::Changes::Geometry)) { + updateLayerBounds(snapshot, requested, parentSnapshot, displayRotationFlags); + updateRoundedCorner(snapshot, requested, parentSnapshot); + } + + if (forceUpdate || + snapshot.changes.any(RequestedLayerState::Changes::Hierarchy | + RequestedLayerState::Changes::Geometry | + RequestedLayerState::Changes::Input)) { + static frontend::DisplayInfo sDefaultInfo = {.isSecure = false}; + const std::optional<frontend::DisplayInfo> displayInfo = + args.displays.get(snapshot.outputFilter.layerStack); + bool noValidDisplay = !displayInfo.has_value(); + updateInput(snapshot, requested, parentSnapshot, displayInfo.value_or(sDefaultInfo), + noValidDisplay, path); + } + + // computed snapshot properties + updateShadows(snapshot, requested, args.globalShadowSettings); + if (args.includeMetadata) { + snapshot.layerMetadata = parentSnapshot.layerMetadata; + snapshot.layerMetadata.merge(requested.metadata); + } + snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 || + requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect(); + snapshot.isVisible = snapshot.getIsVisible(); + snapshot.isOpaque = snapshot.isContentOpaque() && !snapshot.roundedCorner.hasRoundedCorners() && + snapshot.color.a == 1.f; + snapshot.blendMode = getBlendMode(snapshot, requested); + + ALOGV("%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s", + args.forceUpdate ? "Force " : "", requested.id, requested.name.c_str(), + parentSnapshot.changes.string().c_str(), snapshot.changes.string().c_str(), + requested.changes.string().c_str(), std::to_string(requested.what).c_str(), + snapshot.getDebugString().c_str(), parentSnapshot.getDebugString().c_str()); +} + +void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, + const RequestedLayerState& requested, + const LayerSnapshot& parentSnapshot) { + snapshot.roundedCorner = RoundedCornerState(); + RoundedCornerState parentRoundedCorner; + if (parentSnapshot.roundedCorner.hasRoundedCorners()) { + parentRoundedCorner = parentSnapshot.roundedCorner; + ui::Transform t = snapshot.localTransform.inverse(); + parentRoundedCorner.cropRect = t.transform(parentRoundedCorner.cropRect); + parentRoundedCorner.radius.x *= t.getScaleX(); + parentRoundedCorner.radius.y *= t.getScaleY(); + } + + FloatRect layerCropRect = snapshot.croppedBufferSize.toFloatRect(); + const vec2 radius(requested.cornerRadius, requested.cornerRadius); + RoundedCornerState layerSettings(layerCropRect, radius); + const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty(); + const bool parentRoundedCornerValid = parentRoundedCorner.hasRoundedCorners(); + if (layerSettingsValid && parentRoundedCornerValid) { + // If the parent and the layer have rounded corner settings, use the parent settings if + // the parent crop is entirely inside the layer crop. This has limitations and cause + // rendering artifacts. See b/200300845 for correct fix. + if (parentRoundedCorner.cropRect.left > layerCropRect.left && + parentRoundedCorner.cropRect.top > layerCropRect.top && + parentRoundedCorner.cropRect.right < layerCropRect.right && + parentRoundedCorner.cropRect.bottom < layerCropRect.bottom) { + snapshot.roundedCorner = parentRoundedCorner; + } else { + snapshot.roundedCorner = layerSettings; + } + } else if (layerSettingsValid) { + snapshot.roundedCorner = layerSettings; + } else if (parentRoundedCornerValid) { + snapshot.roundedCorner = parentRoundedCorner; + } +} + +void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot, + const RequestedLayerState& requested, + const LayerSnapshot& parentSnapshot, + uint32_t displayRotationFlags) { + snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize); + snapshot.geomCrop = requested.crop; + snapshot.localTransform = requested.getTransform(displayRotationFlags); + snapshot.localTransformInverse = snapshot.localTransform.inverse(); + snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform; + snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform); + if (snapshot.invalidTransform) { + ALOGW("Resetting transform for %s because it has an invalid transformation.", + requested.getDebugStringShort().c_str()); + snapshot.geomLayerTransform.reset(); + } + snapshot.geomInverseLayerTransform = snapshot.geomLayerTransform.inverse(); + + FloatRect parentBounds = parentSnapshot.geomLayerBounds; + parentBounds = snapshot.localTransform.inverse().transform(parentBounds); + snapshot.geomLayerBounds = + (requested.externalTexture) ? snapshot.bufferSize.toFloatRect() : parentBounds; + if (!requested.crop.isEmpty()) { + snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(requested.crop.toFloatRect()); + } + snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds); + snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds); + snapshot.parentTransform = parentSnapshot.geomLayerTransform; + + // Subtract the transparent region and snap to the bounds + Rect bounds = + RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion); + snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds); + + // TODO(b/238781169) use dest vs src + snapshot.bufferNeedsFiltering = snapshot.externalTexture && + getBufferNeedsFiltering(snapshot, + requested.getUnrotatedBufferSize(displayRotationFlags)); +} + +void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, + const RequestedLayerState& requested, + const renderengine::ShadowSettings& globalShadowSettings) { + snapshot.shadowRadius = requested.shadowRadius; + snapshot.shadowSettings.length = requested.shadowRadius; + if (snapshot.shadowRadius > 0.f) { + snapshot.shadowSettings = globalShadowSettings; + + // Note: this preserves existing behavior of shadowing the entire layer and not cropping + // it if transparent regions are present. This may not be necessary since shadows are + // typically cast by layers without transparent regions. + snapshot.shadowSettings.boundaries = snapshot.geomLayerBounds; + + // If the casting layer is translucent, we need to fill in the shadow underneath the + // layer. Otherwise the generated shadow will only be shown around the casting layer. + snapshot.shadowSettings.casterIsTranslucent = + !snapshot.isContentOpaque() || (snapshot.alpha < 1.0f); + snapshot.shadowSettings.ambientColor *= snapshot.alpha; + snapshot.shadowSettings.spotColor *= snapshot.alpha; + } +} + +void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, + const RequestedLayerState& requested, + const LayerSnapshot& parentSnapshot, + const frontend::DisplayInfo& displayInfo, + bool noValidDisplay, + const LayerHierarchy::TraversalPath& path) { + snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id); + if (!requested.hasInputInfo()) { + snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL; + return; + } + + fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot); + + if (noValidDisplay) { + // Do not let the window receive touches if it is not associated with a valid display + // transform. We still allow the window to receive keys and prevent ANRs. + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_TOUCHABLE; + } + + // For compatibility reasons we let layers which can receive input + // receive input before they have actually submitted a buffer. Because + // of this we use canReceiveInput instead of isVisible to check the + // policy-visibility, ignoring the buffer state. However for layers with + // hasInputInfo()==false we can use the real visibility state. + // 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 = requested.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible; + snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible); + + snapshot.inputInfo.alpha = snapshot.color.a; + snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode; + if (requested.dropInputMode == gui::DropInputMode::ALL || + parentSnapshot.dropInputMode == gui::DropInputMode::ALL) { + snapshot.dropInputMode = gui::DropInputMode::ALL; + } else if (requested.dropInputMode == gui::DropInputMode::OBSCURED || + parentSnapshot.dropInputMode == gui::DropInputMode::OBSCURED) { + snapshot.dropInputMode = gui::DropInputMode::OBSCURED; + } else { + snapshot.dropInputMode = gui::DropInputMode::NONE; + } + + handleDropInputMode(snapshot, parentSnapshot); + + // If the window will be blacked out on a display because the display does not have the secure + // flag and the layer has the secure flag set, then drop input. + if (!displayInfo.isSecure && snapshot.isSecure) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT; + } + + auto cropLayerSnapshot = getSnapshot(requested.touchCropId); + if (snapshot.inputInfo.replaceTouchableRegionWithCrop) { + const Rect bounds(cropLayerSnapshot ? cropLayerSnapshot->transformedBounds + : snapshot.transformedBounds); + snapshot.inputInfo.touchableRegion = Region(displayInfo.transform.transform(bounds)); + } else if (cropLayerSnapshot) { + snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect( + displayInfo.transform.transform(Rect{cropLayerSnapshot->transformedBounds})); + } + + // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state + // if it was set by WM for a known system overlay + if (snapshot.isTrustedOverlay) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY; + } + + // 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 (path.isClone()) { + snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE; + auto clonedRootSnapshot = getSnapshot(path.mirrorRootIds.back()); + if (clonedRootSnapshot) { + const Rect rect = + displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds}); + snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect(rect); + } + } +} + +std::vector<std::unique_ptr<LayerSnapshot>>& LayerSnapshotBuilder::getSnapshots() { + return mSnapshots; +} + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h new file mode 100644 index 0000000000..33b250c793 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h @@ -0,0 +1,110 @@ +/* + * Copyright 2022 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 + +#include "Display/DisplayMap.h" +#include "FrontEnd/DisplayInfo.h" +#include "FrontEnd/LayerLifecycleManager.h" +#include "LayerHierarchy.h" +#include "LayerSnapshot.h" +#include "RequestedLayerState.h" + +namespace android::surfaceflinger::frontend { + +// Walks through the layer hierarchy to build an ordered list +// of LayerSnapshots that can be passed on to CompositionEngine. +// This builder does a minimum amount of work to update +// an existing set of snapshots based on hierarchy changes +// and RequestedLayerState changes. + +// The builder also uses a fast path to update +// snapshots when there are only buffer updates. +class LayerSnapshotBuilder { +public: + struct Args { + const LayerHierarchy& root; + const LayerLifecycleManager& layerLifecycleManager; + bool forceUpdate = false; + bool includeMetadata = false; + const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays; + // Set to true if there were display changes since last update. + bool displayChanges = false; + const renderengine::ShadowSettings& globalShadowSettings; + }; + LayerSnapshotBuilder(); + + // Rebuild the snapshots from scratch. + LayerSnapshotBuilder(Args); + + // Update an existing set of snapshot using change flags in RequestedLayerState + // and LayerLifecycleManager. This needs to be called before + // LayerLifecycleManager.commitChanges is called as that function will clear all + // change flags. + void update(const Args&); + std::vector<std::unique_ptr<LayerSnapshot>>& getSnapshots(); + +private: + friend class LayerSnapshotTest; + LayerSnapshot* getSnapshot(uint32_t layerId) const; + LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const; + static LayerSnapshot getRootSnapshot(); + + // return true if we were able to successfully update the snapshots via + // the fast path. + bool tryFastUpdate(const Args& args); + + void updateSnapshots(const Args& args); + + void updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy, + LayerHierarchy::TraversalPath& traversalPath, + const LayerSnapshot& parentSnapshot); + void updateSnapshot(LayerSnapshot& snapshot, const Args& args, const RequestedLayerState&, + const LayerSnapshot& parentSnapshot, + const LayerHierarchy::TraversalPath& path); + static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot, + bool parentIsRelative, const Args& args); + static void resetRelativeState(LayerSnapshot& snapshot); + static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState, + const LayerSnapshot& parentSnapshot); + static void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState, + const LayerSnapshot& parentSnapshot, + uint32_t displayRotationFlags); + static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested, + const renderengine::ShadowSettings& globalShadowSettings); + void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested, + const LayerSnapshot& parentSnapshot, const frontend::DisplayInfo& displayInfo, + bool noValidDisplay, const LayerHierarchy::TraversalPath& path); + void sortSnapshotsByZ(const Args& args); + LayerSnapshot* getOrCreateSnapshot(const LayerHierarchy::TraversalPath& id, + const RequestedLayerState& layer); + + 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; + } + return std::hash<size_t>{}(hashCode); + } + }; + std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*, TraversalPathHash> + mIdToSnapshot; + std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots; + LayerSnapshot mRootSnapshot; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 054382cd06..dcc16e8db7 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -118,7 +118,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) { bool oldFlags = flags; - Rect oldBufferSize = getBufferSize(); + Rect oldBufferSize = getBufferSize(0); const layer_state_t& clientState = resolvedComposerState.state; uint64_t clientChanges = what | layer_state_t::diff(clientState); @@ -133,7 +133,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::Geometry; } } - if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize()) { + if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize(0)) { changes |= RequestedLayerState::Changes::Geometry; } if (clientChanges & layer_state_t::HIERARCHY_CHANGES) @@ -142,6 +142,8 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::Content; if (clientChanges & layer_state_t::GEOMETRY_CHANGES) changes |= RequestedLayerState::Changes::Geometry; + if (clientChanges & layer_state_t::AFFECTS_CHILDREN) + changes |= RequestedLayerState::Changes::AffectsChildren; if (clientState.what & layer_state_t::eColorTransformChanged) { static const mat4 identityMatrix = mat4(); @@ -205,7 +207,22 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta } } -ui::Transform RequestedLayerState::getTransform() const { +ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const { + uint32_t bufferWidth = externalTexture->getWidth(); + uint32_t bufferHeight = externalTexture->getHeight(); + // Undo any transformations on the buffer. + if (bufferTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + if (transformToDisplayInverse) { + if (displayRotationFlags & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + return {bufferWidth, bufferHeight}; +} + +ui::Transform RequestedLayerState::getTransform(uint32_t displayRotationFlags) const { if ((flags & layer_state_t::eIgnoreDestinationFrame) || destinationFrame.isEmpty()) { // If destination frame is not set, use the requested transform set via // Transaction::setPosition and Transaction::setMatrix. @@ -230,22 +247,10 @@ ui::Transform RequestedLayerState::getTransform() const { return transform; } - uint32_t bufferWidth = externalTexture->getWidth(); - uint32_t bufferHeight = externalTexture->getHeight(); - // Undo any transformations on the buffer. - if (bufferTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - // TODO(b/238781169) remove dep - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); - if (transformToDisplayInverse) { - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - } + ui::Size bufferSize = getUnrotatedBufferSize(displayRotationFlags); - float sx = static_cast<float>(destW) / static_cast<float>(bufferWidth); - float sy = static_cast<float>(destH) / static_cast<float>(bufferHeight); + float sx = static_cast<float>(destW) / static_cast<float>(bufferSize.width); + float sy = static_cast<float>(destH) / static_cast<float>(bufferSize.height); ui::Transform transform; transform.set(sx, 0, 0, sy); transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top)); @@ -279,7 +284,7 @@ half4 RequestedLayerState::getColor() const { } return color; } -Rect RequestedLayerState::getBufferSize() const { +Rect RequestedLayerState::getBufferSize(uint32_t displayRotationFlags) const { // for buffer state layers we use the display frame size as the buffer size. if (!externalTexture) { return Rect::INVALID_RECT; @@ -294,8 +299,7 @@ Rect RequestedLayerState::getBufferSize() const { } if (transformToDisplayInverse) { - // TODO(b/238781169) pass in display metrics (would be useful for input info as well - uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + uint32_t invTransform = displayRotationFlags; if (invTransform & ui::Transform::ROT_90) { std::swap(bufWidth, bufHeight); } @@ -304,8 +308,8 @@ Rect RequestedLayerState::getBufferSize() const { return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); } -Rect RequestedLayerState::getCroppedBufferSize() const { - Rect size = getBufferSize(); +Rect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const { + Rect size = bufferSize; if (!crop.isEmpty() && size.isValid()) { size.intersect(crop, &size); } else if (!crop.isEmpty()) { @@ -367,4 +371,13 @@ bool RequestedLayerState::hasValidRelativeParent() const { return isRelativeOf && parentId != relativeParentId; } +bool RequestedLayerState::hasInputInfo() const { + if (!windowInfoHandle) { + return false; + } + const auto windowInfo = windowInfoHandle->getInfo(); + return windowInfo->token != nullptr || + windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 78491655be..95240d0682 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -47,22 +47,26 @@ struct RequestedLayerState : layer_state_t { RelativeParent = 1u << 9, Metadata = 1u << 10, Visibility = 1u << 11, + AffectsChildren = 1u << 12, }; static Rect reduce(const Rect& win, const Region& exclude); RequestedLayerState(const LayerCreationArgs&); void merge(const ResolvedComposerState&); - ui::Transform getTransform() const; + // Currently we only care about the primary display + ui::Transform getTransform(uint32_t displayRotationFlags) const; + ui::Size getUnrotatedBufferSize(uint32_t displayRotationFlags) const; bool canBeDestroyed() const; bool isRoot() const; bool isHiddenByPolicy() const; half4 getColor() const; - Rect getBufferSize() const; - Rect getCroppedBufferSize() const; + Rect getBufferSize(uint32_t displayRotationFlags) const; + Rect getCroppedBufferSize(const Rect& bufferSize) const; Rect getBufferCrop() const; std::string getDebugString() const; std::string getDebugStringShort() const; aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; bool hasValidRelativeParent() const; + bool hasInputInfo() 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 diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index de9ea0420c..0179d62f08 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -128,6 +128,8 @@ TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate using namespace ftl::flag_operators; using base::StringAppendF; +using frontend::LayerSnapshot; +using frontend::RoundedCornerState; using gui::GameMode; using gui::LayerMetadata; using gui::WindowInfo; @@ -208,7 +210,7 @@ Layer::Layer(const LayerCreationArgs& args) mSnapshot->name = getDebugName(); mSnapshot->textureName = mTextureName; mSnapshot->premultipliedAlpha = mPremultipliedAlpha; - mSnapshot->transform = {}; + mSnapshot->parentTransform = {}; } void Layer::onFirstRef() { @@ -473,7 +475,7 @@ void Layer::prepareBasicGeometryCompositionState() { snapshot->geomLayerTransform = getTransform(); snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse(); snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState); - snapshot->blurRegionTransform = getActiveTransform(drawingState).inverse(); + snapshot->localTransformInverse = getActiveTransform(drawingState).inverse(); snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); snapshot->alpha = alpha; snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius; @@ -3526,7 +3528,7 @@ bool Layer::isOpaque(const Layer::State& s) const { } // If the buffer has no alpha channel, then we are opaque - if (hasBufferOrSidebandStream() && isOpaqueFormat(getPixelFormat())) { + if (hasBufferOrSidebandStream() && LayerSnapshot::isOpaqueFormat(getPixelFormat())) { return true; } @@ -3700,29 +3702,6 @@ bool Layer::isProtected() const { (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); } -// As documented in libhardware header, formats in the range -// 0x100 - 0x1FF are specific to the HAL implementation, and -// are known to have no alpha channel -// TODO: move definition for device-specific range into -// hardware.h, instead of using hard-coded values here. -#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) - -bool Layer::isOpaqueFormat(PixelFormat format) { - if (HARDWARE_IS_DEVICE_FORMAT(format)) { - return true; - } - switch (format) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_BGRA_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_R_8: - return false; - } - // in all other case, we have no blending (also for unknown formats) - return true; -} - bool Layer::needsFiltering(const DisplayDevice* display) const { if (!hasBufferOrSidebandStream()) { return false; @@ -3913,13 +3892,15 @@ void Layer::updateSnapshot(bool updateGeometry) { snapshot->shadowSettings.length = mEffectiveShadowRadius; } snapshot->contentOpaque = isOpaque(mDrawingState); + snapshot->layerOpaqueFlagSet = + (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque; snapshot->isHdrY410 = isHdrY410(); snapshot->bufferNeedsFiltering = bufferNeedsFiltering(); sp<Layer> p = mDrawingParent.promote(); if (p != nullptr) { - snapshot->transform = p->getTransform(); + snapshot->parentTransform = p->getTransform(); } else { - snapshot->transform.reset(); + snapshot->parentTransform.reset(); } snapshot->bufferSize = getBufferSize(mDrawingState); snapshot->externalTexture = mBufferInfo.mBuffer; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 08a13a34eb..8281140e72 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -324,8 +324,8 @@ public: virtual sp<LayerFE> getCompositionEngineLayerFE() const; virtual sp<LayerFE> copyCompositionEngineLayerFE() const; - const LayerSnapshot* getLayerSnapshot() const; - LayerSnapshot* editLayerSnapshot(); + const frontend::LayerSnapshot* getLayerSnapshot() const; + frontend::LayerSnapshot* editLayerSnapshot(); // If we have received a new buffer this frame, we will pass its surface // damage down to hardware composer. Otherwise, we must send a region with @@ -464,7 +464,7 @@ public: // Returns how rounded corners should be drawn for this layer. // A layer can override its parent's rounded corner settings if the parent's rounded // corner crop does not intersect with its own rounded corner crop. - virtual RoundedCornerState getRoundedCornerState() const; + virtual frontend::RoundedCornerState getRoundedCornerState() const; bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); } @@ -746,12 +746,12 @@ public: */ bool hasInputInfo() const; - // Sets the GameMode for the tree rooted at this layer. A layer in the tree inherits this - // GameMode unless it (or an ancestor) has GAME_MODE_METADATA. - void setGameModeForTree(GameMode); + // Sets the gui::GameMode for the tree rooted at this layer. A layer in the tree inherits this + // gui::GameMode unless it (or an ancestor) has GAME_MODE_METADATA. + void setGameModeForTree(gui::GameMode); - void setGameMode(GameMode gameMode) { mGameMode = gameMode; } - GameMode getGameMode() const { return mGameMode; } + void setGameMode(gui::GameMode gameMode) { mGameMode = gameMode; } + gui::GameMode getGameMode() const { return mGameMode; } virtual uid_t getOwnerUid() const { return mOwnerUid; } @@ -827,7 +827,9 @@ protected: void gatherBufferInfo(); void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&); - sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; } + sp<Layer> getClonedFrom() const { + return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; + } bool isClone() { return mClonedFrom != nullptr; } bool isClonedFromAlive() { return getClonedFrom() != nullptr; } @@ -1064,7 +1066,7 @@ private: float mEffectiveShadowRadius = 0.f; // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats. - GameMode mGameMode = GameMode::Unsupported; + gui::GameMode mGameMode = gui::GameMode::Unsupported; // A list of regions on this layer that should have blurs. const std::vector<BlurRegion> getBlurRegions() const; @@ -1120,7 +1122,8 @@ private: ui::Transform mRequestedTransform; sp<LayerFE> mLayerFE; - std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>(); + std::unique_ptr<frontend::LayerSnapshot> mSnapshot = + std::make_unique<frontend::LayerSnapshot>(); friend class LayerSnapshotGuard; }; diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index 363adc641e..c31a2e3cfc 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -148,14 +148,14 @@ std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientC case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled: layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; layerSettings.blurRegions = mSnapshot->blurRegions; - layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4(); + layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4(); break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly: layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly: layerSettings.blurRegions = mSnapshot->blurRegions; - layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4(); + layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4(); break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled: default: @@ -275,7 +275,7 @@ void LayerFE::prepareBufferStateClientComposition( * of a camera where the buffer remains in native orientation, * we want the pixels to always be upright. */ - const auto parentTransform = mSnapshot->transform; + const auto parentTransform = mSnapshot->parentTransform; tr = tr * inverseOrientation(parentTransform.getOrientation()); // and finally apply it to the original texture matrix diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index 822bcb7a94..01da0199a2 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -17,48 +17,12 @@ #pragma once #include <gui/LayerMetadata.h> - +#include "FrontEnd/LayerSnapshot.h" #include "compositionengine/LayerFE.h" #include "compositionengine/LayerFECompositionState.h" #include "renderengine/LayerSettings.h" namespace android { -struct RoundedCornerState { - RoundedCornerState() = default; - RoundedCornerState(const FloatRect& cropRect, const vec2& radius) - : cropRect(cropRect), radius(radius) {} - - // Rounded rectangle in local layer coordinate space. - FloatRect cropRect = FloatRect(); - // Radius of the rounded rectangle. - vec2 radius; - bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; } -}; - -// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition -// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings -// passed to Render Engine are created using properties stored on this struct. -struct LayerSnapshot : public compositionengine::LayerFECompositionState { - int32_t sequence; - std::string name; - uint32_t textureName; - bool contentOpaque; - RoundedCornerState roundedCorner; - StretchEffect stretchEffect; - FloatRect transformedBounds; - renderengine::ShadowSettings shadowSettings; - bool premultipliedAlpha; - bool isHdrY410; - bool bufferNeedsFiltering; - ui::Transform transform; - Rect bufferSize; - std::shared_ptr<renderengine::ExternalTexture> externalTexture; - gui::LayerMetadata layerMetadata; - gui::LayerMetadata relativeLayerMetadata; - bool contentDirty; - bool hasReadyFrame; - ui::Transform blurRegionTransform; -}; struct CompositionResult { // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition @@ -86,7 +50,7 @@ public: compositionengine::LayerFE::ClientCompositionTargetSettings&) const; CompositionResult&& stealCompositionResult(); - std::unique_ptr<LayerSnapshot> mSnapshot; + std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot; private: std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal( diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 824899b422..87c3c65a74 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -94,6 +94,7 @@ cc_test { "LayerMetadataTest.cpp", "LayerHierarchyTest.cpp", "LayerLifecycleManagerTest.cpp", + "LayerSnapshotTest.cpp", "LayerTest.cpp", "LayerTestUtils.cpp", "MessageQueueTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp index 8560902e4e..33c94400d6 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp @@ -21,6 +21,7 @@ #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" #include "Layer.h" +#include "LayerHierarchyTest.h" #include "gui/SurfaceComposerClient.h" #define UPDATE_AND_VERIFY(HIERARCHY) \ @@ -31,16 +32,6 @@ namespace android::surfaceflinger::frontend { -namespace { -LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) { - LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); - args.addToRoot = canBeRoot; - args.parentHandle = parent; - args.mirrorLayerHandle = mirror; - return args; -} -} // namespace - // To run test: /** mp :libsurfaceflinger_unittest && adb sync; adb shell \ @@ -50,163 +41,9 @@ LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp --gtest_brief=1 */ -class LayerHierarchyTest : public testing::Test { +class LayerHierarchyTest : public LayerHierarchyTestBase { protected: - LayerHierarchyTest() { - // tree with 3 levels of children - // ROOT - // ├── 1 - // │ ├── 11 - // │ │ └── 111 - // │ ├── 12 - // │ │ ├── 121 - // │ │ └── 122 - // │ │ └── 1221 - // │ └── 13 - // └── 2 - - createRootLayer(1); - createRootLayer(2); - createLayer(11, 1); - createLayer(12, 1); - createLayer(13, 1); - createLayer(111, 11); - createLayer(121, 12); - createLayer(122, 12); - createLayer(1221, 122); - mLifecycleManager.commitChanges(); - } - std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const { - std::vector<uint32_t> layerIds; - hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy, - const LayerHierarchy::TraversalPath&) -> bool { - layerIds.emplace_back(hierarchy.getLayer()->id); - return true; - }); - return layerIds; - } - - std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const { - std::vector<uint32_t> layerIds; - hierarchy.traverseInZOrder( - [&layerIds = layerIds](const LayerHierarchy& hierarchy, - const LayerHierarchy::TraversalPath&) -> bool { - layerIds.emplace_back(hierarchy.getLayer()->id); - return true; - }); - return layerIds; - } - - void createRootLayer(uint32_t id) { - sp<LayerHandle> handle = sp<LayerHandle>::make(id); - mHandles[id] = handle; - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr))); - mLifecycleManager.addLayers(std::move(layers)); - } - - void createLayer(uint32_t id, uint32_t parentId) { - sp<LayerHandle> handle = sp<LayerHandle>::make(id); - mHandles[id] = handle; - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId], - /*mirror=*/nullptr))); - mLifecycleManager.addLayers(std::move(layers)); - } - - void reparentLayer(uint32_t id, uint32_t newParentId) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - if (newParentId == UNASSIGNED_LAYER_ID) { - transactions.back().states.front().state.parentSurfaceControlForChild = nullptr; - } else { - auto parentHandle = mHandles[newParentId]; - transactions.back().states.front().state.parentSurfaceControlForChild = - sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle, - static_cast<int32_t>(newParentId), "Test"); - } - transactions.back().states.front().state.what = layer_state_t::eReparent; - transactions.back().states.front().state.surface = mHandles[id]; - mLifecycleManager.applyTransactions(transactions); - } - - void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - if (relativeParentId == UNASSIGNED_LAYER_ID) { - transactions.back().states.front().state.what = layer_state_t::eLayerChanged; - } else { - auto parentHandle = mHandles[relativeParentId]; - transactions.back().states.front().state.relativeLayerSurfaceControl = - sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle, - static_cast<int32_t>(relativeParentId), "test"); - transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged; - } - transactions.back().states.front().state.surface = mHandles[id]; - mLifecycleManager.applyTransactions(transactions); - } - - void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) { - auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent]; - auto mirrorHandle = - (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror]; - - sp<LayerHandle> handle = sp<LayerHandle>::make(id); - mHandles[id] = handle; - std::vector<std::unique_ptr<RequestedLayerState>> layers; - layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle, - /*mirror=*/mHandles[layerToMirror]))); - mLifecycleManager.addLayers(std::move(layers)); - } - - void updateBackgroundColor(uint32_t id, half alpha) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; - transactions.back().states.front().state.bgColorAlpha = alpha; - transactions.back().states.front().state.surface = mHandles[id]; - mLifecycleManager.applyTransactions(transactions); - } - - void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); } - - void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) { - if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { - hierarchyBuilder.update(mLifecycleManager.getLayers(), - mLifecycleManager.getDestroyedLayers()); - } - mLifecycleManager.commitChanges(); - - // rebuild layer hierarchy from scratch and verify that it matches the updated state. - LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers()); - EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), - getTraversalPath(newBuilder.getHierarchy())); - EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), - getTraversalPathInZOrder(newBuilder.getHierarchy())); - EXPECT_FALSE( - mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); - } - - void setZ(uint32_t id, int32_t z) { - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - - transactions.back().states.front().state.what = layer_state_t::eLayerChanged; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); - transactions.back().states.front().state.z = z; - mLifecycleManager.applyTransactions(transactions); - } - LayerLifecycleManager mLifecycleManager; - std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; + LayerHierarchyTest() : LayerHierarchyTestBase() { mLifecycleManager.commitChanges(); } }; // reparenting tests diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h new file mode 100644 index 0000000000..08727f2c17 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -0,0 +1,252 @@ +/* + * Copyright 2022 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "FrontEnd/LayerHandle.h" +#include "FrontEnd/LayerHierarchy.h" +#include "FrontEnd/LayerLifecycleManager.h" +#include "Layer.h" +#include "gui/SurfaceComposerClient.h" + +namespace android::surfaceflinger::frontend { + +class LayerHierarchyTestBase : public testing::Test { +protected: + LayerHierarchyTestBase() { + // tree with 3 levels of children + // ROOT + // ├── 1 + // │ ├── 11 + // │ │ └── 111 + // │ ├── 12 + // │ │ ├── 121 + // │ │ └── 122 + // │ │ └── 1221 + // │ └── 13 + // └── 2 + + createRootLayer(1); + createRootLayer(2); + createLayer(11, 1); + createLayer(12, 1); + createLayer(13, 1); + createLayer(111, 11); + createLayer(121, 12); + createLayer(122, 12); + createLayer(1221, 122); + } + + LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, + wp<IBinder> mirror) { + LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); + args.addToRoot = canBeRoot; + args.parentHandle = parent; + args.mirrorLayerHandle = mirror; + return args; + } + + std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const { + std::vector<uint32_t> layerIds; + hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath&) -> bool { + layerIds.emplace_back(hierarchy.getLayer()->id); + return true; + }); + return layerIds; + } + + std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const { + std::vector<uint32_t> layerIds; + hierarchy.traverseInZOrder( + [&layerIds = layerIds](const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath&) -> bool { + layerIds.emplace_back(hierarchy.getLayer()->id); + return true; + }); + return layerIds; + } + + virtual void createRootLayer(uint32_t id) { + sp<LayerHandle> handle = sp<LayerHandle>::make(id); + mHandles[id] = handle; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr))); + mLifecycleManager.addLayers(std::move(layers)); + } + + virtual void createLayer(uint32_t id, uint32_t parentId) { + sp<LayerHandle> handle = sp<LayerHandle>::make(id); + mHandles[id] = handle; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId], + /*mirror=*/nullptr))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void reparentLayer(uint32_t id, uint32_t newParentId) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + if (newParentId == UNASSIGNED_LAYER_ID) { + transactions.back().states.front().state.parentSurfaceControlForChild = nullptr; + } else { + auto parentHandle = mHandles[newParentId]; + transactions.back().states.front().state.parentSurfaceControlForChild = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle, + static_cast<int32_t>(newParentId), "Test"); + } + transactions.back().states.front().state.what = layer_state_t::eReparent; + transactions.back().states.front().state.surface = mHandles[id]; + mLifecycleManager.applyTransactions(transactions); + } + + void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + if (relativeParentId == UNASSIGNED_LAYER_ID) { + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + } else { + auto parentHandle = mHandles[relativeParentId]; + transactions.back().states.front().state.relativeLayerSurfaceControl = + sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle, + static_cast<int32_t>(relativeParentId), "test"); + transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged; + } + transactions.back().states.front().state.surface = mHandles[id]; + mLifecycleManager.applyTransactions(transactions); + } + + virtual void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) { + auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent]; + auto mirrorHandle = + (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror]; + + sp<LayerHandle> handle = sp<LayerHandle>::make(id); + mHandles[id] = handle; + std::vector<std::unique_ptr<RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle, + /*mirror=*/mHandles[layerToMirror]))); + mLifecycleManager.addLayers(std::move(layers)); + } + + void updateBackgroundColor(uint32_t id, half alpha) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; + transactions.back().states.front().state.bgColorAlpha = alpha; + transactions.back().states.front().state.surface = mHandles[id]; + mLifecycleManager.applyTransactions(transactions); + } + + void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); } + + void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) { + if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { + hierarchyBuilder.update(mLifecycleManager.getLayers(), + mLifecycleManager.getDestroyedLayers()); + } + mLifecycleManager.commitChanges(); + + // rebuild layer hierarchy from scratch and verify that it matches the updated state. + LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers()); + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), + getTraversalPath(newBuilder.getHierarchy())); + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), + getTraversalPathInZOrder(newBuilder.getHierarchy())); + EXPECT_FALSE( + mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); + } + + void setZ(uint32_t id, int32_t z) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eLayerChanged; + transactions.back().states.front().state.surface = mHandles[id]; + transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().state.z = z; + mLifecycleManager.applyTransactions(transactions); + } + + void setCrop(uint32_t id, const Rect& crop) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eCropChanged; + transactions.back().states.front().state.surface = mHandles[id]; + transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().state.crop = crop; + mLifecycleManager.applyTransactions(transactions); + } + + void setFlags(uint32_t id, uint32_t mask, uint32_t flags) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eFlagsChanged; + transactions.back().states.front().state.flags = flags; + transactions.back().states.front().state.mask = mask; + transactions.back().states.front().state.surface = mHandles[id]; + transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + mLifecycleManager.applyTransactions(transactions); + } + + void setAlpha(uint32_t id, float alpha) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eAlphaChanged; + transactions.back().states.front().state.surface = mHandles[id]; + transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().state.color.a = static_cast<half>(alpha); + mLifecycleManager.applyTransactions(transactions); + } + + void hideLayer(uint32_t id) { + setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); + } + + void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); } + + void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eColorChanged; + transactions.back().states.front().state.color.rgb = rgb; + transactions.back().states.front().state.surface = mHandles[id]; + transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + mLifecycleManager.applyTransactions(transactions); + } + + LayerLifecycleManager mLifecycleManager; + std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; +}; + +} // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp new file mode 100644 index 0000000000..2441c065c0 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -0,0 +1,260 @@ +/* + * Copyright 2022 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "FrontEnd/LayerHandle.h" +#include "FrontEnd/LayerHierarchy.h" +#include "FrontEnd/LayerLifecycleManager.h" +#include "FrontEnd/LayerSnapshotBuilder.h" +#include "Layer.h" +#include "LayerHierarchyTest.h" + +#define UPDATE_AND_VERIFY(BUILDER, ...) \ + ({ \ + SCOPED_TRACE(""); \ + updateAndVerify((BUILDER), /*displayChanges=*/false, __VA_ARGS__); \ + }) + +#define UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(BUILDER, ...) \ + ({ \ + SCOPED_TRACE(""); \ + updateAndVerify((BUILDER), /*displayChanges=*/true, __VA_ARGS__); \ + }) + +namespace android::surfaceflinger::frontend { + +// To run test: +/** + mp :libsurfaceflinger_unittest && adb sync; adb shell \ + /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \ + --gtest_filter="LayerSnapshotTest.*" --gtest_brief=1 +*/ + +class LayerSnapshotTest : public LayerHierarchyTestBase { +protected: + LayerSnapshotTest() : LayerHierarchyTestBase() { + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + } + + void createRootLayer(uint32_t id) override { + LayerHierarchyTestBase::createRootLayer(id); + setColor(id); + } + + void createLayer(uint32_t id, uint32_t parentId) override { + LayerHierarchyTestBase::createLayer(id, parentId); + setColor(parentId); + } + + void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override { + LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror); + setColor(id); + } + + void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges, + const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) { + if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { + mHierarchyBuilder.update(mLifecycleManager.getLayers(), + mLifecycleManager.getDestroyedLayers()); + } + LayerSnapshotBuilder::Args args{ + .root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .displayChanges = hasDisplayChanges, + .globalShadowSettings = globalShadowSettings, + }; + actualBuilder.update(args); + + // rebuild layer snapshots from scratch and verify that it matches the updated state. + LayerSnapshotBuilder expectedBuilder(args); + mLifecycleManager.commitChanges(); + ASSERT_TRUE(expectedBuilder.getSnapshots().size() > 0); + ASSERT_TRUE(actualBuilder.getSnapshots().size() > 0); + + std::vector<std::unique_ptr<LayerSnapshot>>& snapshots = actualBuilder.getSnapshots(); + std::vector<uint32_t> actualVisibleLayerIdsInZOrder; + for (auto& snapshot : snapshots) { + if (!snapshot->isVisible) { + break; + } + actualVisibleLayerIdsInZOrder.push_back(snapshot->path.id); + } + EXPECT_EQ(expectedVisibleLayerIdsInZOrder, actualVisibleLayerIdsInZOrder); + } + + LayerSnapshot* getSnapshot(uint32_t layerId) { return mSnapshotBuilder.getSnapshot(layerId); } + + LayerHierarchyBuilder mHierarchyBuilder{{}}; + LayerSnapshotBuilder mSnapshotBuilder; + std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; + display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos; + renderengine::ShadowSettings globalShadowSettings; + static const std::vector<uint32_t> STARTING_ZORDER; +}; +const std::vector<uint32_t> LayerSnapshotTest::STARTING_ZORDER = {1, 11, 111, 12, 121, + 122, 1221, 13, 2}; + +TEST_F(LayerSnapshotTest, buildSnapshot) { + LayerSnapshotBuilder::Args args{ + .root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + }; + LayerSnapshotBuilder builder(args); +} + +TEST_F(LayerSnapshotTest, updateSnapshot) { + LayerSnapshotBuilder::Args args{ + .root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + }; + + LayerSnapshotBuilder builder; + builder.update(args); +} + +// update using parent snapshot data +TEST_F(LayerSnapshotTest, croppedByParent) { + /// MAKE ALL LAYERS VISIBLE BY DEFAULT + DisplayInfo info; + info.info.logicalHeight = 100; + info.info.logicalWidth = 200; + mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info); + Rect layerCrop(0, 0, 10, 20); + setCrop(11, layerCrop); + EXPECT_TRUE(mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Geometry)); + UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(11)->geomCrop, layerCrop); + EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop.toFloatRect()); + float maxHeight = static_cast<float>(info.info.logicalHeight * 10); + float maxWidth = static_cast<float>(info.info.logicalWidth * 10); + + FloatRect maxDisplaySize(-maxWidth, -maxHeight, maxWidth, maxHeight); + EXPECT_EQ(getSnapshot(1)->geomLayerBounds, maxDisplaySize); +} + +// visibility tests +TEST_F(LayerSnapshotTest, newLayerHiddenByPolicy) { + createLayer(112, 11); + hideLayer(112); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + showLayer(112); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 111, 112, 12, 121, 122, 1221, 13, 2}); +} + +TEST_F(LayerSnapshotTest, hiddenByParent) { + hideLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2}); +} + +TEST_F(LayerSnapshotTest, reparentShowsChild) { + hideLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2}); + + showLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); +} + +TEST_F(LayerSnapshotTest, reparentHidesChild) { + hideLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2}); + + reparentLayer(121, 11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 122, 1221, 13, 2}); +} + +TEST_F(LayerSnapshotTest, unHidingUpdatesSnapshot) { + hideLayer(11); + Rect crop(1, 2, 3, 4); + setCrop(111, crop); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2}); + + showLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(111)->geomLayerBounds, crop.toFloatRect()); +} + +TEST_F(LayerSnapshotTest, childBehindParentCanBeHiddenByParent) { + setZ(111, -1); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 111, 11, 12, 121, 122, 1221, 13, 2}); + + hideLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2}); +} + +// relative tests +TEST_F(LayerSnapshotTest, RelativeParentCanHideChild) { + reparentRelativeLayer(13, 11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 13, 111, 12, 121, 122, 1221, 2}); + + hideLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 2}); +} + +TEST_F(LayerSnapshotTest, ReparentingToHiddenRelativeParentHidesChild) { + hideLayer(11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2}); + reparentRelativeLayer(13, 11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 2}); +} + +TEST_F(LayerSnapshotTest, AlphaInheritedByChildren) { + setAlpha(1, 0.5); + setAlpha(122, 0.5); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(12)->alpha, 0.5f); + EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f); +} + +// Change states +TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) { + setCrop(1, Rect(1, 2, 3, 4)); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->changes.get() != 0); + EXPECT_TRUE(getSnapshot(11)->changes.get() != 0); + setCrop(2, Rect(1, 2, 3, 4)); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(2)->changes.get() != 0); + EXPECT_TRUE(getSnapshot(1)->changes.get() == 0); + EXPECT_TRUE(getSnapshot(11)->changes.get() == 0); +} + +TEST_F(LayerSnapshotTest, FastPathClearsPreviousChangeStates) { + setColor(11, {1._hf, 0._hf, 0._hf}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(11)->changes.get() != 0); + EXPECT_TRUE(getSnapshot(1)->changes.get() == 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(11)->changes.get() == 0); +} + +TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) { + setColor(1, {1._hf, 0._hf, 0._hf}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content); +} + +} // namespace android::surfaceflinger::frontend |