blob: 52efff5e392377bedbebe4045fe2a028c3c480bb [file] [log] [blame]
/*
* 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/LayerFECompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
namespace android::compositionengine::impl::planner {
void Planner::setDisplaySize(ui::Size size) {
mFlattener.setDisplaySize(size);
}
void Planner::plan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
std::unordered_set<LayerId> removedLayers;
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);
}
}
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);
}
}
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);
const bool layersWereFlattened = hash != mFlattenedHash;
ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
mPredictedPlan =
mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
: mCurrentLayers,
mFlattenedHash);
if (mPredictedPlan) {
ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
} else {
ALOGV("[%s] No prediction found\n", __func__);
}
}
void Planner::reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
Plan finalPlan;
const GraphicBuffer* currentOverrideBuffer = nullptr;
bool hasSkippedLayers = false;
for (auto layer : layers) {
const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.get();
if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
// Skip this layer since it is part of a previous cached set
hasSkippedLayers = true;
continue;
}
currentOverrideBuffer = overrideBuffer;
const bool forcedOrRequestedClient =
layer->getState().forceClientComposition || layer->requiresClientComposition();
finalPlan.addLayerType(
forcedOrRequestedClient
? hardware::graphics::composer::hal::Composition::CLIENT
: layer->getLayerFE().getCompositionState()->compositionType);
}
mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
finalPlan);
}
void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
mFlattener.renderCachedSets(renderEngine);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
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;
}
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;
}
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;
}
mPredictor.listSimilarStacks(*plan, 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");
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");
}
} // namespace android::compositionengine::impl::planner