summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tianhao Yao <tianhaoy@google.com> 2022-02-22 17:48:33 +0000
committer chaviw <chaviw@google.com> 2022-05-04 11:46:47 -0500
commit67dd7126b52bf8ab25d89470392b887bccf0ad6f (patch)
treec5afc1ef4838d1de70df6195fb25948e17a770a1
parentff4b9b8d6c4e059b4ad6eefdb5d418fae45cf897 (diff)
Enable drawing layers' borders in SurfaceFlinger.
Provide an API on SurfaceComposerClient::Transaction for enabling a border to be drawn for a specified hierarchy. The caller requests a border to be drawn at a certain Layer and all its children will be included when computing the visible region. The border will draw around the union of all the layers' visible regions, starting from the requested layer. Test: go/wm-smoke Test: LayerBorder_test Bug: 225977175 Change-Id: I7760b51b68cdf01bb9146ec91a270a9d63927995
-rw-r--r--libs/gui/LayerState.cpp8
-rw-r--r--libs/gui/SurfaceComposerClient.cpp15
-rw-r--r--libs/gui/include/gui/LayerState.h4
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h2
-rw-r--r--libs/renderengine/include/renderengine/BorderRenderInfo.h33
-rw-r--r--libs/renderengine/include/renderengine/DisplaySettings.h6
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.cpp24
-rw-r--r--libs/renderengine/tests/RenderEngineTest.cpp47
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h5
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h2
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp38
-rw-r--r--services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp8
-rw-r--r--services/surfaceflinger/Layer.cpp15
-rw-r--r--services/surfaceflinger/Layer.h5
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp23
-rw-r--r--services/surfaceflinger/tests/Android.bp1
-rw-r--r--services/surfaceflinger/tests/LayerBorder_test.cpp247
-rw-r--r--services/surfaceflinger/tests/utils/ColorUtils.h12
19 files changed, 490 insertions, 6 deletions
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 3ab9e974bc..0d3b412842 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -66,6 +66,7 @@ layer_state_t::layer_state_t()
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
isTrustedOverlay(false),
+ borderEnabled(false),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
dropInputMode(gui::DropInputMode::NONE) {
@@ -100,7 +101,7 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.write, transparentRegion);
SAFE_PARCEL(output.writeUint32, transform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
-
+ SAFE_PARCEL(output.writeBool, borderEnabled);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
SAFE_PARCEL(output.write, hdrMetadata);
SAFE_PARCEL(output.write, surfaceDamageRegion);
@@ -200,6 +201,7 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, transparentRegion);
SAFE_PARCEL(input.readUint32, &transform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
+ SAFE_PARCEL(input.readBool, &borderEnabled);
uint32_t tmpUint32 = 0;
SAFE_PARCEL(input.readUint32, &tmpUint32);
@@ -550,6 +552,10 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eShadowRadiusChanged;
shadowRadius = other.shadowRadius;
}
+ if (other.what & eRenderBorderChanged) {
+ what |= eRenderBorderChanged;
+ borderEnabled = other.borderEnabled;
+ }
if (other.what & eFrameRateSelectionPriority) {
what |= eFrameRateSelectionPriority;
frameRateSelectionPriority = other.frameRateSelectionPriority;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 3446b1bc0d..d543e9444c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1918,6 +1918,21 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::enableBorder(
+ const sp<SurfaceControl>& sc, bool shouldEnable) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eRenderBorderChanged;
+ s->borderEnabled = shouldEnable;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 4cd9a56fcd..4621d2b24e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -155,7 +155,7 @@ struct layer_state_t {
eLayerStackChanged = 0x00000080,
eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
- /* unused 0x00001000, */
+ eRenderBorderChanged = 0x00001000,
eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -293,6 +293,8 @@ struct layer_state_t {
// should be trusted for input occlusion detection purposes
bool isTrustedOverlay;
+ // Flag to indicate if border needs to be enabled on the layer
+ bool borderEnabled;
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index dc9624269b..cdb60c713a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -625,6 +625,8 @@ public:
const Rect& destinationFrame);
Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
+ Transaction& enableBorder(const sp<SurfaceControl>& sc, bool shouldEnable);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/renderengine/include/renderengine/BorderRenderInfo.h b/libs/renderengine/include/renderengine/BorderRenderInfo.h
new file mode 100644
index 0000000000..85d55fc958
--- /dev/null
+++ b/libs/renderengine/include/renderengine/BorderRenderInfo.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <math/mat4.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+
+struct BorderRenderInfo {
+ Region combinedRegion;
+
+ bool operator==(const BorderRenderInfo& rhs) const {
+ return (combinedRegion.hasSameRects(rhs.combinedRegion));
+ }
+};
+
+} // namespace renderengine
+} // namespace android \ No newline at end of file
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 59ef991eec..25fe9f2d8e 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -22,6 +22,7 @@
#include <math/mat4.h>
#include <renderengine/PrintMatrix.h>
+#include <renderengine/BorderRenderInfo.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -79,6 +80,8 @@ struct DisplaySettings {
// Configures the rendering intent of the output display. This is used for tonemapping.
aidl::android::hardware::graphics::composer3::RenderIntent renderIntent =
aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC;
+
+ std::vector<renderengine::BorderRenderInfo> borderInfoList;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
@@ -90,7 +93,8 @@ static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings&
lhs.deviceHandlesColorTransform == rhs.deviceHandlesColorTransform &&
lhs.orientation == rhs.orientation &&
lhs.targetLuminanceNits == rhs.targetLuminanceNits &&
- lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent;
+ lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent &&
+ lhs.borderInfoList == rhs.borderInfoList;
}
static const char* orientation_to_string(uint32_t orientation) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 11cea1339d..ec9ad54b56 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1245,6 +1245,30 @@ void SkiaGLRenderEngine::drawLayersInternal(
activeSurface->flush();
}
}
+ for (const auto& borderRenderInfo : display.borderInfoList) {
+ SkPaint p;
+ // TODO (b/225977175): Use specified color
+ p.setColor(SkColor4f{.fR = 255 / 255.0f,
+ .fG = 128 / 255.0f,
+ .fB = 0 / 255.0f,
+ .fA = 255 / 255.0f});
+ p.setAntiAlias(true);
+ p.setStyle(SkPaint::kStroke_Style);
+ // TODO (b/225977175): Use specified width
+ p.setStrokeWidth(20);
+ SkRegion sk_region;
+ SkPath path;
+
+ // Construct a final SkRegion using Regions
+ for (const auto& r : borderRenderInfo.combinedRegion) {
+ sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
+ }
+
+ sk_region.getBoundaryPath(&path);
+ canvas->drawPath(path, p);
+ path.close();
+ }
+
surfaceAutoSaveRestore.restore();
mCapture->endCapture();
{
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7c70a748b5..df1b985bd6 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -2411,6 +2411,53 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
expectBufferColor(rect, 0, 128, 0, 128);
}
+TEST_P(RenderEngineTest, testBorder) {
+ if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ GTEST_SKIP();
+ }
+
+ if (!GetParam()->useColorManagement()) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB;
+
+ const auto displayRect = Rect(1080, 2280);
+ renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = dataspace,
+ };
+ display.borderInfoList.clear();
+ renderengine::BorderRenderInfo info;
+ info.combinedRegion = Region(Rect(99, 99, 199, 199));
+ display.borderInfoList.emplace_back(info);
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = dataspace,
+ .whitePointNits = 200.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers;
+ layers.emplace_back(greenLayer);
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(99, 99, 101, 101), 255, 128, 0, 255, 1);
+}
+
TEST_P(RenderEngineTest, testDimming) {
if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
GTEST_SKIP();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index f201751d59..8039bbacb9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -32,6 +32,9 @@ namespace android::compositionengine {
using Layers = std::vector<sp<compositionengine::LayerFE>>;
using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
+struct BorderRenderInfo {
+ std::vector<int32_t> layerIds;
+};
/**
* A parameter object for refreshing a set of outputs
*/
@@ -90,6 +93,8 @@ struct CompositionRefreshArgs {
// If set, a frame has been scheduled for that time.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
+
+ std::vector<BorderRenderInfo> borderInfoList;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 31c51e6a8d..3ad6e52f26 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -152,6 +152,7 @@ protected:
private:
void dirtyEntireOutput();
+ void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&);
compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
void finishPrepareFrame();
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index c65d467b0d..cd56530f37 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -33,6 +33,7 @@
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <compositionengine/ProjectionSpace.h>
+#include <renderengine/BorderRenderInfo.h>
#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -164,6 +165,7 @@ struct OutputCompositionState {
bool treat170mAsSrgb = false;
+ std::vector<renderengine::BorderRenderInfo> borderInfoList;
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 4c30f99610..430d6734fe 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -733,6 +733,39 @@ void Output::updateCompositionState(const compositionengine::CompositionRefreshA
forceClientComposition = false;
}
}
+
+ updateCompositionStateForBorder(refreshArgs);
+}
+
+void Output::updateCompositionStateForBorder(
+ const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ std::unordered_map<int32_t, const Region*> layerVisibleRegionMap;
+ // Store a map of layerId to their computed visible region.
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ int layerId = (layer->getLayerFE()).getSequence();
+ layerVisibleRegionMap[layerId] = &((layer->getState()).visibleRegion);
+ }
+ OutputCompositionState& outputCompositionState = editState();
+ outputCompositionState.borderInfoList.clear();
+ bool clientComposeTopLayer = false;
+ for (const auto& borderInfo : refreshArgs.borderInfoList) {
+ renderengine::BorderRenderInfo info;
+ for (const auto& id : borderInfo.layerIds) {
+ info.combinedRegion.orSelf(*(layerVisibleRegionMap[id]));
+ }
+ outputCompositionState.borderInfoList.emplace_back(std::move(info));
+ clientComposeTopLayer |= !info.combinedRegion.isEmpty();
+ }
+
+ // In this situation we must client compose the top layer instead of using hwc
+ // because we want to draw the border above all else.
+ // This could potentially cause a bit of a performance regression if the top
+ // layer would have been rendered using hwc originally.
+ // TODO(b/227656283): Measure system's performance before enabling the border feature
+ if (clientComposeTopLayer) {
+ auto topLayer = getOutputLayerOrderedByZByIndex(getOutputLayerCount() - 1);
+ (topLayer->editState()).forceClientComposition = true;
+ }
}
void Output::planComposition() {
@@ -1183,6 +1216,11 @@ std::optional<base::unique_fd> Output::composeSurfaces(
// Compute the global color transform matrix.
clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
+ for (auto& info : outputState.borderInfoList) {
+ renderengine::BorderRenderInfo borderInfo;
+ borderInfo.combinedRegion = info.combinedRegion;
+ clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo));
+ }
clientCompositionDisplay.deviceHandlesColorTransform =
outputState.usesDeviceComposition || getSkipColorTransform();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 948c0c90bf..c512a1e97f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -62,14 +62,18 @@ void OutputCompositionState::dump(std::string& out) const {
dumpVal(out, "sdrWhitePointNits", sdrWhitePointNits);
dumpVal(out, "clientTargetBrightness", clientTargetBrightness);
dumpVal(out, "displayBrightness", displayBrightness);
-
out.append("\n ");
dumpVal(out, "compositionStrategyPredictionState", ftl::enum_string(strategyPrediction));
+ out.append("\n ");
out.append("\n ");
dumpVal(out, "treate170mAsSrgb", treat170mAsSrgb);
- out += '\n';
+ out.append("\n");
+ for (const auto& borderRenderInfo : borderInfoList) {
+ dumpVal(out, "borderRegion", borderRenderInfo.combinedRegion);
+ }
+ out.append("\n");
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index af3971749b..d47e423ebe 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,7 +101,8 @@ Layer::Layer(const LayerCreationArgs& args)
mClientRef(args.client),
mWindowType(static_cast<WindowInfo::Type>(
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
- mLayerCreationFlags(args.flags) {
+ mLayerCreationFlags(args.flags),
+ mBorderEnabled(false) {
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -1156,6 +1157,18 @@ StretchEffect Layer::getStretchEffect() const {
return StretchEffect{};
}
+bool Layer::enableBorder(bool shouldEnable) {
+ if (mBorderEnabled == shouldEnable) {
+ return false;
+ }
+ mBorderEnabled = shouldEnable;
+ return true;
+}
+
+bool Layer::isBorderEnabled() {
+ return mBorderEnabled;
+}
+
bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
// The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
const auto frameRate = [&] {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ff18fd0b92..85187e1691 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -895,6 +895,8 @@ public:
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
+ bool enableBorder(bool shouldEnable);
+ bool isBorderEnabled();
virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
@@ -1143,7 +1145,10 @@ private:
bool mIsAtRoot = false;
uint32_t mLayerCreationFlags;
+
bool findInHierarchy(const sp<Layer>&);
+
+ bool mBorderEnabled = false;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 040014d9ce..ec87dbef61 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -156,6 +156,10 @@
#define NO_THREAD_SAFETY_ANALYSIS \
_Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
+// To enable layer borders in the system, change the below flag to true.
+#undef DOES_CONTAIN_BORDER
+#define DOES_CONTAIN_BORDER false
+
namespace android {
using namespace std::string_literals;
@@ -2169,6 +2173,20 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
if (auto layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layers.push_back(layerFE);
});
+
+ if (DOES_CONTAIN_BORDER) {
+ refreshArgs.borderInfoList.clear();
+ mDrawingState.traverse([&refreshArgs](Layer* layer) {
+ if (layer->isBorderEnabled()) {
+ compositionengine::BorderRenderInfo info;
+ layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) {
+ info.layerIds.push_back(ilayer->getSequence());
+ });
+ refreshArgs.borderInfoList.emplace_back(std::move(info));
+ }
+ });
+ }
+
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
@@ -4429,6 +4447,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime
if (what & layer_state_t::eBlurRegionsChanged) {
if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eRenderBorderChanged) {
+ if (layer->enableBorder(s.borderEnabled)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eLayerStackChanged) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
// We only allow setting layer stacks for top level layers,
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index ceddf2727a..276431e661 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -34,6 +34,7 @@ cc_test {
"DisplayConfigs_test.cpp",
"DisplayEventReceiver_test.cpp",
"EffectLayer_test.cpp",
+ "LayerBorder_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
new file mode 100644
index 0000000000..4b382140af
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+// TODO: Amend all tests when screenshots become fully reworked for borders
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <chrono> // std::chrono::seconds
+#include <thread> // std::this_thread::sleep_for
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class LayerBorderTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ toHalf3 = ColorTransformHelper::toHalf3;
+ toHalf4 = ColorTransformHelper::toHalf4;
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ mParentLayer = createColorLayer("Parent layer", Color::RED);
+
+ mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */,
+ 0 /* height */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceContainer |
+ ISurfaceComposerClient::eNoColorFill,
+ mParentLayer->getHandle());
+ EXPECT_NE(nullptr, mContainerLayer.get()) << "failed to create container layer";
+
+ mEffectLayer1 = mClient->createSurface(String8("Effect Layer"), 0 /* width */,
+ 0 /* height */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ mContainerLayer->getHandle());
+ EXPECT_NE(nullptr, mEffectLayer1.get()) << "failed to create effect layer 1";
+
+ mEffectLayer2 = mClient->createSurface(String8("Effect Layer"), 0 /* width */,
+ 0 /* height */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ mContainerLayer->getHandle());
+
+ EXPECT_NE(nullptr, mEffectLayer2.get()) << "failed to create effect layer 2";
+
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
+ t.setLayer(mParentLayer, INT32_MAX - 20).show(mParentLayer);
+ t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+
+ t.setColor(mEffectLayer1, toHalf3(Color::BLUE));
+
+ t.setColor(mEffectLayer2, toHalf3(Color::GREEN));
+ });
+ }
+
+ virtual void TearDown() {
+ // Uncomment the line right below when running any of the tests
+ // std::this_thread::sleep_for (std::chrono::seconds(30));
+ LayerTransactionTest::TearDown();
+ mParentLayer = 0;
+ }
+
+ std::function<half3(Color)> toHalf3;
+ std::function<half4(Color)> toHalf4;
+ sp<SurfaceControl> mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2;
+};
+
+TEST_F(LayerBorderTest, OverlappingVisibleRegions) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+ t.enableBorder(mContainerLayer, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, PartiallyCoveredVisibleRegion) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+ t.enableBorder(mEffectLayer1, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, NonOverlappingVisibleRegion) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
+ t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
+
+ t.enableBorder(mContainerLayer, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, EmptyVisibleRegion) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600));
+
+ t.enableBorder(mEffectLayer1, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, ZOrderAdjustment) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+ t.setLayer(mParentLayer, 10);
+ t.setLayer(mEffectLayer1, 30);
+ t.setLayer(mEffectLayer2, 20);
+
+ t.enableBorder(mEffectLayer1, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, GrandChildHierarchy) {
+ sp<SurfaceControl> containerLayer2 =
+ mClient->createSurface(String8("Container Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceContainer |
+ ISurfaceComposerClient::eNoColorFill,
+ mContainerLayer->getHandle());
+ EXPECT_NE(nullptr, containerLayer2.get()) << "failed to create container layer 2";
+
+ sp<SurfaceControl> effectLayer3 =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ containerLayer2->getHandle());
+
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+ t.setCrop(effectLayer3, Rect(400, 400, 800, 800));
+ t.setColor(effectLayer3, toHalf3(Color::BLUE));
+
+ t.enableBorder(mContainerLayer, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(effectLayer3);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, TransparentAlpha) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+ t.setAlpha(mEffectLayer1, 0.0f);
+
+ t.enableBorder(mEffectLayer1, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, SemiTransparentAlpha) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+ t.setAlpha(mEffectLayer2, 0.5f);
+
+ t.enableBorder(mEffectLayer2, true);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, InvisibleLayers) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+ t.enableBorder(mContainerLayer, true);
+ t.hide(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, BufferStateLayer) {
+ asTransaction([&](Transaction& t) {
+ t.hide(mEffectLayer1);
+ t.hide(mEffectLayer2);
+ t.show(mContainerLayer);
+
+ sp<SurfaceControl> bufferStateLayer =
+ mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ mContainerLayer->getHandle());
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(400, 400, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN);
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE);
+
+ t.setBuffer(bufferStateLayer, buffer);
+ t.setPosition(bufferStateLayer, 100, 100);
+ t.show(bufferStateLayer);
+ t.enableBorder(mContainerLayer, true);
+ });
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
index 07916b60a7..38c422a26d 100644
--- a/services/surfaceflinger/tests/utils/ColorUtils.h
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -33,6 +33,10 @@ struct Color {
static const Color WHITE;
static const Color BLACK;
static const Color TRANSPARENT;
+
+ half3 toHalf3() { return half3{r / 255.0f, g / 255.0f, b / 255.0f}; }
+
+ half4 toHalf4() { return half4{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}; }
};
const Color Color::RED{255, 0, 0, 255};
@@ -81,6 +85,14 @@ public:
}
color = ret;
}
+
+ static half3 toHalf3(const Color& color) {
+ return half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f};
+ }
+
+ static half4 toHalf4(const Color& color) {
+ return half4{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f};
+ }
};
} // namespace
} // namespace android