diff options
Diffstat (limited to 'services/surfaceflinger/CompositionEngine/src/planner')
6 files changed, 0 insertions, 2047 deletions
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp deleted file mode 100644 index c1cd5ab5fd..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - * 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 -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <android-base/properties.h> -#include <compositionengine/impl/OutputCompositionState.h> -#include <compositionengine/impl/planner/CachedSet.h> -#include <math/HashCombine.h> -#include <renderengine/DisplaySettings.h> -#include <renderengine/RenderEngine.h> -#include <utils/Trace.h> - -#include <utils/Trace.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()), mLastUpdate(lastUpdate) {} - -CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate) - : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) { - addLayer(layer, lastUpdate); -} - -CachedSet::CachedSet(Layer layer) - : mFingerprint(layer.getHash()), - mLastUpdate(layer.getLastUpdate()), - mBounds(layer.getDisplayFrame()), - mVisibleRegion(layer.getVisibleRegion()) { - 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(); - mVisibleRegion.orSelf(layer->getVisibleRegion()); -} - -NonBufferHash CachedSet::getNonBufferHash() const { - if (mLayers.size() == 1) { - return mFingerprint; - } - - // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is - // necessary (and therefore we need to match implementations). - size_t hash = 0; - android::hashCombineSingle(hash, mBounds); - android::hashCombineSingle(hash, mOutputDataspace); - android::hashCombineSingle(hash, mOrientation); - 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() const { - for (const Layer& layer : mLayers) { - if (layer.getFramesSinceBufferUpdate() == 0) { - return true; - } - } - return false; -} - -bool CachedSet::hasReadyBuffer() const { - return mTexture && 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, TexturePool& texturePool, - const OutputCompositionState& outputState) { - ATRACE_CALL(); - const Rect& viewport = outputState.layerStackSpace.content; - const ui::Dataspace& outputDataspace = outputState.dataspace; - const ui::Transform::RotationFlags orientation = - ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation); - - renderengine::DisplaySettings displaySettings{ - .physicalDisplay = outputState.framebufferSpace.content, - .clip = viewport, - .outputDataspace = outputDataspace, - .orientation = orientation, - }; - - Region clearRegion = Region::INVALID_REGION; - LayerFE::ClientCompositionTargetSettings targetSettings{ - .clip = Region(viewport), - .needsFiltering = false, - .isSecure = outputState.isSecure, - .supportsProtectedContent = false, - .clearRegion = clearRegion, - .viewport = viewport, - .dataspace = outputDataspace, - .realContentIsVisible = true, - .clearContent = false, - .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, - }; - - std::vector<renderengine::LayerSettings> layerSettings; - renderengine::LayerSettings highlight; - 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; }); - - renderengine::LayerSettings blurLayerSettings; - if (mBlurLayer) { - auto blurSettings = targetSettings; - blurSettings.blurSetting = - LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly; - auto clientCompositionList = - mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList( - blurSettings); - blurLayerSettings = clientCompositionList.back(); - // This mimics Layer::prepareClearClientComposition - blurLayerSettings.skipContentDraw = true; - blurLayerSettings.name = std::string("blur layer"); - // Clear out the shadow settings - blurLayerSettings.shadow = {}; - layerSettingsPointers.push_back(&blurLayerSettings); - } - - renderengine::LayerSettings holePunchSettings; - renderengine::LayerSettings holePunchBackgroundSettings; - if (mHolePunchLayer) { - auto clientCompositionList = - mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList( - targetSettings); - // Assume that the final layer contains the buffer that we want to - // replace with a hole punch. - holePunchSettings = clientCompositionList.back(); - // This mimics Layer::prepareClearClientComposition - holePunchSettings.source.buffer.buffer = nullptr; - holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f); - holePunchSettings.disableBlending = true; - holePunchSettings.alpha = 0.0f; - holePunchSettings.name = std::string("hole punch layer"); - layerSettingsPointers.push_back(&holePunchSettings); - - // Add a solid background as the first layer in case there is no opaque - // buffer behind the punch hole - holePunchBackgroundSettings.alpha = 1.0f; - holePunchBackgroundSettings.name = std::string("holePunchBackground"); - holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries; - holePunchBackgroundSettings.geometry.positionTransform = - holePunchSettings.geometry.positionTransform; - layerSettingsPointers.insert(layerSettingsPointers.begin(), &holePunchBackgroundSettings); - } - - if (sDebugHighlighLayers) { - 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); - } - - auto texture = texturePool.borrowTexture(); - LOG_ALWAYS_FATAL_IF(texture->get()->getBuffer()->initCheck() != OK); - - 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 = texture; - mTexture->setReadyFence(mDrawFence); - mOutputSpace.orientation = outputState.framebufferSpace.orientation; - mOutputDataspace = outputDataspace; - mOrientation = orientation; - mSkipCount = 0; - } else { - mTexture.reset(); - } -} - -bool CachedSet::requiresHolePunch() const { - // In order for the hole punch to be beneficial, the layer must be updating - // regularly, meaning it should not have been merged with other layers. - if (getLayerCount() != 1) { - return false; - } - - // There is no benefit to a hole punch unless the layer has a buffer. - if (!mLayers[0].getBuffer()) { - return false; - } - - if (hasUnsupportedDataspace()) { - return false; - } - - const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE(); - if (layerFE.getCompositionState()->forceClientComposition) { - return false; - } - - return layerFE.hasRoundedCorners(); -} - -bool CachedSet::hasBlurBehind() const { - return std::any_of(mLayers.cbegin(), mLayers.cend(), - [](const Layer& layer) { return layer.getState()->hasBlurBehind(); }); -} - -namespace { -bool contains(const Rect& outer, const Rect& inner) { - return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top && - outer.bottom >= inner.bottom; -} -}; // namespace - -void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) { - // Verify that this CachedSet is opaque where the hole punch layer - // will draw. - const Rect& holePunchBounds = holePunchLayer.getBounds(); - for (const auto& layer : mLayers) { - // The first layer is considered opaque because nothing is behind it. - // Note that isOpaque is always false for a layer with rounded - // corners, even if the interior is opaque. In theory, such a layer - // could be used for a hole punch, but this is unlikely to happen in - // practice. - const auto* outputLayer = layer.getState()->getOutputLayer(); - if (contains(outputLayer->getState().displayFrame, holePunchBounds) && - (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) { - mHolePunchLayer = holePunchLayer.getFirstLayer().getState(); - return; - } - } -} - -void CachedSet::addBackgroundBlurLayer(const CachedSet& blurLayer) { - mBlurLayer = blurLayer.getFirstLayer().getState(); -} - -compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const { - return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr; -} - -compositionengine::OutputLayer* CachedSet::getBlurLayer() const { - return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr; -} - -bool CachedSet::hasUnsupportedDataspace() const { - return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) { - auto dataspace = layer.getState()->getDataspace(); - const auto transfer = static_cast<ui::Dataspace>(dataspace & ui::Dataspace::TRANSFER_MASK); - if (transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG) { - // Skip HDR. - return true; - } - - if ((dataspace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT601_625) { - // RenderEngine does not match some DPUs, so skip - // to avoid flickering/color differences. - return true; - } - return false; - }); -} - -bool CachedSet::hasProtectedLayers() const { - return std::any_of(mLayers.cbegin(), mLayers.cend(), - [](const Layer& layer) { return layer.getState()->isProtected(); }); -} - -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); - { - const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr; - base::StringAppendF(&result, " Override buffer: %p\n", b); - } - base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer); - - if (mLayers.size() == 1) { - base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str()); - base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get()); - base::StringAppendF(&result, " Protected [%s]", - mLayers[0].getState()->isProtected() ? "true" : "false"); - } 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 Protected [%s]", - layer.getState()->isProtected() ? "true" : "false"); - } - } - - 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 deleted file mode 100644 index f033279caa..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ /dev/null @@ -1,545 +0,0 @@ -/* - * 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 -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <android-base/properties.h> -#include <compositionengine/impl/planner/Flattener.h> -#include <compositionengine/impl/planner/LayerState.h> - -#include <gui/TraceUtils.h> - -using time_point = std::chrono::steady_clock::time_point; -using namespace std::chrono_literals; - -namespace android::compositionengine::impl::planner { - -namespace { - -// True if the underlying layer stack is the same modulo state that would be expected to be -// different like specific buffers, false otherwise. -bool isSameStack(const std::vector<const LayerState*>& incomingLayers, - const std::vector<CachedSet>& cachedSets) { - std::vector<const LayerState*> existingLayers; - for (auto& cachedSet : cachedSets) { - for (auto& layer : cachedSet.getConstituentLayers()) { - existingLayers.push_back(layer.getState()); - } - } - - if (incomingLayers.size() != existingLayers.size()) { - return false; - } - - for (size_t i = 0; i < incomingLayers.size(); i++) { - // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try - // to access destroyed OutputLayers later on. - if (incomingLayers[i]->getId() != existingLayers[i]->getId() || - incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) { - return false; - } - } - return true; -} - -} // namespace - -Flattener::Flattener( - renderengine::RenderEngine& renderEngine, bool enableHolePunch, - std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables) - : mRenderEngine(renderEngine), - mEnableHolePunch(enableHolePunch), - mCachedSetRenderSchedulingTunables(cachedSetRenderSchedulingTunables), - mTexturePool(mRenderEngine) { - const int timeoutInMs = - base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0); - if (timeoutInMs != 0) { - mActiveLayerTimeout = std::chrono::milliseconds(timeoutInMs); - } -} - -NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers, - NonBufferHash hash, time_point now) { - ATRACE_CALL(); - const size_t unflattenedDisplayCost = calculateDisplayCost(layers); - mUnflattenedDisplayCost += unflattenedDisplayCost; - - // We invalidate the layer cache if: - // 1. We're not tracking any layers, or - // 2. The last seen hashed geometry changed between frames, or - // 3. A stricter equality check demonstrates that the layer stack really did change, since the - // hashed geometry does not guarantee uniqueness. - if (mCurrentGeometry != hash || (!mLayers.empty() && !isSameStack(layers, mLayers))) { - resetActivities(hash, now); - mFlattenedDisplayCost += unflattenedDisplayCost; - return hash; - } - - ++mInitialLayerCounts[layers.size()]; - - // Only buildCachedSets if these layers are already stored in mLayers. - // Otherwise (i.e. mergeWithCachedSets returns false), the time has not - // changed, so buildCachedSets will never find any runs. - const bool alreadyHadCachedSets = mergeWithCachedSets(layers, now); - - ++mFinalLayerCounts[mLayers.size()]; - - if (alreadyHadCachedSets) { - buildCachedSets(now); - hash = computeLayersHash(); - } - - return hash; -} - -void Flattener::renderCachedSets( - const OutputCompositionState& outputState, - std::optional<std::chrono::steady_clock::time_point> renderDeadline) { - ATRACE_CALL(); - - if (!mNewCachedSet) { - return; - } - - // Ensure that a cached set has a valid buffer first - if (mNewCachedSet->hasRenderedBuffer()) { - ATRACE_NAME("mNewCachedSet->hasRenderedBuffer()"); - return; - } - - const auto now = std::chrono::steady_clock::now(); - - // If we have a render deadline, and the flattener is configured to skip rendering if we don't - // have enough time, then we skip rendering the cached set if we think that we'll steal too much - // time from the next frame. - if (renderDeadline && mCachedSetRenderSchedulingTunables) { - if (const auto estimatedRenderFinish = - now + mCachedSetRenderSchedulingTunables->cachedSetRenderDuration; - estimatedRenderFinish > *renderDeadline) { - mNewCachedSet->incrementSkipCount(); - - if (mNewCachedSet->getSkipCount() <= - mCachedSetRenderSchedulingTunables->maxDeferRenderAttempts) { - ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us", - std::chrono::duration_cast<std::chrono::microseconds>( - estimatedRenderFinish - *renderDeadline) - .count()); - return; - } else { - ATRACE_NAME("DeadlinePassed: exceeded max skips"); - } - } - } - - mNewCachedSet->render(mRenderEngine, mTexturePool, outputState); -} - -void Flattener::dumpLayers(std::string& result) const { - result.append(" Current layers:"); - for (const CachedSet& layer : mLayers) { - result.append("\n"); - layer.dump(result); - } -} - -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 = mInitialLayerCounts.empty() - ? 0u - : 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()); - - dumpLayers(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; - } -} - -NonBufferHash Flattener::computeLayersHash() const{ - size_t hash = 0; - for (const auto& layer : mLayers) { - android::hashCombineSingleHashed(hash, layer.getNonBufferHash()); - } - return hash; -} - -// Only called if the geometry matches the last frame. Return true if mLayers -// was already populated with these layers, i.e. on the second and following -// calls with the same geometry. -bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) { - ATRACE_CALL(); - 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; - } - - // the compiler should strip out the following no-op loops when ALOGV is off - 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) { - const auto dumper = [&] { - std::string dump; - layer.dump(dump); - return dump; - }; - ALOGV("%s", dumper().c_str()); - } - - auto currentLayerIter = mLayers.begin(); - auto incomingLayerIter = layers.begin(); - - // If not null, this represents the layer that is blurring the layer before - // currentLayerIter. The blurring was stored in the override buffer, so the - // layer that requests the blur no longer needs to do any blurring. - compositionengine::OutputLayer* priorBlurLayer = nullptr; - - while (incomingLayerIter != layers.end()) { - if (mNewCachedSet && - mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) { - if (mNewCachedSet->hasBufferUpdate()) { - 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) { - auto* peekThroughLayer = mNewCachedSet->getHolePunchLayer(); - const size_t layerCount = currentLayerIter->getLayerCount(); - for (size_t i = 0; i < layerCount; ++i) { - bool disableBlur = priorBlurLayer && - priorBlurLayer == (*incomingLayerIter)->getOutputLayer(); - OutputLayer::CompositionState& state = - (*incomingLayerIter)->getOutputLayer()->editState(); - state.overrideInfo = { - .buffer = mNewCachedSet->getBuffer(), - .acquireFence = mNewCachedSet->getDrawFence(), - .displayFrame = mNewCachedSet->getTextureBounds(), - .dataspace = mNewCachedSet->getOutputDataspace(), - .displaySpace = mNewCachedSet->getOutputSpace(), - .damageRegion = Region::INVALID_REGION, - .visibleRegion = mNewCachedSet->getVisibleRegion(), - .peekThroughLayer = peekThroughLayer, - .disableBackgroundBlur = disableBlur, - }; - ++incomingLayerIter; - } - - if (currentLayerIter->getLayerCount() > 1) { - ++mInvalidatedCachedSetAges[currentLayerIter->getAge()]; - } - ++currentLayerIter; - - skipCount -= layerCount; - } - priorBlurLayer = mNewCachedSet->getBlurLayer(); - merged.emplace_back(std::move(*mNewCachedSet)); - mNewCachedSet = std::nullopt; - continue; - } - } - - if (!currentLayerIter->hasBufferUpdate()) { - currentLayerIter->incrementAge(); - merged.emplace_back(*currentLayerIter); - - // Skip the incoming layers corresponding to this valid current layer - const size_t layerCount = currentLayerIter->getLayerCount(); - auto* peekThroughLayer = currentLayerIter->getHolePunchLayer(); - for (size_t i = 0; i < layerCount; ++i) { - bool disableBlur = - priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer(); - OutputLayer::CompositionState& state = - (*incomingLayerIter)->getOutputLayer()->editState(); - state.overrideInfo = { - .buffer = currentLayerIter->getBuffer(), - .acquireFence = currentLayerIter->getDrawFence(), - .displayFrame = currentLayerIter->getTextureBounds(), - .dataspace = currentLayerIter->getOutputDataspace(), - .displaySpace = currentLayerIter->getOutputSpace(), - .damageRegion = Region(), - .visibleRegion = currentLayerIter->getVisibleRegion(), - .peekThroughLayer = peekThroughLayer, - .disableBackgroundBlur = disableBlur, - }; - ++incomingLayerIter; - } - } else if (currentLayerIter->getLayerCount() > 1) { - // Break the current layer into its constituent layers - ++mInvalidatedCachedSetAges[currentLayerIter->getAge()]; - for (CachedSet& layer : currentLayerIter->decompose()) { - bool disableBlur = - priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer(); - OutputLayer::CompositionState& state = - (*incomingLayerIter)->getOutputLayer()->editState(); - state.overrideInfo.disableBackgroundBlur = disableBlur; - layer.updateAge(now); - merged.emplace_back(layer); - ++incomingLayerIter; - } - } else { - bool disableBlur = - priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer(); - OutputLayer::CompositionState& state = - (*incomingLayerIter)->getOutputLayer()->editState(); - state.overrideInfo.disableBackgroundBlur = disableBlur; - currentLayerIter->updateAge(now); - merged.emplace_back(*currentLayerIter); - ++incomingLayerIter; - } - priorBlurLayer = currentLayerIter->getBlurLayer(); - ++currentLayerIter; - } - - for (const CachedSet& layer : merged) { - mFlattenedDisplayCost += layer.getDisplayCost(); - } - - mLayers = std::move(merged); - return true; -} - -std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { - ATRACE_CALL(); - std::vector<Run> runs; - bool isPartOfRun = false; - Run::Builder builder; - bool firstLayer = true; - bool runHasFirstLayer = false; - - for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) { - const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout; - const bool layerHasBlur = currentSet->hasBlurBehind(); - if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) && - !currentSet->hasUnsupportedDataspace()) { - if (isPartOfRun) { - builder.append(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 { - builder.init(currentSet); - if (firstLayer) { - runHasFirstLayer = true; - } - isPartOfRun = true; - } - } - } else if (isPartOfRun) { - builder.setHolePunchCandidate(&(*currentSet)); - - // If we're here then this blur layer recently had an active buffer updating, meaning - // that there is exactly one layer. Blur radius currently is part of layer stack - // geometry, so we're also guaranteed that the background blur radius hasn't changed for - // at least as long as this new inactive cached set. - if (runHasFirstLayer && layerHasBlur && - currentSet->getFirstLayer().getBackgroundBlurRadius() > 0) { - builder.setBlurringLayer(&(*currentSet)); - } - if (auto run = builder.validateAndBuild(); run) { - runs.push_back(*run); - } - - runHasFirstLayer = false; - builder.reset(); - isPartOfRun = false; - } - - firstLayer = false; - } - - // If we're in the middle of a run at the end, we still need to validate and build it. - if (isPartOfRun) { - if (auto run = builder.validateAndBuild(); run) { - runs.push_back(*run); - } - } - - ALOGV("[%s] Found %zu candidate runs", __func__, runs.size()); - - return runs; -} - -std::optional<Flattener::Run> Flattener::findBestRun(std::vector<Flattener::Run>& runs) const { - if (runs.empty()) { - return std::nullopt; - } - - // TODO (b/181192467): Choose the best run, instead of just the first. - return runs[0]; -} - -void Flattener::buildCachedSets(time_point now) { - ATRACE_CALL(); - if (mLayers.empty()) { - ALOGV("[%s] No layers found, returning", __func__); - return; - } - - // Don't try to build a new cached set if we already have a new one in progress - if (mNewCachedSet) { - return; - } - - for (const CachedSet& layer : mLayers) { - // TODO (b/191997217): make it less aggressive, and sync with findCandidateRuns - if (layer.hasProtectedLayers()) { - ATRACE_NAME("layer->hasProtectedLayers()"); - return; - } - } - - std::vector<Run> runs = findCandidateRuns(now); - - std::optional<Run> bestRun = findBestRun(runs); - - if (!bestRun) { - return; - } - - mNewCachedSet.emplace(*bestRun->getStart()); - mNewCachedSet->setLastUpdate(now); - auto currentSet = bestRun->getStart(); - while (mNewCachedSet->getLayerCount() < bestRun->getLayerLength()) { - ++currentSet; - mNewCachedSet->append(*currentSet); - } - - if (bestRun->getBlurringLayer()) { - mNewCachedSet->addBackgroundBlurLayer(*bestRun->getBlurringLayer()); - } - - if (mEnableHolePunch && bestRun->getHolePunchCandidate() && - bestRun->getHolePunchCandidate()->requiresHolePunch()) { - // Add the pip layer to mNewCachedSet, but in a special way - it should - // replace the buffer with a clear round rect. - mNewCachedSet->addHolePunchLayerIfFeasible(*bestRun->getHolePunchCandidate(), - bestRun->getStart() == mLayers.cbegin()); - } - - // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run - // and feedback into the predictor - - ++mCachedSetCreationCount; - mCachedSetCreationCost += mNewCachedSet->getCreationCost(); - - // note the compiler should strip the follow no-op statements when ALOGV is off - const auto dumper = [&] { - std::string setDump; - mNewCachedSet->dump(setDump); - return setDump; - }; - ALOGV("[%s] Added new cached set:\n%s", __func__, dumper().c_str()); -} - -} // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp deleted file mode 100644 index 936dba3b29..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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. - */ - -#include <compositionengine/impl/planner/LayerState.h> - -namespace { -extern "C" const char* __attribute__((unused)) __asan_default_options() { - return "detect_container_overflow=0"; -} -} // namespace - -namespace android::compositionengine::impl::planner { - -LayerState::LayerState(compositionengine::OutputLayer* layer) - : mOutputLayer(layer), - mColorTransform({[](auto layer) { - const auto state = layer->getLayerFE().getCompositionState(); - return state->colorTransformIsIdentity ? mat4{} - : state->colorTransform; - }, - [](const mat4& mat) { - using namespace std::string_literals; - std::vector<std::string> split = - base::Split(std::string(mat.asString().string()), "\n"s); - split.pop_back(); // Strip the last (empty) line - return split; - }}) { - update(layer); -} - -Flags<LayerStateField> LayerState::update(compositionengine::OutputLayer* layer) { - ALOGE_IF(mOutputLayer != layer && layer->getLayerFE().getSequence() != mId.get(), - "[%s] Expected mOutputLayer ID to never change: %d, %d", __func__, - layer->getLayerFE().getSequence(), mId.get()); - - // It's possible for the OutputLayer pointer to change even when the layer is logically the - // same, i.e., the LayerFE is the same. An example use-case is screen rotation. - mOutputLayer = layer; - - Flags<LayerStateField> differences; - - // Update the unique fields as well, since we have to set them at least - // once from the OutputLayer - differences |= mId.update(layer); - differences |= mName.update(layer); - - for (StateInterface* field : getNonUniqueFields()) { - differences |= field->update(layer); - } - - return differences; -} - -size_t LayerState::getHash() const { - size_t hash = 0; - for (const StateInterface* field : getNonUniqueFields()) { - if (field->getField() == LayerStateField::Buffer) { - continue; - } - android::hashCombineSingleHashed(hash, field->getHash()); - } - - return hash; -} - -Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const { - Flags<LayerStateField> differences; - auto myFields = getNonUniqueFields(); - auto otherFields = other.getNonUniqueFields(); - for (size_t i = 0; i < myFields.size(); ++i) { - if (myFields[i]->getField() == LayerStateField::Buffer) { - continue; - } - - differences |= myFields[i]->getFieldIfDifferent(otherFields[i]); - } - - return differences; -} - -void LayerState::dump(std::string& result) const { - for (const StateInterface* field : getNonUniqueFields()) { - if (auto viewOpt = flag_name(field->getField()); viewOpt) { - base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str()); - } else { - result.append("<UNKNOWN FIELD>:\n"); - } - - bool first = true; - for (const std::string& line : field->toStrings()) { - base::StringAppendF(&result, "%s%s\n", first ? "" : " ", - line.c_str()); - first = false; - } - } - result.append("\n"); -} - -std::optional<std::string> LayerState::compare(const LayerState& other) const { - std::string result; - - const auto& thisFields = getNonUniqueFields(); - const auto& otherFields = other.getNonUniqueFields(); - for (size_t f = 0; f < thisFields.size(); ++f) { - const auto& thisField = thisFields[f]; - const auto& otherField = otherFields[f]; - // Skip comparing buffers - if (thisField->getField() == LayerStateField::Buffer) { - continue; - } - - if (thisField->equals(otherField)) { - continue; - } - - if (auto viewOpt = flag_name(thisField->getField()); viewOpt) { - base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str()); - } else { - result.append("<UNKNOWN FIELD>:\n"); - } - - const auto& thisStrings = thisField->toStrings(); - const auto& otherStrings = otherField->toStrings(); - bool first = true; - for (size_t line = 0; line < std::max(thisStrings.size(), otherStrings.size()); ++line) { - if (!first) { - result.append(" "); - } - first = false; - - if (line < thisStrings.size()) { - base::StringAppendF(&result, "%-48.48s", thisStrings[line].c_str()); - } else { - result.append(" "); - } - - if (line < otherStrings.size()) { - base::StringAppendF(&result, "%-48.48s", otherStrings[line].c_str()); - } else { - result.append(" "); - } - result.append("\n"); - } - } - - return result.empty() ? std::nullopt : std::make_optional(result); -} - -bool operator==(const LayerState& lhs, const LayerState& rhs) { - return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame && - lhs.mSourceCrop == rhs.mSourceCrop && lhs.mBufferTransform == rhs.mBufferTransform && - lhs.mBlendMode == rhs.mBlendMode && lhs.mAlpha == rhs.mAlpha && - lhs.mLayerMetadata == rhs.mLayerMetadata && lhs.mVisibleRegion == rhs.mVisibleRegion && - lhs.mOutputDataspace == rhs.mOutputDataspace && lhs.mPixelFormat == rhs.mPixelFormat && - lhs.mColorTransform == rhs.mColorTransform && - lhs.mCompositionType == rhs.mCompositionType && - lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer && - (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR || - lhs.mSolidColor == rhs.mSolidColor); -} - -NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) { - size_t hash = 0; - for (const auto layer : layers) { - android::hashCombineSingleHashed(hash, layer->getHash()); - } - - return hash; -} - -} // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp deleted file mode 100644 index f077470c80..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * 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" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <android-base/properties.h> -#include <compositionengine/LayerFECompositionState.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> -#include <compositionengine/impl/planner/Planner.h> - -#include <utils/Trace.h> -#include <chrono> - -namespace android::compositionengine::impl::planner { - -namespace { - -std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() { - if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) { - return std::nullopt; - } - - auto renderDuration = std::chrono::nanoseconds( - base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"), - Flattener::CachedSetRenderSchedulingTunables:: - kDefaultCachedSetRenderDuration.count())); - - auto maxDeferRenderAttempts = base::GetUintProperty< - size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"), - Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts); - - return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>( - Flattener::CachedSetRenderSchedulingTunables{ - .cachedSetRenderDuration = renderDuration, - .maxDeferRenderAttempts = maxDeferRenderAttempts, - }); -} - -} // namespace - -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(renderEngine, - base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true), - buildFlattenerTuneables()) { - mPredictorEnabled = - base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false); -} - -void Planner::setDisplaySize(ui::Size size) { - mFlattener.setDisplaySize(size); -} - -void Planner::plan( - compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) { - ATRACE_CALL(); - std::unordered_set<LayerId> removedLayers; - removedLayers.reserve(mPreviousLayers.size()); - - std::transform(mPreviousLayers.begin(), mPreviousLayers.end(), - std::inserter(removedLayers, removedLayers.begin()), - [](const auto& layer) { return layer.first; }); - - std::vector<LayerId> currentLayerIds; - for (auto layer : layers) { - LayerId id = layer->getLayerFE().getSequence(); - if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) { - // Track changes from previous info - LayerState& state = layerEntry->second; - Flags<LayerStateField> differences = state.update(layer); - if (differences.get() == 0) { - state.incrementFramesSinceBufferUpdate(); - } else { - ALOGV("Layer %s changed: %s", state.getName().c_str(), - differences.string().c_str()); - - if (differences.test(LayerStateField::Buffer)) { - state.resetFramesSinceBufferUpdate(); - } else { - state.incrementFramesSinceBufferUpdate(); - } - } - } else { - LayerState state(layer); - ALOGV("Added layer %s", state.getName().c_str()); - mPreviousLayers.emplace(std::make_pair(id, std::move(state))); - } - - currentLayerIds.emplace_back(id); - - if (const auto found = removedLayers.find(id); found != removedLayers.end()) { - removedLayers.erase(found); - } - } - - mCurrentLayers.clear(); - mCurrentLayers.reserve(currentLayerIds.size()); - std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(), - std::back_inserter(mCurrentLayers), [this](LayerId id) { - LayerState* state = &mPreviousLayers.at(id); - state->getOutputLayer()->editState().overrideInfo = {}; - return state; - }); - - const NonBufferHash hash = getNonBufferHash(mCurrentLayers); - mFlattenedHash = - mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now()); - const bool layersWereFlattened = hash != mFlattenedHash; - - ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash); - - if (mPredictorEnabled) { - 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 { - ALOGV("[%s] No prediction found\n", __func__); - } - } - - // Clean up the set of previous layers now that the view of the LayerStates in the flattener are - // up-to-date. - for (LayerId removedLayer : removedLayers) { - if (const auto layerEntry = mPreviousLayers.find(removedLayer); - layerEntry != mPreviousLayers.end()) { - const auto& [id, state] = *layerEntry; - ALOGV("Removed layer %s", state.getName().c_str()); - mPreviousLayers.erase(removedLayer); - } - } -} - -void Planner::reportFinalPlan( - compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) { - ATRACE_CALL(); - if (!mPredictorEnabled) { - return; - } - - Plan finalPlan; - const GraphicBuffer* currentOverrideBuffer = nullptr; - bool hasSkippedLayers = false; - for (auto layer : layers) { - if (!layer->getState().overrideInfo.buffer) { - continue; - } - - const GraphicBuffer* overrideBuffer = - layer->getState().overrideInfo.buffer->getBuffer().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(); - - finalPlan.addLayerType( - forcedOrRequestedClient - ? hardware::graphics::composer::hal::Composition::CLIENT - : layer->getLayerFE().getCompositionState()->compositionType); - } - - mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers, - finalPlan); -} - -void Planner::renderCachedSets( - const OutputCompositionState& outputState, - std::optional<std::chrono::steady_clock::time_point> renderDeadline) { - ATRACE_CALL(); - mFlattener.renderCachedSets(outputState, renderDeadline); -} - -void Planner::dump(const Vector<String16>& args, std::string& result) { - if (args.size() > 1) { - const String8 command(args[1]); - if (command == "--compare" || command == "-c") { - if (args.size() < 4) { - base::StringAppendF(&result, - "Expected two layer stack hashes, e.g. '--planner %s " - "<left_hash> <right_hash>'\n", - command.string()); - return; - } - if (args.size() > 4) { - base::StringAppendF(&result, - "Too many arguments found, expected '--planner %s <left_hash> " - "<right_hash>'\n", - command.string()); - return; - } - - const String8 leftHashString(args[2]); - size_t leftHash = 0; - int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash); - if (fieldsRead != 1) { - base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - leftHashString.string()); - return; - } - - const String8 rightHashString(args[3]); - size_t rightHash = 0; - fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash); - if (fieldsRead != 1) { - base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - rightHashString.string()); - return; - } - - if (mPredictorEnabled) { - mPredictor.compareLayerStacks(leftHash, rightHash, result); - } - } else if (command == "--describe" || command == "-d") { - if (args.size() < 3) { - base::StringAppendF(&result, - "Expected a layer stack hash, e.g. '--planner %s <hash>'\n", - command.string()); - return; - } - if (args.size() > 3) { - base::StringAppendF(&result, - "Too many arguments found, expected '--planner %s <hash>'\n", - command.string()); - return; - } - - const String8 hashString(args[2]); - size_t hash = 0; - const int fieldsRead = sscanf(hashString.string(), "%zx", &hash); - if (fieldsRead != 1) { - base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - hashString.string()); - return; - } - - if (mPredictorEnabled) { - mPredictor.describeLayerStack(hash, result); - } - } else if (command == "--help" || command == "-h") { - dumpUsage(result); - } else if (command == "--similar" || command == "-s") { - if (args.size() < 3) { - base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n", - command.string()); - return; - } - if (args.size() > 3) { - base::StringAppendF(&result, - "Too many arguments found, expected '--planner %s <plan>'\n", - command.string()); - return; - } - - const String8 planString(args[2]); - std::optional<Plan> plan = Plan::fromString(std::string(planString.string())); - if (!plan) { - base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string()); - return; - } - - if (mPredictorEnabled) { - mPredictor.listSimilarStacks(*plan, result); - } - } else if (command == "--layers" || command == "-l") { - mFlattener.dumpLayers(result); - } else { - base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string()); - dumpUsage(result); - } - return; - } - - // If there are no specific commands, dump the usual state - - mFlattener.dump(result); - result.append("\n"); - - if (mPredictorEnabled) { - mPredictor.dump(result); - } -} - -void Planner::dumpUsage(std::string& result) const { - result.append("Planner command line interface usage\n"); - result.append(" dumpsys SurfaceFlinger --planner <command> [arguments]\n\n"); - - result.append("If run without a command, dumps current Planner state\n\n"); - - result.append("Commands:\n"); - - result.append("[--compare|-c] <left_hash> <right_hash>\n"); - result.append(" Compares the predictions <left_hash> and <right_hash> by showing differences" - " in their example layer stacks\n"); - - result.append("[--describe|-d] <hash>\n"); - result.append(" Prints the example layer stack and prediction statistics for <hash>\n"); - - result.append("[--help|-h]\n"); - result.append(" Shows this message\n"); - - result.append("[--similar|-s] <plan>\n"); - result.append(" Prints the example layer names for similar stacks matching <plan>\n"); - - result.append("[--layers|-l]\n"); - result.append(" Prints the current layers\n"); -} - -} // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp deleted file mode 100644 index 8226ef7b4c..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp +++ /dev/null @@ -1,481 +0,0 @@ -/* - * 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/Predictor.h> - -namespace android::compositionengine::impl::planner { - -std::optional<LayerStack::ApproximateMatch> LayerStack::getApproximateMatch( - const std::vector<const LayerState*>& other) const { - // Differing numbers of layers are never an approximate match - if (mLayers.size() != other.size()) { - return std::nullopt; - } - - std::optional<ApproximateMatch> approximateMatch = {}; - for (size_t i = 0; i < mLayers.size(); ++i) { - // Skip identical layers - if (mLayers[i].getHash() == other[i]->getHash()) { - continue; - } - - // Skip layers where both are client-composited, since that doesn't change the - // composition plan - if (mLayers[i].getCompositionType() == hal::Composition::CLIENT && - other[i]->getCompositionType() == hal::Composition::CLIENT) { - continue; - } - - // If layers differ in composition type, their stacks are too different - if (mLayers[i].getCompositionType() != other[i]->getCompositionType()) { - return std::nullopt; - } - - // If layers are not identical, but we already detected a prior approximate match for a - // previous layer, the LayerStacks differ by too much, so return nothing - if (approximateMatch) { - return std::nullopt; - } - - Flags<LayerStateField> differingFields = mLayers[i].getDifferingFields(*other[i]); - - // If we don't find an approximate match on this layer, then the LayerStacks differ - // by too much, so return nothing - const int differingFieldCount = __builtin_popcount(differingFields.get()); - if (differingFieldCount <= kMaxDifferingFields) { - approximateMatch = ApproximateMatch{ - .differingIndex = i, - .differingFields = differingFields, - }; - } else { - return std::nullopt; - } - } - - if (approximateMatch) { - return approximateMatch; - } - - // If we make it through the layer-by-layer comparison without an approximate match, - // it means that all layers were either identical or had client-composited layers in common, - // which don't affect the composition strategy, so return a successful result with - // no differences. - return ApproximateMatch{ - .differingIndex = 0, - .differingFields = {}, - }; -} - -std::optional<Plan> Plan::fromString(const std::string& string) { - Plan plan; - for (char c : string) { - switch (c) { - case 'C': - plan.addLayerType(hal::Composition::CLIENT); - continue; - case 'U': - plan.addLayerType(hal::Composition::CURSOR); - continue; - case 'D': - plan.addLayerType(hal::Composition::DEVICE); - continue; - case 'I': - plan.addLayerType(hal::Composition::INVALID); - continue; - case 'B': - plan.addLayerType(hal::Composition::SIDEBAND); - continue; - case 'S': - plan.addLayerType(hal::Composition::SOLID_COLOR); - continue; - default: - return std::nullopt; - } - } - return plan; -} - -std::string to_string(const Plan& plan) { - std::string result; - for (auto type : plan.mLayerTypes) { - switch (type) { - case hal::Composition::CLIENT: - result.append("C"); - break; - case hal::Composition::CURSOR: - result.append("U"); - break; - case hal::Composition::DEVICE: - result.append("D"); - break; - case hal::Composition::INVALID: - result.append("I"); - break; - case hal::Composition::SIDEBAND: - result.append("B"); - break; - case hal::Composition::SOLID_COLOR: - result.append("S"); - break; - } - } - return result; -} - -void Prediction::dump(std::string& result) const { - result.append(to_string(mPlan)); - result.append(" [Exact "); - mExactStats.dump(result); - result.append("] [Approximate "); - mApproximateStats.dump(result); - result.append("]"); -} - -std::optional<Predictor::PredictedPlan> Predictor::getPredictedPlan( - const std::vector<const LayerState*>& layers, NonBufferHash hash) const { - // First check for an exact match - if (std::optional<Plan> exactMatch = getExactMatch(hash); exactMatch) { - ALOGV("[%s] Found an exact match for %zx", __func__, hash); - return PredictedPlan{.hash = hash, .plan = *exactMatch, .type = Prediction::Type::Exact}; - } - - // If only a hash was passed in for a layer stack with a cached set, don't perform - // approximate matches and return early - if (layers.empty()) { - ALOGV("[%s] Only hash was passed, but no exact match was found", __func__); - return std::nullopt; - } - - // Then check for approximate matches - if (std::optional<NonBufferHash> approximateMatch = getApproximateMatch(layers); - approximateMatch) { - ALOGV("[%s] Found an approximate match for %zx", __func__, *approximateMatch); - const Prediction& prediction = getPrediction(*approximateMatch); - return PredictedPlan{.hash = *approximateMatch, - .plan = prediction.getPlan(), - .type = Prediction::Type::Approximate}; - } - - return std::nullopt; -} - -void Predictor::recordResult(std::optional<PredictedPlan> predictedPlan, - NonBufferHash flattenedHash, - const std::vector<const LayerState*>& layers, bool hasSkippedLayers, - Plan result) { - if (predictedPlan) { - recordPredictedResult(*predictedPlan, layers, std::move(result)); - return; - } - - ++mMissCount; - - if (!hasSkippedLayers && findSimilarPrediction(layers, result)) { - return; - } - - ALOGV("[%s] Adding novel candidate %zx", __func__, flattenedHash); - mCandidates.emplace_front(flattenedHash, Prediction(layers, result)); - if (mCandidates.size() > MAX_CANDIDATES) { - mCandidates.pop_back(); - } -} - -void Predictor::dump(std::string& result) const { - result.append("Predictor state:\n"); - - const size_t hitCount = mExactHitCount + mApproximateHitCount; - const size_t totalAttempts = hitCount + mMissCount; - base::StringAppendF(&result, "Global non-skipped hit rate: %.2f%% (%zd/%zd)\n", - 100.0f * hitCount / totalAttempts, hitCount, totalAttempts); - base::StringAppendF(&result, " Exact hits: %zd\n", mExactHitCount); - base::StringAppendF(&result, " Approximate hits: %zd\n", mApproximateHitCount); - base::StringAppendF(&result, " Misses: %zd\n\n", mMissCount); - - dumpPredictionsByFrequency(result); -} - -void Predictor::compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash, - std::string& result) const { - const auto& [leftPredictionEntry, rightPredictionEntry] = - std::make_tuple(mPredictions.find(leftHash), mPredictions.find(rightHash)); - if (leftPredictionEntry == mPredictions.end()) { - base::StringAppendF(&result, "No prediction found for %zx\n", leftHash); - return; - } - if (rightPredictionEntry == mPredictions.end()) { - base::StringAppendF(&result, "No prediction found for %zx\n", rightHash); - return; - } - - base::StringAppendF(&result, - "Comparing %-16zx %-16zx\n", - leftHash, rightHash); - - const auto& [leftPrediction, rightPrediction] = - std::make_tuple(leftPredictionEntry->second, rightPredictionEntry->second); - const auto& [leftStack, rightStack] = std::make_tuple(leftPrediction.getExampleLayerStack(), - rightPrediction.getExampleLayerStack()); - leftStack.compare(rightStack, result); -} - -void Predictor::describeLayerStack(NonBufferHash hash, std::string& result) const { - base::StringAppendF(&result, "Describing %zx:\n\n", hash); - - if (const auto predictionsEntry = mPredictions.find(hash); - predictionsEntry != mPredictions.cend()) { - const auto& [hash, prediction] = *predictionsEntry; - - prediction.getExampleLayerStack().dump(result); - - result.append("Prediction: "); - prediction.dump(result); - result.append("\n"); - } else { - result.append("No predictions found\n"); - } -} - -void Predictor::listSimilarStacks(Plan plan, std::string& result) const { - base::StringAppendF(&result, "Similar stacks for plan %s:\n", to_string(plan).c_str()); - - if (const auto similarStacksEntry = mSimilarStacks.find(plan); - similarStacksEntry != mSimilarStacks.end()) { - const auto& [_, similarStacks] = *similarStacksEntry; - for (NonBufferHash hash : similarStacks) { - base::StringAppendF(&result, "\nPrediction hash %zx:\n", hash); - const Prediction& prediction = mPredictions.at(hash); - prediction.getExampleLayerStack().dumpLayerNames(result); - } - } else { - result.append("No similar stacks found\n"); - } -} - -const Prediction& Predictor::getPrediction(NonBufferHash hash) const { - if (const auto predictionEntry = mPredictions.find(hash); - predictionEntry != mPredictions.end()) { - const auto& [_, prediction] = *predictionEntry; - return prediction; - } else { - const auto candidateEntry = getCandidateEntryByHash(hash); - ALOGE_IF(candidateEntry == mCandidates.cend(), - "Hash should have been found in either predictions or candidates"); - const auto& [_, prediction] = *candidateEntry; - return prediction; - } -} - -Prediction& Predictor::getPrediction(NonBufferHash hash) { - return const_cast<Prediction&>(const_cast<const Predictor*>(this)->getPrediction(hash)); -} - -std::optional<Plan> Predictor::getExactMatch(NonBufferHash hash) const { - const Prediction* match = nullptr; - if (const auto predictionEntry = mPredictions.find(hash); - predictionEntry != mPredictions.end()) { - const auto& [hash, prediction] = *predictionEntry; - match = &prediction; - } else if (const auto candidateEntry = getCandidateEntryByHash(hash); - candidateEntry != mCandidates.cend()) { - match = &(candidateEntry->prediction); - } - - if (match == nullptr) { - return std::nullopt; - } - - if (match->getMissCount(Prediction::Type::Exact) != 0) { - ALOGV("[%s] Skipping exact match for %zx because of prior miss", __func__, hash); - return std::nullopt; - } - - return match->getPlan(); -} - -std::optional<NonBufferHash> Predictor::getApproximateMatch( - const std::vector<const LayerState*>& layers) const { - const auto approximateStackMatches = [&](const ApproximateStack& approximateStack) { - const auto& exampleStack = mPredictions.at(approximateStack.hash).getExampleLayerStack(); - if (const auto approximateMatchOpt = exampleStack.getApproximateMatch(layers); - approximateMatchOpt) { - return *approximateMatchOpt == approximateStack.match; - } - return false; - }; - - const auto candidateMatches = [&](const PromotionCandidate& candidate) { - ALOGV("[getApproximateMatch] checking against %zx", candidate.hash); - return candidate.prediction.getExampleLayerStack().getApproximateMatch(layers) != - std::nullopt; - }; - - const Prediction* match = nullptr; - NonBufferHash hash; - if (const auto approximateStackIter = - std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(), - approximateStackMatches); - approximateStackIter != mApproximateStacks.cend()) { - match = &mPredictions.at(approximateStackIter->hash); - hash = approximateStackIter->hash; - } else if (const auto candidateEntry = - std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches); - candidateEntry != mCandidates.cend()) { - match = &(candidateEntry->prediction); - hash = candidateEntry->hash; - } - - if (match == nullptr) { - return std::nullopt; - } - - if (match->getMissCount(Prediction::Type::Approximate) != 0) { - ALOGV("[%s] Skipping approximate match for %zx because of prior miss", __func__, hash); - return std::nullopt; - } - - return hash; -} - -void Predictor::promoteIfCandidate(NonBufferHash predictionHash) { - // Return if the candidate has already been promoted - if (mPredictions.count(predictionHash) != 0) { - return; - } - - ALOGV("[%s] Promoting %zx from candidate to prediction", __func__, predictionHash); - - auto candidateEntry = getCandidateEntryByHash(predictionHash); - ALOGE_IF(candidateEntry == mCandidates.end(), "Expected to find candidate"); - - mSimilarStacks[candidateEntry->prediction.getPlan()].push_back(predictionHash); - mPredictions.emplace(predictionHash, std::move(candidateEntry->prediction)); - mCandidates.erase(candidateEntry); -} - -void Predictor::recordPredictedResult(PredictedPlan predictedPlan, - const std::vector<const LayerState*>& layers, Plan result) { - Prediction& prediction = getPrediction(predictedPlan.hash); - if (prediction.getPlan() != result) { - ALOGV("[%s] %s prediction missed, expected %s, found %s", __func__, - to_string(predictedPlan.type).c_str(), to_string(prediction.getPlan()).c_str(), - to_string(result).c_str()); - prediction.recordMiss(predictedPlan.type); - ++mMissCount; - return; - } - - switch (predictedPlan.type) { - case Prediction::Type::Approximate: - ++mApproximateHitCount; - break; - case Prediction::Type::Exact: - ++mExactHitCount; - break; - default: - break; - } - - ALOGV("[%s] %s prediction hit", __func__, to_string(predictedPlan.type).c_str()); - ALOGV("[%s] Plan: %s", __func__, to_string(result).c_str()); - prediction.recordHit(predictedPlan.type); - - const auto stackMatchesHash = [hash = predictedPlan.hash](const ApproximateStack& stack) { - return stack.hash == hash; - }; - - if (predictedPlan.type == Prediction::Type::Approximate) { - // If this approximate match is not already in the list of approximate stacks, add it - if (std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(), - stackMatchesHash) == mApproximateStacks.cend()) { - ALOGV("[%s] Adding approximate match to list", __func__); - const auto approximateMatchOpt = - prediction.getExampleLayerStack().getApproximateMatch(layers); - ALOGE_IF(!approximateMatchOpt, "Expected an approximate match"); - mApproximateStacks.emplace_back(predictedPlan.hash, *approximateMatchOpt); - } - } - - promoteIfCandidate(predictedPlan.hash); -} - -bool Predictor::findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result) { - const auto stacksEntry = mSimilarStacks.find(result); - if (stacksEntry == mSimilarStacks.end()) { - return false; - } - - std::optional<ApproximateStack> bestMatch; - const auto& [plan, similarStacks] = *stacksEntry; - for (NonBufferHash hash : similarStacks) { - const Prediction& prediction = mPredictions.at(hash); - auto approximateMatch = prediction.getExampleLayerStack().getApproximateMatch(layers); - if (!approximateMatch) { - continue; - } - - const int differingFieldCount = __builtin_popcount(approximateMatch->differingFields.get()); - if (!bestMatch || - differingFieldCount < __builtin_popcount(bestMatch->match.differingFields.get())) { - bestMatch = {hash, *approximateMatch}; - } - } - - if (!bestMatch) { - return false; - } - - ALOGV("[%s] Adding %zx to approximate stacks", __func__, bestMatch->hash); - - mApproximateStacks.emplace_back(*bestMatch); - return true; -} - -void Predictor::dumpPredictionsByFrequency(std::string& result) const { - struct HashFrequency { - HashFrequency(NonBufferHash hash, size_t totalAttempts) - : hash(hash), totalAttempts(totalAttempts) {} - - NonBufferHash hash; - size_t totalAttempts; - }; - - std::vector<HashFrequency> hashFrequencies; - for (const auto& [hash, prediction] : mPredictions) { - hashFrequencies.emplace_back(hash, - prediction.getHitCount(Prediction::Type::Total) + - prediction.getMissCount(Prediction::Type::Total)); - } - - std::sort(hashFrequencies.begin(), hashFrequencies.end(), - [](const HashFrequency& lhs, const HashFrequency& rhs) { - return lhs.totalAttempts > rhs.totalAttempts; - }); - - result.append("Predictions:\n"); - for (const auto& [hash, totalAttempts] : hashFrequencies) { - base::StringAppendF(&result, " %016zx ", hash); - mPredictions.at(hash).dump(result); - result.append("\n"); - } -} - -} // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp deleted file mode 100644 index e3772a22d2..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 |