Skip client composition requests
Keep track of client compositions requests in CompositionEngine and
the buffer used to render the request. If the requests do not change
and the buffer matches, reuse the buffer instead of going
to client composition.
Certain layers properties (buffer format, rounded corner or shadows)
will force the device to go into GPU or mixed composition. In mixed
composition scenarios, we can avoid redundant client composition
requests by reusing the RenderSurface buffers. If the device goes
into GPU composition then all the layers will be composited and there
will be no performance benefit.
Bug: b/136561771, b/144690120
Test: dump SurfaceFlinger --timestats -dump
Test: manual testing with mixed and gpu composition scenarios
Test: atest libcompositionengine_test libsurfaceflinger_unittest SurfaceFlinger_test
Change-Id: I466d5dcded0c9fcfa64bc72fd91dfaddd795f315
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
new file mode 100644
index 0000000..acaaf4e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+
+namespace android::compositionengine::impl {
+
+namespace {
+LayerFE::LayerSettings getLayerSettingsSnapshot(const LayerFE::LayerSettings& settings) {
+ LayerFE::LayerSettings snapshot = settings;
+ snapshot.source.buffer.buffer = nullptr;
+ snapshot.source.buffer.fence = nullptr;
+ return snapshot;
+}
+
+inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs,
+ const renderengine::LayerSettings& rhs) {
+ return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
+ lhs.sourceDataspace == rhs.sourceDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+}
+
+inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
+ return lhs.textureName == rhs.textureName &&
+ lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ lhs.textureTransform == rhs.textureTransform &&
+ lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+ lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+ lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+ lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
+ const renderengine::LayerSettings& rhs) {
+ // compare LayerSettings without LayerSettings.PixelSource
+ return equalIgnoringSource(lhs, rhs) &&
+
+ // compare LayerSettings.PixelSource without buffer
+ lhs.source.solidColor == rhs.source.solidColor &&
+
+ // compare LayerSettings.PixelSource.Buffer without buffer & fence
+ equalIgnoringBuffer(lhs.source.buffer, rhs.source.buffer);
+}
+
+bool layerSettingsAreEqual(const LayerFE::LayerSettings& lhs, const LayerFE::LayerSettings& rhs) {
+ return lhs.bufferId == rhs.bufferId && lhs.frameNumber == rhs.frameNumber &&
+ equalIgnoringBuffer(lhs, rhs);
+}
+
+} // namespace
+
+ClientCompositionRequestCache::ClientCompositionRequest::ClientCompositionRequest(
+ const renderengine::DisplaySettings& initDisplay,
+ const std::vector<LayerFE::LayerSettings>& initLayerSettings)
+ : display(initDisplay) {
+ layerSettings.reserve(initLayerSettings.size());
+ for (const LayerFE::LayerSettings& settings : initLayerSettings) {
+ layerSettings.push_back(getLayerSettingsSnapshot(settings));
+ }
+}
+
+bool ClientCompositionRequestCache::ClientCompositionRequest::equals(
+ const renderengine::DisplaySettings& newDisplay,
+ const std::vector<LayerFE::LayerSettings>& newLayerSettings) const {
+ return newDisplay == display &&
+ std::equal(layerSettings.begin(), layerSettings.end(), newLayerSettings.begin(),
+ newLayerSettings.end(), layerSettingsAreEqual);
+}
+
+bool ClientCompositionRequestCache::exists(
+ uint64_t bufferId, const renderengine::DisplaySettings& display,
+ const std::vector<LayerFE::LayerSettings>& layerSettings) const {
+ for (const auto& [cachedBufferId, cachedRequest] : mCache) {
+ if (cachedBufferId == bufferId) {
+ return cachedRequest.equals(display, layerSettings);
+ }
+ }
+ return false;
+}
+
+void ClientCompositionRequestCache::add(uint64_t bufferId,
+ const renderengine::DisplaySettings& display,
+ const std::vector<LayerFE::LayerSettings>& layerSettings) {
+ const ClientCompositionRequest request(display, layerSettings);
+ for (auto& [cachedBufferId, cachedRequest] : mCache) {
+ if (cachedBufferId == bufferId) {
+ cachedRequest = std::move(request);
+ return;
+ }
+ }
+
+ if (mCache.size() >= mMaxCacheSize) {
+ mCache.pop_front();
+ }
+
+ mCache.emplace_back(bufferId, std::move(request));
+}
+
+void ClientCompositionRequestCache::remove(uint64_t bufferId) {
+ for (auto it = mCache.begin(); it != mCache.end(); it++) {
+ if (it->first == bufferId) {
+ mCache.erase(it);
+ return;
+ }
+ }
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 20f765c..13f5137 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -137,6 +137,10 @@
compositionengine::impl::createRenderSurface(getCompositionEngine(), *this, args));
}
+void Display::createClientCompositionCache(uint32_t cacheSize) {
+ cacheClientCompositionRequests(cacheSize);
+}
+
std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
const std::shared_ptr<compositionengine::Layer>& layer,
const sp<compositionengine::LayerFE>& layerFE) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 650b5c1..df33cf0 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -231,6 +231,14 @@
dirtyEntireOutput();
}
+void Output::cacheClientCompositionRequests(uint32_t cacheSize) {
+ if (cacheSize == 0) {
+ mClientCompositionRequestCache.reset();
+ } else {
+ mClientCompositionRequestCache = std::make_unique<ClientCompositionRequestCache>(cacheSize);
+ }
+};
+
void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
}
@@ -806,6 +814,7 @@
ALOGV(__FUNCTION__);
const auto& outputState = getState();
+ OutputCompositionState& outputCompositionState = editState();
const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
outputState.usesClientComposition};
base::unique_fd readyFence;
@@ -839,7 +848,7 @@
clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
// Generate the client composition requests for the layers on this output.
- std::vector<renderengine::LayerSettings> clientCompositionLayers =
+ std::vector<LayerFE::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
clientCompositionDisplay.clearRegion,
clientCompositionDisplay.outputDataspace);
@@ -871,6 +880,19 @@
return std::nullopt;
}
+ // Check if the client composition requests were rendered into the provided graphic buffer. If
+ // so, we can reuse the buffer and avoid client composition.
+ if (mClientCompositionRequestCache) {
+ if (mClientCompositionRequestCache->exists(buf->getId(), clientCompositionDisplay,
+ clientCompositionLayers)) {
+ outputCompositionState.reusedClientComposition = true;
+ setExpensiveRenderingExpected(false);
+ return readyFence;
+ }
+ mClientCompositionRequestCache->add(buf->getId(), clientCompositionDisplay,
+ clientCompositionLayers);
+ }
+
// We boost GPU frequency here because there will be color spaces conversion
// or complex GPU shaders and it's expensive. We boost the GPU frequency so that
// GPU composition can finish in time. We must reset GPU frequency afterwards,
@@ -882,10 +904,25 @@
setExpensiveRenderingExpected(true);
}
+ std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
+ clientCompositionLayerPointers.reserve(clientCompositionLayers.size());
+ std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+ std::back_inserter(clientCompositionLayerPointers),
+ [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings* {
+ return &settings;
+ });
+
const nsecs_t renderEngineStart = systemTime();
- renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
- buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
- &readyFence);
+ status_t status =
+ renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
+ buf->getNativeBuffer(), /*useFramebufferCache=*/true,
+ std::move(fd), &readyFence);
+
+ if (status != NO_ERROR && mClientCompositionRequestCache) {
+ // If rendering was not successful, remove the request from the cache.
+ mClientCompositionRequestCache->remove(buf->getId());
+ }
+
auto& timeStats = getCompositionEngine().getTimeStats();
if (readyFence.get() < 0) {
timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
@@ -898,9 +935,9 @@
return readyFence;
}
-std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests(
+std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
- std::vector<renderengine::LayerSettings> clientCompositionLayers;
+ std::vector<LayerFE::LayerSettings> clientCompositionLayers;
ALOGV("Rendering client layers");
const auto& outputState = getState();
@@ -944,15 +981,17 @@
supportsProtectedContent,
clientComposition ? clearRegion : dummyRegion,
};
- if (auto result = layerFE.prepareClientComposition(targetSettings)) {
+ if (std::optional<LayerFE::LayerSettings> result =
+ layerFE.prepareClientComposition(targetSettings)) {
if (!clientComposition) {
- auto& layerSettings = *result;
+ LayerFE::LayerSettings& layerSettings = *result;
layerSettings.source.buffer.buffer = nullptr;
layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
layerSettings.alpha = half(0.0);
layerSettings.disableBlending = true;
+ layerSettings.frameNumber = 0;
} else {
- std::optional<renderengine::LayerSettings> shadowLayer =
+ std::optional<LayerFE::LayerSettings> shadowLayer =
layerFE.prepareShadowClientComposition(*result, outputState.viewport,
outputDataspace);
if (shadowLayer) {
@@ -979,13 +1018,12 @@
}
void Output::appendRegionFlashRequests(
- const Region& flashRegion,
- std::vector<renderengine::LayerSettings>& clientCompositionLayers) {
+ const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
if (flashRegion.isEmpty()) {
return;
}
- renderengine::LayerSettings layerSettings;
+ LayerFE::LayerSettings layerSettings;
layerSettings.source.buffer.buffer = nullptr;
layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
layerSettings.alpha = half(1.0);
@@ -1065,6 +1103,7 @@
auto& outputState = editState();
outputState.usesClientComposition = true;
outputState.usesDeviceComposition = false;
+ outputState.reusedClientComposition = false;
}
bool Output::getSkipColorTransform() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 0fcc308..84d79f7 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -27,6 +27,7 @@
dumpVal(out, "usesClientComposition", usesClientComposition);
dumpVal(out, "usesDeviceComposition", usesDeviceComposition);
dumpVal(out, "flipClientTarget", flipClientTarget);
+ dumpVal(out, "reusedClientComposition", reusedClientComposition);
dumpVal(out, "layerStack", layerStackId);
dumpVal(out, "layerStackInternal", layerStackInternal);