SurfaceFlinger and libgui: Support for child layers.
Add support for parenting Layers in a tree. Layers
follow scene-graph style rules, that is to say:
1. A child is cropped to the final bounds of the parent.
2. A child inherits the parent's transform (including position)
3. A child's Z ordering is relative to the parent and bounded between
the parents siblings.
4. A childs lifetime is bounded by it's parents lifetime.
Test: New tests in Transaction_test plus manual testing with later branches.
Change-Id: I96f8ad863665b9a70b6f845561344c297b7e6eff
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5b61c25..6fd9cd7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -280,6 +280,9 @@
// it's removed from the drawing state list)
void Layer::onRemoved() {
mSurfaceFlingerConsumer->abandon();
+ for (const auto& child : mCurrentChildren) {
+ child->onRemoved();
+ }
}
// ---------------------------------------------------------------------------
@@ -362,6 +365,40 @@
return Region(win).subtract(exclude).getBounds();
}
+Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
+ const Layer::State& s(getDrawingState());
+ Rect win(s.active.w, s.active.h);
+
+ if (!s.crop.isEmpty()) {
+ win.intersect(s.crop, &win);
+ }
+
+ Transform t = getTransform();
+ win = t.transform(win);
+
+ const sp<Layer>& p = getParent();
+ // Now we need to calculate the parent bounds, so we can clip ourselves to those.
+ // When calculating the parent bounds for purposes of clipping,
+ // we don't need to constrain the parent to its transparent region.
+ // The transparent region is an optimization based on the
+ // buffer contents of the layer, but does not affect the space allocated to
+ // it by policy, and thus children should be allowed to extend into the
+ // parent's transparent region. In fact one of the main uses, is to reduce
+ // buffer allocation size in cases where a child window sits behind a main window
+ // (by marking the hole in the parent window as a transparent region)
+ if (p != nullptr) {
+ Rect bounds = p->computeScreenBounds(false);
+ bounds.intersect(win, &win);
+ }
+
+ if (reduceTransparentRegion) {
+ auto const screenTransparentRegion = t.transform(s.activeTransparentRegion);
+ win = reduce(win, screenTransparentRegion);
+ }
+
+ return win;
+}
+
Rect Layer::computeBounds() const {
const Layer::State& s(getDrawingState());
return computeBounds(s.activeTransparentRegion);
@@ -374,15 +411,25 @@
if (!s.crop.isEmpty()) {
win.intersect(s.crop, &win);
}
+
+ Rect bounds = win;
+ const auto& p = getParent();
+ if (p != nullptr) {
+ bounds = p->computeScreenBounds();
+ }
+
+ Transform t = getTransform();
+ if (p != nullptr) {
+ win = t.transform(win);
+ win.intersect(bounds, &win);
+ win = t.inverse().transform(win);
+ }
+
// subtract the transparent region and snap to the bounds
return reduce(win, activeTransparentRegion);
}
-gfx::FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
- // the content crop is the area of the content that gets scaled to the
- // layer's size.
- gfx::FloatRect crop = getContentCrop().toFloatRect();
-
+Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const {
// the crop is the area of the window that gets cropped, but not
// scaled in any ways.
const State& s(getDrawingState());
@@ -399,7 +446,8 @@
activeCrop = s.crop;
}
- activeCrop = s.active.transform.transform(activeCrop);
+ Transform t = getTransform();
+ activeCrop = t.transform(activeCrop);
if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
activeCrop.clear();
}
@@ -408,7 +456,27 @@
activeCrop.clear();
}
}
- activeCrop = s.active.transform.inverse().transform(activeCrop);
+ return activeCrop;
+}
+
+gfx::FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
+ // the content crop is the area of the content that gets scaled to the
+ // layer's size. This is in buffer space.
+ gfx::FloatRect crop = getContentCrop().toFloatRect();
+
+ // In addition there is a WM-specified crop we pull from our drawing state.
+ const State& s(getDrawingState());
+
+ // Screen space to make reduction to parent crop clearer.
+ Rect activeCrop = computeInitialCrop(hw);
+ const auto& p = getParent();
+ if (p != nullptr) {
+ auto parentCrop = p->computeInitialCrop(hw);
+ activeCrop.intersect(parentCrop, &activeCrop);
+ }
+ Transform t = getTransform();
+ // Back to layer space to work with the content crop.
+ activeCrop = t.inverse().transform(activeCrop);
// This needs to be here as transform.transform(Rect) computes the
// transformed rect and then takes the bounding box of the result before
@@ -535,9 +603,10 @@
// apply the layer's transform, followed by the display's global transform
// here we're guaranteed that the layer's transform preserves rects
Region activeTransparentRegion(s.activeTransparentRegion);
+ Transform t = getTransform();
if (!s.crop.isEmpty()) {
Rect activeCrop(s.crop);
- activeCrop = s.active.transform.transform(activeCrop);
+ activeCrop = t.transform(activeCrop);
#ifdef USE_HWC2
if(!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) {
#else
@@ -545,7 +614,7 @@
#endif
activeCrop.clear();
}
- activeCrop = s.active.transform.inverse().transform(activeCrop, true);
+ activeCrop = t.inverse().transform(activeCrop, true);
// This needs to be here as transform.transform(Rect) computes the
// transformed rect and then takes the bounding box of the result before
// returning. This means
@@ -564,7 +633,8 @@
activeTransparentRegion.orSelf(Rect(activeCrop.right, activeCrop.top,
s.active.w, activeCrop.bottom));
}
- Rect frame(s.active.transform.transform(computeBounds(activeTransparentRegion)));
+
+ Rect frame(t.transform(computeBounds(activeTransparentRegion)));
if (!s.finalCrop.isEmpty()) {
if(!frame.intersect(s.finalCrop, &frame)) {
frame.clear();
@@ -625,7 +695,7 @@
*/
const Transform bufferOrientation(mCurrentTransform);
- Transform transform(tr * s.active.transform * bufferOrientation);
+ Transform transform(tr * t * bufferOrientation);
if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
/*
@@ -811,7 +881,7 @@
}
// Subtract the transparent region and snap to the bounds
Rect bounds = reduce(win, s.activeTransparentRegion);
- Rect frame(s.active.transform.transform(bounds));
+ Rect frame(getTransform().transform(bounds));
frame.intersect(displayDevice->getViewport(), &frame);
if (!s.finalCrop.isEmpty()) {
frame.intersect(s.finalCrop, &frame);
@@ -860,7 +930,7 @@
}
// subtract the transparent region and snap to the bounds
Rect bounds = reduce(win, s.activeTransparentRegion);
- Rect frame(s.active.transform.transform(bounds));
+ Rect frame(getTransform().transform(bounds));
frame.intersect(hw->getViewport(), &frame);
if (!s.finalCrop.isEmpty()) {
frame.intersect(s.finalCrop, &frame);
@@ -903,15 +973,14 @@
// figure out if there is something below us
Region under;
- const LayerVector& drawingLayers(
- mFlinger->mDrawingState.layersSortedByZ);
- const size_t count = drawingLayers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<Layer>& layer(drawingLayers[i]);
- if (layer.get() == static_cast<Layer const*>(this))
- break;
+ bool finished = false;
+ mFlinger->mDrawingState.layersSortedByZ.traverseInZOrder([&](Layer* layer) {
+ if (finished || layer == static_cast<Layer const*>(this)) {
+ finished = true;
+ return;
+ }
under.orSelf( hw->getTransform().transform(layer->visibleRegion) );
- }
+ });
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
@@ -1023,12 +1092,13 @@
*/
Rect win(computeBounds());
+ Transform t = getTransform();
if (!s.finalCrop.isEmpty()) {
- win = s.active.transform.transform(win);
+ win = t.transform(win);
if (!win.intersect(s.finalCrop, &win)) {
win.clear();
}
- win = s.active.transform.inverse().transform(win);
+ win = t.inverse().transform(win);
if (!win.intersect(computeBounds(), &win)) {
win.clear();
}
@@ -1228,25 +1298,21 @@
bool useIdentityTransform) const
{
const Layer::State& s(getDrawingState());
- const Transform tr(hw->getTransform());
+ const Transform hwTransform(hw->getTransform());
const uint32_t hw_h = hw->getHeight();
- Rect win(s.active.w, s.active.h);
- if (!s.crop.isEmpty()) {
- win.intersect(s.crop, &win);
- }
- // subtract the transparent region and snap to the bounds
- win = reduce(win, s.activeTransparentRegion);
+ Rect win = computeBounds();
vec2 lt = vec2(win.left, win.top);
vec2 lb = vec2(win.left, win.bottom);
vec2 rb = vec2(win.right, win.bottom);
vec2 rt = vec2(win.right, win.top);
+ Transform layerTransform = getTransform();
if (!useIdentityTransform) {
- lt = s.active.transform.transform(lt);
- lb = s.active.transform.transform(lb);
- rb = s.active.transform.transform(rb);
- rt = s.active.transform.transform(rt);
+ lt = layerTransform.transform(lt);
+ lb = layerTransform.transform(lb);
+ rb = layerTransform.transform(rb);
+ rt = layerTransform.transform(rt);
}
if (!s.finalCrop.isEmpty()) {
@@ -1257,10 +1323,10 @@
}
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = tr.transform(lt);
- position[1] = tr.transform(lb);
- position[2] = tr.transform(rb);
- position[3] = tr.transform(rt);
+ position[0] = hwTransform.transform(lt);
+ position[1] = hwTransform.transform(lb);
+ position[2] = hwTransform.transform(rb);
+ position[3] = hwTransform.transform(rt);
for (size_t i=0 ; i<4 ; i++) {
position[i].y = hw_h - position[i].y;
}
@@ -1542,11 +1608,7 @@
// that transactions for layers depending on this layer's frames becoming
// visible are not blocked
if (c.flags & layer_state_t::eLayerHidden) {
- Mutex::Autolock lock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- point->setFrameAvailable();
- }
- mLocalSyncPoints.clear();
+ clearSyncPoints();
}
// Commit the transaction
@@ -1585,6 +1647,18 @@
return true;
}
+bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) {
+ ssize_t idx = mCurrentChildren.indexOf(childLayer);
+ if (idx < 0) {
+ return false;
+ }
+ if (childLayer->setLayer(z)) {
+ mCurrentChildren.removeAt(idx);
+ mCurrentChildren.add(childLayer);
+ }
+ return true;
+}
+
bool Layer::setLayer(int32_t z) {
if (mCurrentState.z == z)
return false;
@@ -1594,6 +1668,7 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
+
bool Layer::setSize(uint32_t w, uint32_t h) {
if (mCurrentState.requested.w == w && mCurrentState.requested.h == h)
return false;
@@ -1699,6 +1774,14 @@
return true;
}
+uint32_t Layer::getLayerStack() const {
+ auto p = getParent();
+ if (p == nullptr) {
+ return getDrawingState().layerStack;
+ }
+ return p->getLayerStack();
+}
+
void Layer::deferTransactionUntil(const sp<IBinder>& handle,
uint64_t frameNumber) {
mCurrentState.handle = handle;
@@ -1827,13 +1910,22 @@
}
#endif
+bool Layer::isHiddenByPolicy() const {
+ const Layer::State& s(mDrawingState);
+ const auto& parent = getParent();
+ if (parent != nullptr && parent->isHiddenByPolicy()) {
+ return true;
+ }
+ return s.flags & layer_state_t::eLayerHidden;
+}
+
bool Layer::isVisible() const {
const Layer::State& s(mDrawingState);
#ifdef USE_HWC2
- return !(s.flags & layer_state_t::eLayerHidden) && s.alpha > 0.0f
+ return !(isHiddenByPolicy()) && s.alpha > 0.0f
&& (mActiveBuffer != NULL || mSidebandStream != NULL);
#else
- return !(s.flags & layer_state_t::eLayerHidden) && s.alpha
+ return !(isHiddenByPolicy()) && s.alpha
&& (mActiveBuffer != NULL || mSidebandStream != NULL);
#endif
}
@@ -1878,7 +1970,7 @@
recomputeVisibleRegions = true;
const State& s(getDrawingState());
- return s.active.transform.transform(Region(Rect(s.active.w, s.active.h)));
+ return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
}
Region outDirtyRegion;
@@ -2067,7 +2159,7 @@
Region dirtyRegion(Rect(s.active.w, s.active.h));
// transform the dirty region to window-manager space
- outDirtyRegion = (s.active.transform.transform(dirtyRegion));
+ outDirtyRegion = (getTransform().transform(dirtyRegion));
return outDirtyRegion;
}
@@ -2130,7 +2222,9 @@
"alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
#endif
" client=%p\n",
- s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h,
+ getLayerStack(), s.z,
+ s.active.transform.tx(), s.active.transform.ty(),
+ s.active.w, s.active.h,
s.crop.left, s.crop.top,
s.crop.right, s.crop.bottom,
s.finalCrop.left, s.finalCrop.top,
@@ -2263,6 +2357,90 @@
return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
}
+void Layer::addChild(const sp<Layer>& layer) {
+ mCurrentChildren.add(layer);
+ layer->setParent(this);
+}
+
+ssize_t Layer::removeChild(const sp<Layer>& layer) {
+ layer->setParent(nullptr);
+ return mCurrentChildren.remove(layer);
+}
+
+void Layer::setParent(const sp<Layer>& layer) {
+ mParent = layer;
+}
+
+void Layer::clearSyncPoints() {
+ for (const auto& child : mCurrentChildren) {
+ child->clearSyncPoints();
+ }
+
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+ for (auto& point : mLocalSyncPoints) {
+ point->setFrameAvailable();
+ }
+ mLocalSyncPoints.clear();
+}
+
+int32_t Layer::getZ() const {
+ return mDrawingState.z;
+}
+
+/**
+ * Negatively signed children are before 'this' in Z-order.
+ */
+void Layer::traverseInZOrder(const std::function<void(Layer*)>& exec) {
+ size_t i = 0;
+ for (; i < mDrawingChildren.size(); i++) {
+ const auto& child = mDrawingChildren[i];
+ if (child->getZ() >= 0)
+ break;
+ child->traverseInZOrder(exec);
+ }
+ exec(this);
+ for (; i < mDrawingChildren.size(); i++) {
+ const auto& child = mDrawingChildren[i];
+ child->traverseInZOrder(exec);
+ }
+}
+
+/**
+ * Positively signed children are before 'this' in reverse Z-order.
+ */
+void Layer::traverseInReverseZOrder(const std::function<void(Layer*)>& exec) {
+ int32_t i = 0;
+ for (i = mDrawingChildren.size()-1; i>=0; i--) {
+ const auto& child = mDrawingChildren[i];
+ if (child->getZ() < 0) {
+ break;
+ }
+ child->traverseInReverseZOrder(exec);
+ }
+ exec(this);
+ for (; i>=0; i--) {
+ const auto& child = mDrawingChildren[i];
+ child->traverseInReverseZOrder(exec);
+ }
+}
+
+Transform Layer::getTransform() const {
+ Transform t;
+ const auto& p = getParent();
+ if (p != nullptr) {
+ t = p->getTransform();
+ }
+ return t * getDrawingState().active.transform;
+}
+
+void Layer::commitChildList() {
+ for (size_t i = 0; i < mCurrentChildren.size(); i++) {
+ const auto& child = mCurrentChildren[i];
+ child->commitChildList();
+ }
+ mDrawingChildren = mCurrentChildren;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android