SF: Add Planner flattener
Adds the Planner flattener, which detects when layers are inactive and
flattens them using GPU composition in order to reduce the workload on
the display processor.
Bug: 158790260
Test: atest libcompositionengine_test libsurfaceflinger_unittest
Change-Id: Idc5c2927c8af6fe85653404a7d94c9e68ffc329b
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index e2f2324..f9e5b9a 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -52,6 +52,8 @@
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
srcs: [
+ "src/planner/CachedSet.cpp",
+ "src/planner/Flattener.cpp",
"src/planner/LayerState.cpp",
"src/planner/Planner.cpp",
"src/planner/Predictor.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a8ecb62..4976213 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -280,6 +280,7 @@
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
virtual void postFramebuffer() = 0;
+ virtual void renderCachedSets() = 0;
virtual void chooseCompositionStrategy() = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index fb19216..3a84327 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -30,6 +30,8 @@
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
+#include "LayerFE.h"
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -43,7 +45,6 @@
class CompositionEngine;
class Output;
-class LayerFE;
namespace impl {
struct OutputLayerCompositionState;
@@ -88,8 +89,9 @@
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
- // skipped.
- virtual void writeStateToHWC(bool includeGeometry) = 0;
+ // skipped. If skipLayer is true, then the alpha of the layer is forced to
+ // 0 so that HWC will ignore it.
+ virtual void writeStateToHWC(bool includeGeometry, bool skipLayer) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
@@ -115,6 +117,10 @@
// Returns true if the composition settings scale pixels
virtual bool needsFiltering() const = 0;
+ // Returns a composition list to be used by RenderEngine if the layer has been overridden
+ // during the composition process
+ virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0;
+
// Debugging
virtual void dump(std::string& result) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 044ddd8..eeb20fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -94,6 +94,7 @@
std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
void postFramebuffer() override;
+ void renderCachedSets() override;
void cacheClientCompositionRequests(uint32_t) override;
// Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 8cb5ae8..f113c34 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
@@ -41,7 +42,7 @@
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
- void writeStateToHWC(bool) override;
+ void writeStateToHWC(bool includeGeometry, bool skipLayer) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -51,6 +52,7 @@
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
+ std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
void dump(std::string&) const override;
@@ -66,7 +68,8 @@
private:
Rect calculateInitialCrop() const;
void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
- void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
+ bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 9a118d3..5f834be 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -84,6 +84,13 @@
// The Z order index of this layer on this output
uint32_t z{0};
+ // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
+ struct {
+ sp<GraphicBuffer> buffer = nullptr;
+ sp<Fence> acquireFence = nullptr;
+ Rect displayFrame = {};
+ } overrideInfo;
+
/*
* HWC state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
new file mode 100644
index 0000000..00424b2
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021 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/impl/planner/LayerState.h>
+
+#include <chrono>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+
+std::string durationString(std::chrono::milliseconds duration);
+
+class LayerState;
+
+class CachedSet {
+public:
+ class Layer {
+ public:
+ Layer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ const LayerState* getState() const { return mState; }
+ const std::string& getName() const { return mState->getName(); }
+ Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
+ const sp<GraphicBuffer>& getBuffer() const { return mState->getBuffer(); }
+ int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
+ NonBufferHash getHash() const { return mHash; }
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+
+ private:
+ const LayerState* mState;
+ NonBufferHash mHash;
+ std::chrono::steady_clock::time_point mLastUpdate;
+ };
+
+ CachedSet(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+ CachedSet(Layer layer);
+
+ void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+ NonBufferHash getFingerprint() const { return mFingerprint; }
+ size_t getLayerCount() const { return mLayers.size(); }
+ const Layer& getFirstLayer() const { return mLayers[0]; }
+ const Rect& getBounds() const { return mBounds; }
+ size_t getAge() const { return mAge; }
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<Fence>& getDrawFence() const { return mDrawFence; }
+
+ NonBufferHash getNonBufferHash() const;
+
+ size_t getComponentDisplayCost() const;
+ size_t getCreationCost() const;
+ size_t getDisplayCost() const;
+
+ bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const;
+ bool hasReadyBuffer() const;
+
+ // Decomposes this CachedSet into a vector of its layers as individual CachedSets
+ std::vector<CachedSet> decompose() const;
+
+ void updateAge(std::chrono::steady_clock::time_point now);
+
+ void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
+ void append(const CachedSet& other) {
+ mBuffer = nullptr;
+ mDrawFence = nullptr;
+
+ mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(other.mBounds);
+ mBounds = boundingRegion.getBounds();
+ }
+ void incrementAge() { ++mAge; }
+
+ void render(renderengine::RenderEngine&);
+
+ void dump(std::string& result) const;
+
+private:
+ CachedSet() = default;
+
+ NonBufferHash mFingerprint = 0;
+ std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
+ std::vector<Layer> mLayers;
+ Rect mBounds = Rect::EMPTY_RECT;
+ size_t mAge = 0;
+ sp<GraphicBuffer> mBuffer;
+ sp<Fence> mDrawFence;
+
+ static const bool sDebugHighlighLayers;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
new file mode 100644
index 0000000..6c86408
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2021 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/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+
+#include <vector>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+using namespace std::chrono_literals;
+
+class LayerState;
+class Predictor;
+
+class Flattener {
+public:
+ Flattener(Predictor& predictor) : mPredictor(predictor) {}
+
+ void setDisplaySize(ui::Size size) { mDisplaySize = size; }
+
+ NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash);
+
+ void renderCachedSets(renderengine::RenderEngine&);
+
+ void reset();
+
+ void dump(std::string& result) const;
+
+private:
+ size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
+
+ void resetActivities(NonBufferHash, std::chrono::steady_clock::time_point now);
+
+ void updateLayersHash();
+
+ bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
+ std::chrono::steady_clock::time_point now);
+
+ void buildCachedSets(std::chrono::steady_clock::time_point now);
+
+ Predictor& mPredictor;
+
+ ui::Size mDisplaySize;
+
+ NonBufferHash mCurrentGeometry;
+ std::chrono::steady_clock::time_point mLastGeometryUpdate;
+
+ std::vector<CachedSet> mLayers;
+ NonBufferHash mLayersHash = 0;
+ std::optional<CachedSet> mNewCachedSet;
+
+ // Statistics
+ size_t mUnflattenedDisplayCost = 0;
+ size_t mFlattenedDisplayCost = 0;
+ std::unordered_map<size_t, size_t> mInitialLayerCounts;
+ std::unordered_map<size_t, size_t> mFinalLayerCounts;
+ size_t mCachedSetCreationCount = 0;
+ size_t mCachedSetCreationCost = 0;
+ std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
+
+ static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index ad53d27..e96abb7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -17,6 +17,7 @@
#pragma once
#include <compositionengine/Output.h>
+#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/impl/planner/Predictor.h>
#include <utils/String16.h>
@@ -28,6 +29,10 @@
namespace android {
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
namespace compositionengine::impl::planner {
// This is the top level class for layer caching. It is responsible for
@@ -36,8 +41,15 @@
// as a more efficient representation of parts of the layer stack.
class Planner {
public:
+ Planner() : mFlattener(mPredictor) {}
+
+ void setDisplaySize(ui::Size);
+
// Updates the Planner with the current set of layers before a composition strategy is
// determined.
+ // The Planner will call to the Flattener to determine to:
+ // 1. Replace any cached sets with a newly available flattened cached set
+ // 2. Create a new cached set if possible
void plan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
@@ -46,6 +58,9 @@
void reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+ // The planner will call to the Flattener to render any pending cached set
+ void renderCachedSets(renderengine::RenderEngine&);
+
void dump(const Vector<String16>& args, std::string&);
private:
@@ -56,8 +71,10 @@
std::vector<const LayerState*> mCurrentLayers;
Predictor mPredictor;
+ Flattener mFlattener;
std::optional<Predictor::PredictedPlan> mPredictedPlan;
+ NonBufferHash mFlattenedHash = 0;
};
} // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index 38c73f7..422af77 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -210,8 +210,8 @@
std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&,
NonBufferHash) const;
- void recordResult(std::optional<PredictedPlan> predictedPlan,
- const std::vector<const LayerState*>&, Plan result);
+ void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
void dump(std::string&) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 9299199..5aa53e5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -107,6 +107,7 @@
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 81e1fc7..2454ff7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -39,7 +39,7 @@
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
- MOCK_METHOD1(writeStateToHWC, void(bool));
+ MOCK_METHOD2(writeStateToHWC, void(bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
@@ -49,6 +49,7 @@
MOCK_METHOD0(prepareForDeviceLayerRequests, void());
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
+ MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 57b7481..dc1aacc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -198,6 +198,10 @@
const Rect newOrientedBounds(orientedSize);
state.orientedDisplaySpace.bounds = newOrientedBounds;
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
+
dirtyEntireOutput();
}
@@ -317,7 +321,11 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
+ const auto size = mRenderSurface->getSize();
+ editState().framebufferSpace.bounds = Rect(size);
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
dirtyEntireOutput();
}
@@ -402,6 +410,7 @@
devOptRepaintFlash(refreshArgs);
finishFrame(refreshArgs);
postFramebuffer();
+ renderCachedSets();
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -701,8 +710,23 @@
return;
}
+ sp<GraphicBuffer> previousOverride = nullptr;
for (auto* layer : getOutputLayersOrderedByZ()) {
- layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+ bool skipLayer = false;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ if (previousOverride != nullptr &&
+ layer->getState().overrideInfo.buffer == previousOverride) {
+ ALOGV("Skipping redundant buffer");
+ skipLayer = true;
+ }
+ previousOverride = layer->getState().overrideInfo.buffer;
+ }
+
+ // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+ // only when the geometry actually changes
+ const bool includeGeometry = refreshArgs.updatingGeometryThisFrame ||
+ layer->getState().overrideInfo.buffer != nullptr || skipLayer;
+ layer->writeStateToHWC(includeGeometry, skipLayer);
}
}
@@ -1126,10 +1150,16 @@
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
.disableBlurs = disableBlurs};
- std::vector<LayerFE::LayerSettings> results =
- layerFE.prepareClientCompositionList(targetSettings);
- if (realContentIsVisible && !results.empty()) {
- layer->editState().clientCompositionTimestamp = systemTime();
+
+ std::vector<LayerFE::LayerSettings> results;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ results = layer->getOverrideCompositionList();
+ ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ } else {
+ results = layerFE.prepareClientCompositionList(targetSettings);
+ if (realContentIsVisible && !results.empty()) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ }
}
clientCompositionLayers.insert(clientCompositionLayers.end(),
@@ -1220,6 +1250,12 @@
mReleasedLayers.clear();
}
+void Output::renderCachedSets() {
+ if (mPlanner) {
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine());
+ }
+}
+
void Output::dirtyEntireOutput() {
auto& outputState = editState();
outputState.dirtyRegion.set(outputState.displaySpace.bounds);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0faab6f..54784a2 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,7 +16,6 @@
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -313,7 +312,7 @@
}
}
-void OutputLayer::writeStateToHWC(bool includeGeometry) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -336,7 +335,8 @@
if (includeGeometry) {
writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
- writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+ writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
+ skipLayer);
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
@@ -352,23 +352,27 @@
HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
const auto& outputDependentState = getState();
- if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
- getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
- outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
- outputDependentState.displayFrame.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ Rect displayFrame = outputDependentState.displayFrame;
+ FloatRect sourceCrop = outputDependentState.sourceCrop;
+ if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
+ displayFrame = outputDependentState.overrideInfo.displayFrame;
+ sourceCrop = displayFrame.toFloatRect();
}
- if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
- error != hal::Error::NONE) {
+ ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
+ displayFrame.right, displayFrame.bottom);
+
+ if (auto error = hwcLayer->setDisplayFrame(displayFrame); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+ getLayerFE().getDebugName(), displayFrame.left, displayFrame.top, displayFrame.right,
+ displayFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setSourceCrop(sourceCrop); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
"%s (%d)",
- getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
- outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
- outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ getLayerFE().getDebugName(), sourceCrop.left, sourceCrop.top, sourceCrop.right,
+ sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
@@ -389,7 +393,8 @@
}
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+ HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
error != hal::Error::NONE) {
ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
@@ -397,10 +402,12 @@
static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+ const float alpha = skipLayer ? 0.0f : outputIndependentState.alpha;
+ ALOGV("Writing alpha %f", alpha);
+
+ if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(), alpha,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
for (const auto& [name, entry] : outputIndependentState.metadata) {
@@ -509,19 +516,26 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
+ sp<GraphicBuffer> buffer = outputIndependentState.buffer;
+ sp<Fence> acquireFence = outputIndependentState.acquireFence;
+ if (getState().overrideInfo.buffer != nullptr) {
+ buffer = getState().overrideInfo.buffer;
+ acquireFence = getState().overrideInfo.acquireFence;
+ }
+
+ ALOGV("Writing buffer %p", buffer.get());
+
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
// We need access to the output-dependent state for the buffer cache there,
// though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot,
- outputIndependentState.buffer, &hwcSlot,
- &hwcBuffer);
+ editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
+ &hwcSlot, &hwcBuffer);
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.buffer->handle, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -652,6 +666,26 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
+std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+ if (getState().overrideInfo.buffer == nullptr) {
+ return {};
+ }
+
+ LayerFE::LayerSettings settings;
+ settings.geometry = renderengine::Geometry{
+ .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+ };
+ settings.bufferId = getState().overrideInfo.buffer->getId();
+ settings.source =
+ renderengine::PixelSource{.buffer = renderengine::Buffer{
+ .buffer = getState().overrideInfo.buffer,
+ .fence = getState().overrideInfo.acquireFence,
+ }};
+ settings.alpha = 1.0f;
+
+ return {static_cast<LayerFE::LayerSettings>(settings)};
+}
+
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
new file mode 100644
index 0000000..ab3fe9e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+// #define LOG_NDEBUG 0
+
+#include <android-base/properties.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <math/HashCombine.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/RenderEngine.h>
+
+namespace android::compositionengine::impl::planner {
+
+const bool CachedSet::sDebugHighlighLayers =
+ base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
+
+std::string durationString(std::chrono::milliseconds duration) {
+ using namespace std::chrono_literals;
+
+ std::string result;
+
+ if (duration >= 1h) {
+ const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
+ base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
+ duration -= hours;
+ }
+ if (duration >= 1min) {
+ const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
+ base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
+ duration -= minutes;
+ }
+ base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
+
+ return result;
+}
+
+CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
+ : mState(state), mHash(state->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+
+CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
+ : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+ addLayer(layer, lastUpdate);
+}
+
+CachedSet::CachedSet(Layer layer)
+ : mFingerprint(layer.getHash()),
+ mLastUpdate(layer.getLastUpdate()),
+ mBounds(layer.getDisplayFrame()) {
+ mLayers.emplace_back(std::move(layer));
+}
+
+void CachedSet::addLayer(const LayerState* layer,
+ std::chrono::steady_clock::time_point lastUpdate) {
+ mLayers.emplace_back(layer, lastUpdate);
+
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(layer->getDisplayFrame());
+ mBounds = boundingRegion.getBounds();
+}
+
+NonBufferHash CachedSet::getNonBufferHash() const {
+ if (mLayers.size() == 1) {
+ return mFingerprint;
+ }
+
+ // TODO(b/181192080): Add all fields which contribute to geometry of override layer (e.g.,
+ // dataspace)
+ size_t hash = 0;
+ android::hashCombineSingle(hash, mBounds);
+ return hash;
+}
+
+size_t CachedSet::getComponentDisplayCost() const {
+ size_t displayCost = 0;
+
+ for (const Layer& layer : mLayers) {
+ displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
+ layer.getDisplayFrame().height());
+ }
+
+ return displayCost;
+}
+
+size_t CachedSet::getCreationCost() const {
+ if (mLayers.size() == 1) {
+ return 0;
+ }
+
+ // Reads
+ size_t creationCost = getComponentDisplayCost();
+
+ // Write - assumes that the output buffer only gets written once per pixel
+ creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
+
+ return creationCost;
+}
+
+size_t CachedSet::getDisplayCost() const {
+ return static_cast<size_t>(mBounds.width() * mBounds.height());
+}
+
+bool CachedSet::hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const {
+ for (const Layer& layer : mLayers) {
+ if (layer.getFramesSinceBufferUpdate() == 0) {
+ return true;
+ }
+ ++layers;
+ }
+ return false;
+}
+
+bool CachedSet::hasReadyBuffer() const {
+ return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+}
+
+std::vector<CachedSet> CachedSet::decompose() const {
+ std::vector<CachedSet> layers;
+
+ std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
+ [](Layer layer) { return CachedSet(std::move(layer)); });
+
+ return layers;
+}
+
+void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
+ LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
+ __func__);
+
+ if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
+ mLastUpdate = now;
+ mAge = 0;
+ }
+}
+
+void CachedSet::render(renderengine::RenderEngine& renderEngine) {
+ renderengine::DisplaySettings displaySettings{
+ .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+ .clip = mBounds,
+ };
+
+ Region clearRegion = Region::INVALID_REGION;
+ Rect viewport = mBounds;
+ LayerFE::ClientCompositionTargetSettings targetSettings{
+ .clip = Region(mBounds),
+ .needsFiltering = false,
+ .isSecure = true,
+ .supportsProtectedContent = false,
+ .clearRegion = clearRegion,
+ .viewport = viewport,
+ // TODO(181192086): Propagate the Output's dataspace instead of using UNKNOWN
+ .dataspace = ui::Dataspace::UNKNOWN,
+ .realContentIsVisible = true,
+ .clearContent = false,
+ .disableBlurs = false,
+ };
+
+ std::vector<renderengine::LayerSettings> layerSettings;
+ for (const auto& layer : mLayers) {
+ const auto clientCompositionList =
+ layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
+ clientCompositionList.cend());
+ }
+
+ std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
+ std::transform(layerSettings.cbegin(), layerSettings.cend(),
+ std::back_inserter(layerSettingsPointers),
+ [](const renderengine::LayerSettings& settings) { return &settings; });
+
+ if (sDebugHighlighLayers) {
+ renderengine::LayerSettings highlight{
+ .geometry =
+ renderengine::Geometry{
+ .boundaries = FloatRect(0.0f, 0.0f,
+ static_cast<float>(mBounds.getWidth()),
+ static_cast<float>(mBounds.getHeight())),
+ },
+ .source =
+ renderengine::PixelSource{
+ .solidColor = half3(0.25f, 0.0f, 0.5f),
+ },
+ .alpha = half(0.05f),
+ };
+
+ layerSettingsPointers.emplace_back(&highlight);
+ }
+
+ const uint64_t usageFlags = GraphicBuffer::USAGE_HW_RENDER | GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()),
+ static_cast<uint32_t>(mBounds.getHeight()),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
+ LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
+ base::unique_fd drawFence;
+ status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
+ base::unique_fd(), &drawFence);
+
+ if (result == NO_ERROR) {
+ mBuffer = buffer;
+ mDrawFence = new Fence(drawFence.release());
+ }
+}
+
+void CachedSet::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
+ base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
+ mFingerprint, durationString(lastUpdate).c_str(), mAge);
+
+ if (mLayers.size() == 1) {
+ base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
+ base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
+ } else {
+ result.append(" Cached set of:");
+ for (const Layer& layer : mLayers) {
+ base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
+ }
+ }
+
+ base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost());
+ base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
new file mode 100644
index 0000000..0c09714
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+// #define LOG_NDEBUG 0
+
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+
+using time_point = std::chrono::steady_clock::time_point;
+using namespace std::chrono_literals;
+
+namespace android::compositionengine::impl::planner {
+
+NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash) {
+ const auto now = std::chrono::steady_clock::now();
+
+ const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
+ mUnflattenedDisplayCost += unflattenedDisplayCost;
+
+ if (mCurrentGeometry != hash) {
+ resetActivities(hash, now);
+ mFlattenedDisplayCost += unflattenedDisplayCost;
+ return hash;
+ }
+
+ ++mInitialLayerCounts[layers.size()];
+
+ if (mergeWithCachedSets(layers, now)) {
+ hash = mLayersHash;
+ }
+
+ ++mFinalLayerCounts[mLayers.size()];
+
+ buildCachedSets(now);
+
+ return hash;
+}
+
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+ if (!mNewCachedSet) {
+ return;
+ }
+
+ mNewCachedSet->render(renderEngine);
+}
+
+void Flattener::reset() {
+ resetActivities(0, std::chrono::steady_clock::now());
+
+ mUnflattenedDisplayCost = 0;
+ mFlattenedDisplayCost = 0;
+ mInitialLayerCounts.clear();
+ mFinalLayerCounts.clear();
+ mCachedSetCreationCount = 0;
+ mCachedSetCreationCost = 0;
+ mInvalidatedCachedSetAges.clear();
+}
+
+void Flattener::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ base::StringAppendF(&result, "Flattener state:\n");
+
+ result.append("\n Statistics:\n");
+
+ result.append(" Display cost (in screen-size buffers):\n");
+ const size_t displayArea = static_cast<size_t>(mDisplaySize.width * mDisplaySize.height);
+ base::StringAppendF(&result, " Unflattened: %.2f\n",
+ static_cast<float>(mUnflattenedDisplayCost) / displayArea);
+ base::StringAppendF(&result, " Flattened: %.2f\n",
+ static_cast<float>(mFlattenedDisplayCost) / displayArea);
+
+ const auto compareLayerCounts = [](const std::pair<size_t, size_t>& left,
+ const std::pair<size_t, size_t>& right) {
+ return left.first < right.first;
+ };
+
+ const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
+ mInitialLayerCounts.cend(), compareLayerCounts)
+ ->first;
+
+ result.append("\n Initial counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t initial = mInitialLayerCounts.count(count) > 0 ? mInitialLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, initial);
+ }
+
+ result.append("\n Final counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t final = mFinalLayerCounts.count(count) > 0 ? mFinalLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, final);
+ }
+
+ base::StringAppendF(&result, "\n Cached sets created: %zd\n", mCachedSetCreationCount);
+ base::StringAppendF(&result, " Cost: %.2f\n",
+ static_cast<float>(mCachedSetCreationCost) / displayArea);
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastGeometryUpdate);
+ base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
+ durationString(lastUpdate).c_str());
+
+ result.append(" Current layers:");
+ for (const CachedSet& layer : mLayers) {
+ result.append("\n");
+ layer.dump(result);
+ }
+}
+
+size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
+ Region coveredRegion;
+ size_t displayCost = 0;
+ bool hasClientComposition = false;
+
+ for (const LayerState* layer : layers) {
+ coveredRegion.orSelf(layer->getDisplayFrame());
+
+ // Regardless of composition type, we always have to read each input once
+ displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
+ layer->getDisplayFrame().height());
+
+ hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+ }
+
+ if (hasClientComposition) {
+ // If there is client composition, the client target buffer has to be both written by the
+ // GPU and read by the DPU, so we pay its cost twice
+ displayCost += 2 *
+ static_cast<size_t>(coveredRegion.bounds().width() *
+ coveredRegion.bounds().height());
+ }
+
+ return displayCost;
+}
+
+void Flattener::resetActivities(NonBufferHash hash, time_point now) {
+ ALOGV("[%s]", __func__);
+
+ mCurrentGeometry = hash;
+ mLastGeometryUpdate = now;
+
+ for (const CachedSet& cachedSet : mLayers) {
+ if (cachedSet.getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[cachedSet.getAge()];
+ }
+ }
+
+ mLayers.clear();
+
+ if (mNewCachedSet) {
+ ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
+ mNewCachedSet = std::nullopt;
+ }
+}
+
+void Flattener::updateLayersHash() {
+ size_t hash = 0;
+ for (const auto& layer : mLayers) {
+ android::hashCombineSingleHashed(hash, layer.getNonBufferHash());
+ }
+ mLayersHash = hash;
+}
+
+bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
+ std::vector<CachedSet> merged;
+
+ if (mLayers.empty()) {
+ merged.reserve(layers.size());
+ for (const LayerState* layer : layers) {
+ merged.emplace_back(layer, now);
+ mFlattenedDisplayCost += merged.back().getDisplayCost();
+ }
+ mLayers = std::move(merged);
+ return false;
+ }
+
+ ALOGV("[%s] Incoming layers:", __func__);
+ for (const LayerState* layer : layers) {
+ ALOGV("%s", layer->getName().c_str());
+ }
+
+ ALOGV("[%s] Current layers:", __func__);
+ for (const CachedSet& layer : mLayers) {
+ std::string dump;
+ layer.dump(dump);
+ ALOGV("%s", dump.c_str());
+ }
+
+ auto currentLayerIter = mLayers.begin();
+ auto incomingLayerIter = layers.begin();
+ while (incomingLayerIter != layers.end()) {
+ if (mNewCachedSet &&
+ mNewCachedSet->getFingerprint() ==
+ (*incomingLayerIter)->getHash(LayerStateField::Buffer)) {
+ if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) {
+ ALOGV("[%s] Dropping new cached set", __func__);
+ ++mInvalidatedCachedSetAges[0];
+ mNewCachedSet = std::nullopt;
+ } else if (mNewCachedSet->hasReadyBuffer()) {
+ ALOGV("[%s] Found ready buffer", __func__);
+ size_t skipCount = mNewCachedSet->getLayerCount();
+ while (skipCount != 0) {
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = mNewCachedSet->getBuffer(),
+ .acquireFence = mNewCachedSet->getDrawFence(),
+ .displayFrame = mNewCachedSet->getBounds(),
+ };
+ ++incomingLayerIter;
+ }
+
+ if (currentLayerIter->getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ }
+ ++currentLayerIter;
+
+ skipCount -= layerCount;
+ }
+ merged.emplace_back(std::move(*mNewCachedSet));
+ mNewCachedSet = std::nullopt;
+ continue;
+ }
+ }
+
+ if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) {
+ currentLayerIter->incrementAge();
+ merged.emplace_back(*currentLayerIter);
+
+ // Skip the incoming layers corresponding to this valid current layer
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = currentLayerIter->getBuffer(),
+ .acquireFence = currentLayerIter->getDrawFence(),
+ .displayFrame = currentLayerIter->getBounds(),
+ };
+ ++incomingLayerIter;
+ }
+ } else if (currentLayerIter->getLayerCount() > 1) {
+ // Break the current layer into its constituent layers
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ for (CachedSet& layer : currentLayerIter->decompose()) {
+ layer.updateAge(now);
+ merged.emplace_back(layer);
+ ++incomingLayerIter;
+ }
+ } else {
+ currentLayerIter->updateAge(now);
+ merged.emplace_back(*currentLayerIter);
+ ++incomingLayerIter;
+ }
+ ++currentLayerIter;
+ }
+
+ for (const CachedSet& layer : merged) {
+ mFlattenedDisplayCost += layer.getDisplayCost();
+ }
+
+ mLayers = std::move(merged);
+ updateLayersHash();
+ return true;
+}
+
+void Flattener::buildCachedSets(time_point now) {
+ struct Run {
+ Run(std::vector<CachedSet>::const_iterator start, size_t length)
+ : start(start), length(length) {}
+
+ std::vector<CachedSet>::const_iterator start;
+ size_t length;
+ };
+
+ if (mLayers.empty()) {
+ ALOGV("[%s] No layers found, returning", __func__);
+ return;
+ }
+
+ std::vector<Run> runs;
+ bool isPartOfRun = false;
+ for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
+ if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
+ // Layer is inactive
+ if (isPartOfRun) {
+ runs.back().length += currentSet->getLayerCount();
+ } else {
+ // Runs can't start with a non-buffer layer
+ if (currentSet->getFirstLayer().getBuffer() == nullptr) {
+ ALOGV("[%s] Skipping initial non-buffer layer", __func__);
+ } else {
+ runs.emplace_back(currentSet, currentSet->getLayerCount());
+ isPartOfRun = true;
+ }
+ }
+ } else {
+ // Runs must be at least 2 sets long or there's nothing to combine
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ isPartOfRun = false;
+ }
+ }
+
+ // Check for at least 2 sets one more time in case the set includes the last layer
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
+
+ if (runs.empty()) {
+ return;
+ }
+
+ mNewCachedSet.emplace(*runs[0].start);
+ mNewCachedSet->setLastUpdate(now);
+ auto currentSet = runs[0].start;
+ while (mNewCachedSet->getLayerCount() < runs[0].length) {
+ ++currentSet;
+ mNewCachedSet->append(*currentSet);
+ }
+
+ // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
+ mPredictor.getPredictedPlan({}, 0);
+
+ ++mCachedSetCreationCount;
+ mCachedSetCreationCost += mNewCachedSet->getCreationCost();
+ std::string setDump;
+ mNewCachedSet->dump(setDump);
+ ALOGV("[%s] Added new cached set:\n%s", __func__, setDump.c_str());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 619d008..52efff5 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -19,10 +19,16 @@
#undef LOG_TAG
#define LOG_TAG "Planner"
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
namespace android::compositionengine::impl::planner {
+void Planner::setDisplaySize(ui::Size size) {
+ mFlattener.setDisplaySize(size);
+}
+
void Planner::plan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
std::unordered_set<LayerId> removedLayers;
@@ -39,7 +45,9 @@
// Track changes from previous info
LayerState& state = layerEntry->second;
Flags<LayerStateField> differences = state.update(layer);
- if (differences.get() != 0) {
+ if (differences.get() == 0) {
+ state.incrementFramesSinceBufferUpdate();
+ } else {
ALOGV("Layer %s changed: %s", state.getName().c_str(),
differences.string().c_str());
@@ -74,10 +82,21 @@
mCurrentLayers.clear();
mCurrentLayers.reserve(currentLayerIds.size());
std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
- std::back_inserter(mCurrentLayers),
- [this](LayerId id) { return &mPreviousLayers.at(id); });
+ std::back_inserter(mCurrentLayers), [this](LayerId id) {
+ LayerState* state = &mPreviousLayers.at(id);
+ state->getOutputLayer()->editState().overrideInfo = {};
+ return state;
+ });
- mPredictedPlan = mPredictor.getPredictedPlan(mCurrentLayers, getNonBufferHash(mCurrentLayers));
+ const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
+ mFlattenedHash = mFlattener.flattenLayers(mCurrentLayers, hash);
+ const bool layersWereFlattened = hash != mFlattenedHash;
+ ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
+
+ mPredictedPlan =
+ mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+ : mCurrentLayers,
+ mFlattenedHash);
if (mPredictedPlan) {
ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
} else {
@@ -88,7 +107,18 @@
void Planner::reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
Plan finalPlan;
+ const GraphicBuffer* currentOverrideBuffer = nullptr;
+ bool hasSkippedLayers = false;
for (auto layer : layers) {
+ const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.get();
+ if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
+ // Skip this layer since it is part of a previous cached set
+ hasSkippedLayers = true;
+ continue;
+ }
+
+ currentOverrideBuffer = overrideBuffer;
+
const bool forcedOrRequestedClient =
layer->getState().forceClientComposition || layer->requiresClientComposition();
@@ -98,7 +128,12 @@
: layer->getLayerFE().getCompositionState()->compositionType);
}
- mPredictor.recordResult(mPredictedPlan, mCurrentLayers, finalPlan);
+ mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
+ finalPlan);
+}
+
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+ mFlattener.renderCachedSets(renderEngine);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
@@ -194,6 +229,10 @@
}
// If there are no specific commands, dump the usual state
+
+ mFlattener.dump(result);
+ result.append("\n");
+
mPredictor.dump(result);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 28be9db..ba5e64d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -176,7 +176,9 @@
}
void Predictor::recordResult(std::optional<PredictedPlan> predictedPlan,
- const std::vector<const LayerState*>& layers, Plan result) {
+ NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>& layers, bool hasSkippedLayers,
+ Plan result) {
if (predictedPlan) {
recordPredictedResult(*predictedPlan, layers, std::move(result));
return;
@@ -184,14 +186,12 @@
++mMissCount;
- if (findSimilarPrediction(layers, result)) {
+ if (!hasSkippedLayers && findSimilarPrediction(layers, result)) {
return;
}
- const NonBufferHash hash = getNonBufferHash(layers);
-
- ALOGV("[%s] Adding novel candidate %zx", __func__, hash);
- mCandidates.emplace_front(hash, Prediction(layers, result));
+ ALOGV("[%s] Adding novel candidate %zx", __func__, flattenedHash);
+ mCandidates.emplace_front(flattenedHash, Prediction(layers, result));
if (mCandidates.size() > MAX_CANDIDATES) {
mCandidates.pop_back();
}
@@ -476,4 +476,4 @@
}
}
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dcfc162..9dd199d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -840,19 +840,19 @@
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -861,7 +861,7 @@
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -891,7 +891,7 @@
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
expectSetColorCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -901,7 +901,7 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -911,7 +911,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -921,7 +921,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -934,7 +934,7 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -944,7 +944,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -956,7 +956,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -969,7 +969,7 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -980,7 +980,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 8dcace6..3059beb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -783,11 +783,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -810,11 +813,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -836,11 +842,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -1648,6 +1657,7 @@
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
};
StrictMock<OutputPartialMock> mOutput;
@@ -1667,6 +1677,7 @@
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(Ref(args)));
EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, renderCachedSets());
mOutput.present(args);
}
@@ -3482,7 +3493,8 @@
mOutput.editState().isEnabled = true;
EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(mLayer.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
@@ -4063,11 +4075,14 @@
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4092,11 +4107,14 @@
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);