213 lines
9.4 KiB
C++
213 lines
9.4 KiB
C++
/*
|
|
* 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 "FrontEnd/LayerCreationArgs.h"
|
|
#include "RequestedLayerState.h"
|
|
#include "ftl/small_vector.h"
|
|
|
|
namespace android::surfaceflinger::frontend {
|
|
class LayerHierarchyBuilder;
|
|
|
|
// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
|
|
// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
|
|
// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
|
|
//
|
|
// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
|
|
// traversing the hierarchy, a new concept called Variant can be used to understand the
|
|
// relationship of the layer to its parent. The following variants are possible:
|
|
// Attached - child of the parent
|
|
// Detached - child of the parent but currently relative parented to another layer
|
|
// Relative - relative child of the parent
|
|
// Mirror - mirrored from another layer
|
|
//
|
|
// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
|
|
// cloning the layer requested state. The mirrored hierarchy and its corresponding
|
|
// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
|
|
// states.
|
|
class LayerHierarchy {
|
|
public:
|
|
enum Variant : uint32_t {
|
|
Attached,
|
|
Detached,
|
|
Relative,
|
|
Mirror,
|
|
ftl_first = Attached,
|
|
ftl_last = Mirror,
|
|
};
|
|
// Represents a unique path to a node.
|
|
// The layer hierarchy is represented as a graph. Each node can be visited by multiple parents.
|
|
// This allows us to represent mirroring in an efficient way. See the example below:
|
|
// root
|
|
// ├─ A {Traversal path id = 1}
|
|
// ├─ B {Traversal path id = 2}
|
|
// │ ├─ C {Traversal path id = 3}
|
|
// │ ├─ D {Traversal path id = 4}
|
|
// │ └─ E {Traversal path id = 5}
|
|
// ├─ F (Mirrors B) {Traversal path id = 6}
|
|
// └─ G (Mirrors F) {Traversal path id = 7}
|
|
//
|
|
// C, D and E can be traversed via B or via F then B or via G then F then B.
|
|
// Depending on how the node is reached, its properties such as geometry or visibility might be
|
|
// different. And we can uniquely identify the node by keeping track of the nodes leading up to
|
|
// it. But to be more efficient we only need to track the nodes id and the top mirror root path.
|
|
// So C for example, would have the following unique traversal paths:
|
|
// - {Traversal path id = 3}
|
|
// - {Traversal path id = 3, mirrorRootId = 6}
|
|
// - {Traversal path id = 3, mirrorRootId = 7}
|
|
|
|
struct TraversalPath {
|
|
uint32_t id;
|
|
LayerHierarchy::Variant variant;
|
|
// Mirrored layers can have a different geometry than their parents so we need to track
|
|
// the mirror roots in the traversal.
|
|
uint32_t mirrorRootId = UNASSIGNED_LAYER_ID;
|
|
// Relative layers can be visited twice, once by their parent and then once again by
|
|
// their relative parent. We keep track of the roots here to detect any loops in the
|
|
// hierarchy. If a relative root already exists in the list while building the
|
|
// TraversalPath, it means that somewhere in the hierarchy two layers are relatively
|
|
// parented to each other.
|
|
ftl::SmallVector<uint32_t, 5> relativeRootIds;
|
|
// 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; }
|
|
// 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 mirrorRootId != UNASSIGNED_LAYER_ID; }
|
|
TraversalPath getMirrorRoot() const;
|
|
|
|
bool operator==(const TraversalPath& other) const {
|
|
return id == other.id && mirrorRootId == other.mirrorRootId;
|
|
}
|
|
std::string toString() const;
|
|
|
|
static const TraversalPath ROOT;
|
|
};
|
|
|
|
struct TraversalPathHash {
|
|
std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
|
|
uint32_t hashCode = key.id * 31;
|
|
if (key.mirrorRootId != UNASSIGNED_LAYER_ID) {
|
|
hashCode += key.mirrorRootId * 31;
|
|
}
|
|
return std::hash<size_t>{}(hashCode);
|
|
}
|
|
};
|
|
|
|
// Helper class to add nodes to an existing traversal id and removes the
|
|
// node when it goes out of scope.
|
|
class ScopedAddToTraversalPath {
|
|
public:
|
|
ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
|
|
LayerHierarchy::Variant variantArg);
|
|
~ScopedAddToTraversalPath();
|
|
|
|
private:
|
|
TraversalPath& mTraversalPath;
|
|
TraversalPath mParentPath;
|
|
};
|
|
LayerHierarchy(RequestedLayerState* layer);
|
|
|
|
// Visitor function that provides the hierarchy node and a traversal id which uniquely
|
|
// identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
|
|
// Return false to stop traversing down the hierarchy.
|
|
typedef std::function<bool(const LayerHierarchy& hierarchy,
|
|
const LayerHierarchy::TraversalPath& traversalPath)>
|
|
Visitor;
|
|
|
|
// Traverse the hierarchy and visit all child variants.
|
|
void traverse(const Visitor& visitor) const {
|
|
TraversalPath root = TraversalPath::ROOT;
|
|
if (mLayer) {
|
|
root.id = mLayer->id;
|
|
}
|
|
traverse(visitor, root);
|
|
}
|
|
|
|
// Traverse the hierarchy in z-order, skipping children that have relative parents.
|
|
void traverseInZOrder(const Visitor& visitor) const {
|
|
TraversalPath root = TraversalPath::ROOT;
|
|
if (mLayer) {
|
|
root.id = mLayer->id;
|
|
}
|
|
traverseInZOrder(visitor, root);
|
|
}
|
|
|
|
const RequestedLayerState* getLayer() const;
|
|
const LayerHierarchy* getRelativeParent() const;
|
|
const LayerHierarchy* getParent() const;
|
|
std::string getDebugString(const char* prefix = "") const;
|
|
std::string getDebugStringShort() const;
|
|
// Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
|
|
// will contain the first relative root that was visited twice in a traversal.
|
|
bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
|
|
std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
|
|
|
|
private:
|
|
friend LayerHierarchyBuilder;
|
|
LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
|
|
void addChild(LayerHierarchy*, LayerHierarchy::Variant);
|
|
void removeChild(LayerHierarchy*);
|
|
void sortChildrenByZOrder();
|
|
void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
|
|
void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
|
|
void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
|
|
|
|
const RequestedLayerState* mLayer;
|
|
LayerHierarchy* mParent = nullptr;
|
|
LayerHierarchy* mRelativeParent = nullptr;
|
|
};
|
|
|
|
// Given a list of RequestedLayerState, this class will build a root hierarchy and an
|
|
// offscreen hierarchy. The builder also has an update method which can update an existing
|
|
// hierarchy from a list of RequestedLayerState and associated change flags.
|
|
class LayerHierarchyBuilder {
|
|
public:
|
|
LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
|
|
void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
|
|
const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
|
|
LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
|
|
const LayerHierarchy& getHierarchy() const;
|
|
const LayerHierarchy& getOffscreenHierarchy() const;
|
|
std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
|
|
|
|
private:
|
|
void onLayerAdded(RequestedLayerState* layer);
|
|
void attachToParent(LayerHierarchy*);
|
|
void detachFromParent(LayerHierarchy*);
|
|
void attachToRelativeParent(LayerHierarchy*);
|
|
void detachFromRelativeParent(LayerHierarchy*);
|
|
void attachHierarchyToRelativeParent(LayerHierarchy*);
|
|
void detachHierarchyFromRelativeParent(LayerHierarchy*);
|
|
|
|
void onLayerDestroyed(RequestedLayerState* layer);
|
|
void updateMirrorLayer(RequestedLayerState* layer);
|
|
LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
|
|
std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
|
|
std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
|
|
LayerHierarchy mRoot{nullptr};
|
|
LayerHierarchy mOffscreenRoot{nullptr};
|
|
};
|
|
|
|
} // namespace android::surfaceflinger::frontend
|