Add a TexturePool class into layer caching
In order to reduce the risk of buffer allocations burning cycles during
rendering, add a texture pool which preallocates a set of screen-sized
buffers available for use for layer caching.
This texture pool has some properties to ease implementation details:
1. Textures are allocated synchronously. It's not very hard to add
asynchronous support, but that does add a bit of complexity and Android
12 is almost finalized. It's also not yet clear if asynchronous
allocation is needed since we can just tune the min texture pool size so
that more buffers are preallocated; although it is certainly more
flexible if we're able to defer allocations off-thread.
2. The texture pool is soft-bounded. If needed additional textures may
be allocated to allow for complex geometries, but beyond an upper bound
any additional textures may be deallocated.
3. The texture pool only supports screen-sized buffers, so we don't
allocate smaller scratch buffers to save on memory. However, typically
layer caching only caches screen-sized buffers. This also means that if
the display size changes, then the texture pool must be reallocated.
Note that the "display size" really refers to the framebuffer size, not
necessarily the app-visible display.
Bug: 184860700
Test: builds, boots
Test: libcompositionengine_test
Change-Id: Ic084310bae2b41be49c2cfe9f8c5a20ff683d1ad
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e9a8b91..cd2f742 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -129,7 +129,7 @@
}
if (enabled) {
- mPlanner = std::make_unique<planner::Planner>();
+ mPlanner = std::make_unique<planner::Planner>(getCompositionEngine().getRenderEngine());
if (mRenderSurface) {
mPlanner->setDisplaySize(mRenderSurface->getSize());
}
@@ -1314,7 +1314,7 @@
void Output::renderCachedSets() {
if (mPlanner) {
- mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
+ mPlanner->renderCachedSets(getState());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index dcfb05d..69e8c7d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -134,7 +134,7 @@
}
bool CachedSet::hasReadyBuffer() const {
- return mTexture != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+ return mTexture && mDrawFence->getStatus() == Fence::Status::Signaled;
}
std::vector<CachedSet> CachedSet::decompose() const {
@@ -156,7 +156,7 @@
}
}
-void CachedSet::render(renderengine::RenderEngine& renderEngine,
+void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
const OutputCompositionState& outputState) {
ATRACE_CALL();
const Rect& viewport = outputState.layerStackSpace.content;
@@ -165,10 +165,7 @@
ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
renderengine::DisplaySettings displaySettings{
- .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
- -mBounds.top + outputState.framebufferSpace.content.top,
- -mBounds.left + outputState.framebufferSpace.content.right,
- -mBounds.top + outputState.framebufferSpace.content.bottom),
+ .physicalDisplay = outputState.framebufferSpace.content,
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
@@ -255,30 +252,33 @@
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);
- const auto texture = std::make_shared<
- renderengine::ExternalTexture>(buffer, renderEngine,
- renderengine::ExternalTexture::Usage::READABLE |
- renderengine::ExternalTexture::Usage::WRITEABLE);
- LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
- base::unique_fd drawFence;
+ auto texture = texturePool.borrowTexture();
+ LOG_ALWAYS_FATAL_IF(texture->get()->getBuffer()->initCheck() != OK);
- status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture,
- false, base::unique_fd(), &drawFence);
+ base::unique_fd bufferFence;
+ if (texture->getReadyFence()) {
+ // Bail out if the buffer is not ready, because there is some pending GPU work left.
+ if (texture->getReadyFence()->getStatus() != Fence::Status::Signaled) {
+ return;
+ }
+ bufferFence.reset(texture->getReadyFence()->dup());
+ }
+
+ base::unique_fd drawFence;
+ status_t result =
+ renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false,
+ std::move(bufferFence), &drawFence);
if (result == NO_ERROR) {
mDrawFence = new Fence(drawFence.release());
mOutputSpace = outputState.framebufferSpace;
- mTexture = std::move(texture);
+ mTexture = texture;
+ mTexture->setReadyFence(mDrawFence);
mOutputSpace.orientation = outputState.framebufferSpace.orientation;
mOutputDataspace = outputDataspace;
mOrientation = orientation;
} else {
- mTexture = nullptr;
+ mTexture.reset();
}
}
@@ -363,7 +363,7 @@
base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
mFingerprint, durationString(lastUpdate).c_str(), mAge);
{
- const auto b = mTexture ? mTexture->getBuffer().get() : nullptr;
+ const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
base::StringAppendF(&result, " Override buffer: %p\n", b);
}
base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 48fb51f..192c411 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -60,7 +60,10 @@
} // namespace
-Flattener::Flattener(bool enableHolePunch) : mEnableHolePunch(enableHolePunch) {
+Flattener::Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch)
+ : mRenderEngine(renderEngine),
+ mEnableHolePunch(enableHolePunch),
+ mTexturePool(mRenderEngine) {
const int timeoutInMs =
base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0);
if (timeoutInMs != 0) {
@@ -102,14 +105,13 @@
return hash;
}
-void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
- const OutputCompositionState& outputState) {
+void Flattener::renderCachedSets(const OutputCompositionState& outputState) {
ATRACE_CALL();
if (!mNewCachedSet || mNewCachedSet->hasRenderedBuffer()) {
return;
}
- mNewCachedSet->render(renderEngine, outputState);
+ mNewCachedSet->render(mRenderEngine, mTexturePool, outputState);
}
void Flattener::dumpLayers(std::string& result) const {
@@ -285,7 +287,7 @@
state.overrideInfo = {
.buffer = mNewCachedSet->getBuffer(),
.acquireFence = mNewCachedSet->getDrawFence(),
- .displayFrame = mNewCachedSet->getBounds(),
+ .displayFrame = mNewCachedSet->getTextureBounds(),
.dataspace = mNewCachedSet->getOutputDataspace(),
.displaySpace = mNewCachedSet->getOutputSpace(),
.damageRegion = Region::INVALID_REGION,
@@ -325,7 +327,7 @@
state.overrideInfo = {
.buffer = currentLayerIter->getBuffer(),
.acquireFence = currentLayerIter->getDrawFence(),
- .displayFrame = currentLayerIter->getBounds(),
+ .displayFrame = currentLayerIter->getTextureBounds(),
.dataspace = currentLayerIter->getOutputDataspace(),
.displaySpace = currentLayerIter->getOutputSpace(),
.damageRegion = Region(),
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 297c0b2..711a634 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -29,12 +29,13 @@
namespace android::compositionengine::impl::planner {
-Planner::Planner()
+Planner::Planner(renderengine::RenderEngine& renderEngine)
// Implicitly, layer caching must also be enabled for the hole punch or
// predictor to have any effect.
// E.g., setprop debug.sf.enable_layer_caching 1, or
// adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
- : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) {
+ : mFlattener(renderEngine,
+ base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) {
mPredictorEnabled =
base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
}
@@ -160,10 +161,9 @@
finalPlan);
}
-void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
- const OutputCompositionState& outputState) {
+void Planner::renderCachedSets(const OutputCompositionState& outputState) {
ATRACE_CALL();
- mFlattener.renderCachedSets(renderEngine, outputState);
+ mFlattener.renderCachedSets(outputState);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
new file mode 100644
index 0000000..e3772a2
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/impl/planner/TexturePool.h>
+#include <utils/Log.h>
+
+namespace android::compositionengine::impl::planner {
+
+void TexturePool::setDisplaySize(ui::Size size) {
+ if (mSize == size) {
+ return;
+ }
+ mSize = size;
+ mPool.clear();
+ mPool.resize(kMinPoolSize);
+ std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; });
+}
+
+std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
+ if (mPool.empty()) {
+ return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
+ }
+
+ const auto entry = mPool.front();
+ mPool.pop_front();
+ return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
+}
+
+void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
+ const sp<Fence>& fence) {
+ // Drop the texture on the floor if the pool is no longer tracking textures of the same size.
+ if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
+ static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
+ ALOGV("Deallocating texture from Planner's pool - display size changed (previous: (%dx%d), "
+ "current: (%dx%d))",
+ texture->getBuffer()->getWidth(), texture->getBuffer()->getHeight(), mSize.getWidth(),
+ mSize.getHeight());
+ return;
+ }
+
+ // Also ensure the pool does not grow beyond a maximum size.
+ if (mPool.size() == kMaxPoolSize) {
+ ALOGD("Deallocating texture from Planner's pool - max size [%" PRIu64 "] reached",
+ static_cast<uint64_t>(kMaxPoolSize));
+ return;
+ }
+
+ mPool.push_back({std::move(texture), fence});
+}
+
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
+ LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
+ return std::make_shared<
+ renderengine::ExternalTexture>(sp<GraphicBuffer>::
+ make(mSize.getWidth(), mSize.getHeight(),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GraphicBuffer::USAGE_HW_RENDER |
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ "Planner"),
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+}
+
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file