Edge extension effect: shader reimplementation
X-axis activity transitions require the translation of the surfaces
involved. As this movement would create unwanted see-through, we would
have added side windows anchored to the moving activities, and textured
them by clamping their parents.
We are replacing the additional windows with the extension of the
surfaces, and filling the area without buffer with a shader.
See go/edge-extension-sksl for more info.
Bug: 322036393
Test: LayerSnapshotTest
Flag: EXEMPT (calls will be protected by wm shell with com.android.window.flags.edge_extension_shader)
Change-Id: I3682efd16a1b311d67a522bb85960f100948b2ea
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 3745805..307ae39 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -177,6 +177,7 @@
}
SAFE_PARCEL(output.write, stretchEffect);
+ SAFE_PARCEL(output.writeParcelable, edgeExtensionParameters);
SAFE_PARCEL(output.write, bufferCrop);
SAFE_PARCEL(output.write, destinationFrame);
SAFE_PARCEL(output.writeInt32, static_cast<uint32_t>(trustedOverlay));
@@ -306,6 +307,7 @@
}
SAFE_PARCEL(input.read, stretchEffect);
+ SAFE_PARCEL(input.readParcelable, &edgeExtensionParameters);
SAFE_PARCEL(input.read, bufferCrop);
SAFE_PARCEL(input.read, destinationFrame);
uint32_t trustedOverlayInt;
@@ -682,6 +684,10 @@
what |= eStretchChanged;
stretchEffect = other.stretchEffect;
}
+ if (other.what & eEdgeExtensionChanged) {
+ what |= eEdgeExtensionChanged;
+ edgeExtensionParameters = other.edgeExtensionParameters;
+ }
if (other.what & eBufferCropChanged) {
what |= eBufferCropChanged;
bufferCrop = other.bufferCrop;
@@ -783,6 +789,7 @@
CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
CHECK_DIFF(diff, eTrustedOverlayChanged, other, trustedOverlay);
CHECK_DIFF(diff, eStretchChanged, other, stretchEffect);
+ CHECK_DIFF(diff, eEdgeExtensionChanged, other, edgeExtensionParameters);
CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop);
CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame);
if (other.what & eProducerDisconnect) diff |= eProducerDisconnect;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 902684c..88d3a7c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -22,6 +22,7 @@
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/DisplayState.h>
+#include <android/gui/EdgeExtensionParameters.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/TrustedPresentationThresholds.h>
@@ -2330,6 +2331,19 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect(
+ const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eEdgeExtensionChanged;
+ s->edgeExtensionParameters = effect;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop(
const sp<SurfaceControl>& sc, const Rect& bufferCrop) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl b/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl
new file mode 100644
index 0000000..44f4259
--- /dev/null
+++ b/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 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.
+ */
+
+package android.gui;
+
+
+/** @hide */
+parcelable EdgeExtensionParameters {
+ // These represent the translation of the window as requested by the animation
+ boolean extendRight;
+ boolean extendLeft;
+ boolean extendTop;
+ boolean extendBottom;
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 5f2f8dc..3fb1894 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -29,6 +29,7 @@
#include <math/mat4.h>
#include <android/gui/DropInputMode.h>
+#include <android/gui/EdgeExtensionParameters.h>
#include <android/gui/FocusRequest.h>
#include <android/gui/TrustedOverlay.h>
@@ -218,6 +219,7 @@
eTrustedOverlayChanged = 0x4000'00000000,
eDropInputModeChanged = 0x8000'00000000,
eExtendedRangeBrightnessChanged = 0x10000'00000000,
+ eEdgeExtensionChanged = 0x20000'00000000,
};
layer_state_t();
@@ -241,7 +243,7 @@
layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
layer_state_t::eTransformToDisplayInverseChanged |
- layer_state_t::eTransparentRegionChanged;
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged;
// Buffer and related updates.
static constexpr uint64_t BUFFER_CHANGES = layer_state_t::eApiChanged |
@@ -393,6 +395,9 @@
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
+ // Edge extension effect to be applied to this layer
+ gui::EdgeExtensionParameters edgeExtensionParameters;
+
Rect bufferCrop;
Rect destinationFrame;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 88c0c53..a10283b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -35,6 +35,7 @@
#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
+#include <ui/EdgeExtensionEffect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
@@ -744,6 +745,17 @@
Transaction& setStretchEffect(const sp<SurfaceControl>& sc,
const StretchEffect& stretchEffect);
+ /**
+ * Provides the edge extension effect configured on a container that the
+ * surface is rendered within.
+ * @param sc target surface the edge extension should be applied to
+ * @param effect the corresponding EdgeExtensionParameters to be applied
+ * to the surface.
+ * @return The transaction being constructed
+ */
+ Transaction& setEdgeExtensionEffect(const sp<SurfaceControl>& sc,
+ const gui::EdgeExtensionParameters& effect);
+
Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
const Rect& destinationFrame);
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 4a04467..ecf98c6 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -105,6 +105,7 @@
"skia/filters/LinearEffect.cpp",
"skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
+ "skia/filters/EdgeExtensionShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 8ac0af4..859ae8b 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -31,6 +31,7 @@
#include <ui/ShadowSettings.h>
#include <ui/StretchEffect.h>
#include <ui/Transform.h>
+#include "ui/EdgeExtensionEffect.h"
#include <iosfwd>
@@ -134,6 +135,7 @@
mat4 blurRegionTransform = mat4();
StretchEffect stretchEffect;
+ EdgeExtensionEffect edgeExtensionEffect;
// Name associated with the layer for debugging purposes.
std::string name;
@@ -183,7 +185,9 @@
lhs.skipContentDraw == rhs.skipContentDraw && lhs.shadow == rhs.shadow &&
lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
lhs.blurRegionTransform == rhs.blurRegionTransform &&
- lhs.stretchEffect == rhs.stretchEffect && lhs.whitePointNits == rhs.whitePointNits;
+ lhs.stretchEffect == rhs.stretchEffect &&
+ lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
+ lhs.whitePointNits == rhs.whitePointNits;
}
static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
@@ -254,6 +258,10 @@
*os << "\n}";
}
+static inline void PrintTo(const EdgeExtensionEffect& effect, ::std::ostream* os) {
+ *os << effect;
+}
+
static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
*os << "LayerSettings for '" << settings.name.c_str() << "' {";
*os << "\n .geometry = ";
@@ -285,6 +293,10 @@
*os << "\n .stretchEffect = ";
PrintTo(settings.stretchEffect, os);
}
+
+ if (settings.edgeExtensionEffect.hasEffect()) {
+ *os << "\n .edgeExtensionEffect = " << settings.edgeExtensionEffect;
+ }
*os << "\n .whitePointNits = " << settings.whitePointNits;
*os << "\n}";
}
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 5f37125..a609f2d 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -507,16 +507,24 @@
const RuntimeEffectShaderParameters& parameters) {
// The given surface will be stretched by HWUI via matrix transformation
// which gets similar results for most surfaces
- // Determine later on if we need to leverage the stertch shader within
+ // Determine later on if we need to leverage the stretch shader within
// surface flinger
const auto& stretchEffect = parameters.layer.stretchEffect;
const auto& targetBuffer = parameters.layer.source.buffer.buffer;
+ const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+
auto shader = parameters.shader;
- if (stretchEffect.hasEffect()) {
- const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && shader) {
+ if (graphicBuffer && parameters.shader) {
+ if (stretchEffect.hasEffect()) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
+ // The given surface requires to be filled outside of its buffer bounds if the edge
+ // extension is required
+ const auto& edgeExtensionEffect = parameters.layer.edgeExtensionEffect;
+ if (edgeExtensionEffect.hasEffect()) {
+ shader = mEdgeExtensionShaderFactory.createSkShader(shader, parameters.layer,
+ parameters.imageBounds);
+ }
}
if (parameters.requiresLinearEffect) {
@@ -566,8 +574,6 @@
parameters.layerDimmingRatio, 1.f));
}
- const auto targetBuffer = parameters.layer.source.buffer.buffer;
- const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
return createLinearEffectShader(shader, effect, runtimeEffect, std::move(colorTransform),
parameters.display.maxLuminance,
@@ -1039,18 +1045,20 @@
toSkColorSpace(layerDataspace)));
}
- paint.setShader(createRuntimeEffectShader(
- RuntimeEffectShaderParameters{.shader = shader,
- .layer = layer,
- .display = display,
- .undoPremultipliedAlpha = !item.isOpaque &&
- item.usePremultipliedAlpha,
- .requiresLinearEffect = requiresLinearEffect,
- .layerDimmingRatio = dimInLinearSpace
- ? layerDimmingRatio
- : 1.f,
- .outputDataSpace = display.outputDataspace,
- .fakeOutputDataspace = fakeDataspace}));
+ SkRect imageBounds;
+ matrix.mapRect(&imageBounds, SkRect::Make(image->bounds()));
+
+ paint.setShader(createRuntimeEffectShader(RuntimeEffectShaderParameters{
+ .shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque && item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = dimInLinearSpace ? layerDimmingRatio : 1.f,
+ .outputDataSpace = display.outputDataspace,
+ .fakeOutputDataspace = fakeDataspace,
+ .imageBounds = imageBounds,
+ }));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
static constexpr float kDimmingThreshold = 0.9f;
@@ -1118,7 +1126,8 @@
.requiresLinearEffect = requiresLinearEffect,
.layerDimmingRatio = layerDimmingRatio,
.outputDataSpace = display.outputDataspace,
- .fakeOutputDataspace = fakeDataspace}));
+ .fakeOutputDataspace = fakeDataspace,
+ .imageBounds = SkRect::MakeEmpty()}));
}
if (layer.disableBlending) {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index c8f9241..224a1ca 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -38,6 +38,7 @@
#include "compat/SkiaGpuContext.h"
#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
+#include "filters/EdgeExtensionShaderFactory.h"
#include "filters/LinearEffect.h"
#include "filters/StretchShaderFactory.h"
@@ -156,6 +157,7 @@
float layerDimmingRatio;
const ui::Dataspace outputDataSpace;
const ui::Dataspace fakeOutputDataspace;
+ const SkRect& imageBounds;
};
sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
@@ -175,6 +177,7 @@
AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
+ EdgeExtensionShaderFactory mEdgeExtensionShaderFactory;
sp<Fence> mLastDrawFence;
BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
new file mode 100644
index 0000000..1dbcc29
--- /dev/null
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 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 "EdgeExtensionShaderFactory.h"
+#include <SkPoint.h>
+#include <SkRuntimeEffect.h>
+#include <SkStream.h>
+#include <SkString.h>
+#include "log/log_main.h"
+
+namespace android::renderengine::skia {
+
+static const SkString edgeShader = SkString(R"(
+ uniform shader uContentTexture;
+ uniform vec2 uImgSize;
+
+ // TODO(b/214232209) oobTolerance is temporary and will be removed when the scrollbar will be
+ // hidden during the animation
+ const float oobTolerance = 15;
+ const int blurRadius = 3;
+ const float blurArea = float((2 * blurRadius + 1) * (2 * blurRadius + 1));
+
+ vec4 boxBlur(vec2 p) {
+ vec4 sumColors = vec4(0);
+
+ for (int i = -blurRadius; i <= blurRadius; i++) {
+ for (int j = -blurRadius; j <= blurRadius; j++) {
+ sumColors += uContentTexture.eval(p + vec2(i, j));
+ }
+ }
+ return sumColors / blurArea;
+ }
+
+ vec4 main(vec2 coord) {
+ vec2 nearestTexturePoint = clamp(coord, vec2(0, 0), uImgSize);
+ if (coord == nearestTexturePoint) {
+ return uContentTexture.eval(coord);
+ } else {
+ vec2 samplePoint = nearestTexturePoint + oobTolerance * normalize(
+ nearestTexturePoint - coord);
+ return boxBlur(samplePoint);
+ }
+ }
+)");
+
+sk_sp<SkShader> EdgeExtensionShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
+ const LayerSettings& layer,
+ const SkRect& imageBounds) {
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(edgeShader);
+ if (!instance.errorText.isEmpty()) {
+ ALOGE("EdgeExtensionShaderFactory terminated with an error: %s",
+ instance.errorText.c_str());
+ return nullptr;
+ }
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+ mBuilder->child("uContentTexture") = inputShader;
+ if (imageBounds.isEmpty()) {
+ mBuilder->uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(),
+ layer.geometry.boundaries.getHeight()};
+ } else {
+ mBuilder->uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()};
+ }
+ return mBuilder->makeShader();
+}
+} // namespace android::renderengine::skia
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
new file mode 100644
index 0000000..b0a8a93
--- /dev/null
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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 <SkImage.h>
+#include <SkRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/EdgeExtensionEffect.h>
+
+namespace android::renderengine::skia {
+
+/**
+ * This shader is designed to prolong the texture of a surface whose bounds have been extended over
+ * the size of the texture. This shader is similar to the default clamp, but adds a blur effect and
+ * samples from close to the edge (compared to on the edge) to avoid weird artifacts when elements
+ * (in particular, scrollbars) touch the edge.
+ */
+class EdgeExtensionShaderFactory {
+public:
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader, const LayerSettings& layer,
+ const SkRect& imageBounds);
+
+private:
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+} // namespace android::renderengine::skia
diff --git a/libs/ui/include/ui/EdgeExtensionEffect.h b/libs/ui/include/ui/EdgeExtensionEffect.h
new file mode 100644
index 0000000..02126b1
--- /dev/null
+++ b/libs/ui/include/ui/EdgeExtensionEffect.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 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
+
+/**
+ * Stores the edge that will be extended
+ */
+namespace android {
+
+enum CanonicalDirections { NONE = 0, LEFT = 1, RIGHT = 2, TOP = 4, BOTTOM = 8 };
+
+inline std::string to_string(CanonicalDirections direction) {
+ switch (direction) {
+ case LEFT:
+ return "LEFT";
+ case RIGHT:
+ return "RIGHT";
+ case TOP:
+ return "TOP";
+ case BOTTOM:
+ return "BOTTOM";
+ case NONE:
+ return "NONE";
+ }
+}
+
+struct EdgeExtensionEffect {
+ EdgeExtensionEffect(bool left, bool right, bool top, bool bottom) {
+ extensionEdges = NONE;
+ if (left) {
+ extensionEdges |= LEFT;
+ }
+ if (right) {
+ extensionEdges |= RIGHT;
+ }
+ if (top) {
+ extensionEdges |= TOP;
+ }
+ if (bottom) {
+ extensionEdges |= BOTTOM;
+ }
+ }
+
+ EdgeExtensionEffect() { EdgeExtensionEffect(false, false, false, false); }
+
+ bool extendsEdge(CanonicalDirections edge) const { return extensionEdges & edge; }
+
+ bool hasEffect() const { return extensionEdges != NONE; };
+
+ void reset() { extensionEdges = NONE; }
+
+ bool operator==(const EdgeExtensionEffect& other) const {
+ return extensionEdges == other.extensionEdges;
+ }
+
+ bool operator!=(const EdgeExtensionEffect& other) const { return !(*this == other); }
+
+private:
+ int extensionEdges;
+};
+
+inline std::string to_string(const EdgeExtensionEffect& effect) {
+ std::string s = "EdgeExtensionEffect={edges=[";
+ if (effect.hasEffect()) {
+ for (CanonicalDirections edge : {LEFT, RIGHT, TOP, BOTTOM}) {
+ if (effect.extendsEdge(edge)) {
+ s += to_string(edge) + ", ";
+ }
+ }
+ } else {
+ s += to_string(NONE);
+ }
+ s += "]}";
+ return s;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const EdgeExtensionEffect effect) {
+ os << to_string(effect);
+ return os;
+}
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 11759b8..d1429a2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -35,6 +35,7 @@
#pragma clang diagnostic ignored "-Wextra"
#include <gui/BufferQueue.h>
+#include <ui/EdgeExtensionEffect.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/StretchEffect.h>
@@ -133,12 +134,16 @@
// The bounds of the layer in layer local coordinates
FloatRect geomLayerBounds;
+ // The crop to apply to the layer in layer local coordinates
+ FloatRect geomLayerCrop;
+
ShadowSettings shadowSettings;
// List of regions that require blur
std::vector<BlurRegion> blurRegions;
StretchEffect stretchEffect;
+ EdgeExtensionEffect edgeExtensionEffect;
/*
* Geometry state
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index bdaa1d0..d9018bc 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -37,7 +37,8 @@
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
- lhs.stretchEffect == rhs.stretchEffect;
+ lhs.stretchEffect == rhs.stretchEffect &&
+ lhs.edgeExtensionEffect == rhs.edgeExtensionEffect;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 70e3c64..e5f6b7b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -322,6 +322,10 @@
<< touchableRegion.bottom << "," << touchableRegion.right << "}"
<< "}";
}
+
+ if (obj.edgeExtensionEffect.hasEffect()) {
+ out << obj.edgeExtensionEffect;
+ }
return out;
}
@@ -492,8 +496,10 @@
requested.what &
(layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) {
- forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect();
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged |
+ layer_state_t::eEdgeExtensionChanged)) {
+ forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect() ||
+ edgeExtensionEffect.hasEffect();
}
if (forceUpdate ||
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 2b1b2c8..4e09381 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -367,6 +367,7 @@
snapshot.geomLayerBounds = getMaxDisplayBounds({});
snapshot.roundedCorner = RoundedCornerState();
snapshot.stretchEffect = {};
+ snapshot.edgeExtensionEffect = {};
snapshot.outputFilter.layerStack = ui::DEFAULT_LAYER_STACK;
snapshot.outputFilter.toInternalDisplay = false;
snapshot.isSecure = false;
@@ -811,6 +812,32 @@
: parentSnapshot.stretchEffect;
}
+ if (forceUpdate ||
+ (snapshot.clientChanges | parentSnapshot.clientChanges) &
+ layer_state_t::eEdgeExtensionChanged) {
+ if (requested.edgeExtensionParameters.extendLeft ||
+ requested.edgeExtensionParameters.extendRight ||
+ requested.edgeExtensionParameters.extendTop ||
+ requested.edgeExtensionParameters.extendBottom) {
+ // This is the root layer to which the extension is applied
+ snapshot.edgeExtensionEffect =
+ EdgeExtensionEffect(requested.edgeExtensionParameters.extendLeft,
+ requested.edgeExtensionParameters.extendRight,
+ requested.edgeExtensionParameters.extendTop,
+ requested.edgeExtensionParameters.extendBottom);
+ } else if (parentSnapshot.clientChanges & layer_state_t::eEdgeExtensionChanged) {
+ // Extension is inherited
+ snapshot.edgeExtensionEffect = parentSnapshot.edgeExtensionEffect;
+ } else {
+ // There is no edge extension
+ snapshot.edgeExtensionEffect.reset();
+ }
+ if (snapshot.edgeExtensionEffect.hasEffect()) {
+ snapshot.clientChanges |= layer_state_t::eEdgeExtensionChanged;
+ snapshot.changes |= RequestedLayerState::Changes::Geometry;
+ }
+ }
+
if (forceUpdate || snapshot.clientChanges & layer_state_t::eColorTransformChanged) {
if (!parentSnapshot.colorTransformIsIdentity) {
snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform;
@@ -899,6 +926,10 @@
updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
}
+ if (snapshot.edgeExtensionEffect.hasEffect()) {
+ updateBoundsForEdgeExtension(snapshot);
+ }
+
if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::BufferUsageFlags)) {
@@ -917,8 +948,8 @@
}
// computed snapshot properties
- snapshot.forceClientComposition =
- snapshot.shadowSettings.length > 0 || snapshot.stretchEffect.hasEffect();
+ snapshot.forceClientComposition = snapshot.shadowSettings.length > 0 ||
+ snapshot.stretchEffect.hasEffect() || snapshot.edgeExtensionEffect.hasEffect();
snapshot.contentOpaque = snapshot.isContentOpaque();
snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
@@ -973,6 +1004,31 @@
}
}
+/**
+ * According to the edges that we are requested to extend, we increase the bounds to the maximum
+ * extension allowed by the crop (parent crop + requested crop). The animation that called
+ * Transition#setEdgeExtensionEffect is in charge of setting the requested crop.
+ * @param snapshot
+ */
+void LayerSnapshotBuilder::updateBoundsForEdgeExtension(LayerSnapshot& snapshot) {
+ EdgeExtensionEffect& effect = snapshot.edgeExtensionEffect;
+
+ if (effect.extendsEdge(LEFT)) {
+ snapshot.geomLayerBounds.left = snapshot.geomLayerCrop.left;
+ }
+ if (effect.extendsEdge(RIGHT)) {
+ snapshot.geomLayerBounds.right = snapshot.geomLayerCrop.right;
+ }
+ if (effect.extendsEdge(TOP)) {
+ snapshot.geomLayerBounds.top = snapshot.geomLayerCrop.top;
+ }
+ if (effect.extendsEdge(BOTTOM)) {
+ snapshot.geomLayerBounds.bottom = snapshot.geomLayerCrop.bottom;
+ }
+
+ snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
+}
+
void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
@@ -1012,11 +1068,12 @@
FloatRect parentBounds = parentSnapshot.geomLayerBounds;
parentBounds = snapshot.localTransform.inverse().transform(parentBounds);
snapshot.geomLayerBounds =
- (requested.externalTexture) ? snapshot.bufferSize.toFloatRect() : parentBounds;
+ requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds;
+ snapshot.geomLayerCrop = parentBounds;
if (!requested.crop.isEmpty()) {
- snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(requested.crop.toFloatRect());
+ snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect());
}
- snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds);
+ snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop);
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
const Rect geomLayerBoundsWithoutTransparentRegion =
RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index dbbad76..f3c56a4 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -113,6 +113,10 @@
static void resetRelativeState(LayerSnapshot& snapshot);
static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, const Args& args);
+ static bool extensionEdgeSharedWithParent(LayerSnapshot& snapshot,
+ const RequestedLayerState& requested,
+ const LayerSnapshot& parentSnapshot);
+ static void updateBoundsForEdgeExtension(LayerSnapshot& snapshot);
void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 17d3f4b..17d2610 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -609,8 +609,9 @@
layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged |
layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
- layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
- layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged |
+ layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
+ layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
+ layer_state_t::eExtendedRangeBrightnessChanged |
layer_state_t::eDesiredHdrHeadroomChanged |
(FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
? layer_state_t::eFlagsChanged
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 8f819b2..b05f0ee 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -173,6 +173,7 @@
break;
}
layerSettings.stretchEffect = mSnapshot->stretchEffect;
+ layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect;
// Record the name of the layer for debugging further down the stack.
layerSettings.name = mSnapshot->name;