1 /*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // #define LOG_NDEBUG 0
18
19 #undef LOG_TAG
20 #define LOG_TAG "Planner"
21 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
22
23 #include <android-base/properties.h>
24 #include <compositionengine/LayerFECompositionState.h>
25 #include <compositionengine/impl/OutputLayerCompositionState.h>
26 #include <compositionengine/impl/planner/Planner.h>
27
28 #include <utils/Trace.h>
29 #include <chrono>
30
31 namespace android::compositionengine::impl::planner {
32
33 namespace {
34
buildRenderSchedulingTunables()35 std::optional<Flattener::Tunables::RenderScheduling> buildRenderSchedulingTunables() {
36 if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) {
37 return std::nullopt;
38 }
39
40 const auto renderDuration = std::chrono::nanoseconds(
41 base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"),
42 Flattener::Tunables::RenderScheduling::
43 kDefaultCachedSetRenderDuration.count()));
44
45 const auto maxDeferRenderAttempts = base::GetUintProperty<
46 size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"),
47 Flattener::Tunables::RenderScheduling::kDefaultMaxDeferRenderAttempts);
48
49 return std::make_optional<Flattener::Tunables::RenderScheduling>(
50 Flattener::Tunables::RenderScheduling{
51 .cachedSetRenderDuration = renderDuration,
52 .maxDeferRenderAttempts = maxDeferRenderAttempts,
53 });
54 }
55
buildFlattenerTuneables()56 Flattener::Tunables buildFlattenerTuneables() {
57 const auto activeLayerTimeout = std::chrono::milliseconds(
58 base::GetIntProperty<int32_t>(std::string(
59 "debug.sf.layer_caching_active_layer_timeout_ms"),
60 Flattener::Tunables::kDefaultActiveLayerTimeout.count()));
61 const auto enableHolePunch =
62 base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"),
63 Flattener::Tunables::kDefaultEnableHolePunch);
64 return Flattener::Tunables{
65 .mActiveLayerTimeout = activeLayerTimeout,
66 .mRenderScheduling = buildRenderSchedulingTunables(),
67 .mEnableHolePunch = enableHolePunch,
68 };
69 }
70
71 } // namespace
72
Planner(renderengine::RenderEngine & renderEngine)73 Planner::Planner(renderengine::RenderEngine& renderEngine)
74 : mFlattener(renderEngine,
75 buildFlattenerTuneables()) {
76 mPredictorEnabled =
77 base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
78 }
79
setDisplaySize(ui::Size size)80 void Planner::setDisplaySize(ui::Size size) {
81 mFlattener.setDisplaySize(size);
82 }
83
plan(compositionengine::Output::OutputLayersEnumerator<compositionengine::Output> && layers)84 void Planner::plan(
85 compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
86 ATRACE_CALL();
87 std::unordered_set<LayerId> removedLayers;
88 removedLayers.reserve(mPreviousLayers.size());
89
90 std::transform(mPreviousLayers.begin(), mPreviousLayers.end(),
91 std::inserter(removedLayers, removedLayers.begin()),
92 [](const auto& layer) { return layer.first; });
93
94 std::vector<LayerId> currentLayerIds;
95 for (auto layer : layers) {
96 LayerId id = layer->getLayerFE().getSequence();
97 if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) {
98 // Track changes from previous info
99 LayerState& state = layerEntry->second;
100 ftl::Flags<LayerStateField> differences = state.update(layer);
101 if (differences.get() == 0) {
102 state.incrementFramesSinceBufferUpdate();
103 } else {
104 ALOGV("Layer %s changed: %s", state.getName().c_str(),
105 differences.string().c_str());
106
107 if (differences.test(LayerStateField::Buffer)) {
108 state.resetFramesSinceBufferUpdate();
109 } else {
110 state.incrementFramesSinceBufferUpdate();
111 }
112 }
113 } else {
114 LayerState state(layer);
115 ALOGV("Added layer %s", state.getName().c_str());
116 mPreviousLayers.emplace(std::make_pair(id, std::move(state)));
117 }
118
119 currentLayerIds.emplace_back(id);
120
121 if (const auto found = removedLayers.find(id); found != removedLayers.end()) {
122 removedLayers.erase(found);
123 }
124 }
125
126 mCurrentLayers.clear();
127 mCurrentLayers.reserve(currentLayerIds.size());
128 std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
129 std::back_inserter(mCurrentLayers), [this](LayerId id) {
130 LayerState* state = &mPreviousLayers.at(id);
131 state->getOutputLayer()->editState().overrideInfo = {};
132 return state;
133 });
134
135 const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
136 mFlattenedHash =
137 mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
138 const bool layersWereFlattened = hash != mFlattenedHash;
139
140 ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
141
142 if (mPredictorEnabled) {
143 mPredictedPlan =
144 mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
145 : mCurrentLayers,
146 mFlattenedHash);
147 if (mPredictedPlan) {
148 ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
149 } else {
150 ALOGV("[%s] No prediction found\n", __func__);
151 }
152 }
153
154 // Clean up the set of previous layers now that the view of the LayerStates in the flattener are
155 // up-to-date.
156 for (LayerId removedLayer : removedLayers) {
157 if (const auto layerEntry = mPreviousLayers.find(removedLayer);
158 layerEntry != mPreviousLayers.end()) {
159 const auto& [id, state] = *layerEntry;
160 ALOGV("Removed layer %s", state.getName().c_str());
161 mPreviousLayers.erase(removedLayer);
162 }
163 }
164 }
165
reportFinalPlan(compositionengine::Output::OutputLayersEnumerator<compositionengine::Output> && layers)166 void Planner::reportFinalPlan(
167 compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
168 ATRACE_CALL();
169 if (!mPredictorEnabled) {
170 return;
171 }
172
173 Plan finalPlan;
174 const GraphicBuffer* currentOverrideBuffer = nullptr;
175 bool hasSkippedLayers = false;
176 for (auto layer : layers) {
177 if (!layer->getState().overrideInfo.buffer) {
178 continue;
179 }
180
181 const GraphicBuffer* overrideBuffer =
182 layer->getState().overrideInfo.buffer->getBuffer().get();
183 if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
184 // Skip this layer since it is part of a previous cached set
185 hasSkippedLayers = true;
186 continue;
187 }
188
189 currentOverrideBuffer = overrideBuffer;
190
191 const bool forcedOrRequestedClient =
192 layer->getState().forceClientComposition || layer->requiresClientComposition();
193
194 finalPlan.addLayerType(
195 forcedOrRequestedClient
196 ? aidl::android::hardware::graphics::composer3::Composition::CLIENT
197 : layer->getLayerFE().getCompositionState()->compositionType);
198 }
199
200 mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
201 finalPlan);
202 }
203
renderCachedSets(const OutputCompositionState & outputState,std::optional<std::chrono::steady_clock::time_point> renderDeadline,bool deviceHandlesColorTransform)204 void Planner::renderCachedSets(const OutputCompositionState& outputState,
205 std::optional<std::chrono::steady_clock::time_point> renderDeadline,
206 bool deviceHandlesColorTransform) {
207 ATRACE_CALL();
208 mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform);
209 }
210
dump(const Vector<String16> & args,std::string & result)211 void Planner::dump(const Vector<String16>& args, std::string& result) {
212 if (args.size() > 1) {
213 const String8 command(args[1]);
214 if (command == "--compare" || command == "-c") {
215 if (args.size() < 4) {
216 base::StringAppendF(&result,
217 "Expected two layer stack hashes, e.g. '--planner %s "
218 "<left_hash> <right_hash>'\n",
219 command.c_str());
220 return;
221 }
222 if (args.size() > 4) {
223 base::StringAppendF(&result,
224 "Too many arguments found, expected '--planner %s <left_hash> "
225 "<right_hash>'\n",
226 command.c_str());
227 return;
228 }
229
230 const String8 leftHashString(args[2]);
231 size_t leftHash = 0;
232 int fieldsRead = sscanf(leftHashString.c_str(), "%zx", &leftHash);
233 if (fieldsRead != 1) {
234 base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
235 leftHashString.c_str());
236 return;
237 }
238
239 const String8 rightHashString(args[3]);
240 size_t rightHash = 0;
241 fieldsRead = sscanf(rightHashString.c_str(), "%zx", &rightHash);
242 if (fieldsRead != 1) {
243 base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
244 rightHashString.c_str());
245 return;
246 }
247
248 if (mPredictorEnabled) {
249 mPredictor.compareLayerStacks(leftHash, rightHash, result);
250 }
251 } else if (command == "--describe" || command == "-d") {
252 if (args.size() < 3) {
253 base::StringAppendF(&result,
254 "Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
255 command.c_str());
256 return;
257 }
258 if (args.size() > 3) {
259 base::StringAppendF(&result,
260 "Too many arguments found, expected '--planner %s <hash>'\n",
261 command.c_str());
262 return;
263 }
264
265 const String8 hashString(args[2]);
266 size_t hash = 0;
267 const int fieldsRead = sscanf(hashString.c_str(), "%zx", &hash);
268 if (fieldsRead != 1) {
269 base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
270 hashString.c_str());
271 return;
272 }
273
274 if (mPredictorEnabled) {
275 mPredictor.describeLayerStack(hash, result);
276 }
277 } else if (command == "--help" || command == "-h") {
278 dumpUsage(result);
279 } else if (command == "--similar" || command == "-s") {
280 if (args.size() < 3) {
281 base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
282 command.c_str());
283 return;
284 }
285 if (args.size() > 3) {
286 base::StringAppendF(&result,
287 "Too many arguments found, expected '--planner %s <plan>'\n",
288 command.c_str());
289 return;
290 }
291
292 const String8 planString(args[2]);
293 std::optional<Plan> plan = Plan::fromString(std::string(planString.c_str()));
294 if (!plan) {
295 base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.c_str());
296 return;
297 }
298
299 if (mPredictorEnabled) {
300 mPredictor.listSimilarStacks(*plan, result);
301 }
302 } else if (command == "--layers" || command == "-l") {
303 mFlattener.dumpLayers(result);
304 } else {
305 base::StringAppendF(&result, "Unknown command '%s'\n\n", command.c_str());
306 dumpUsage(result);
307 }
308 return;
309 }
310
311 // If there are no specific commands, dump the usual state
312
313 mFlattener.dump(result);
314 result.append("\n");
315
316 if (mPredictorEnabled) {
317 mPredictor.dump(result);
318 }
319 }
320
dumpUsage(std::string & result) const321 void Planner::dumpUsage(std::string& result) const {
322 result.append("Planner command line interface usage\n");
323 result.append(" dumpsys SurfaceFlinger --planner <command> [arguments]\n\n");
324
325 result.append("If run without a command, dumps current Planner state\n\n");
326
327 result.append("Commands:\n");
328
329 result.append("[--compare|-c] <left_hash> <right_hash>\n");
330 result.append(" Compares the predictions <left_hash> and <right_hash> by showing differences"
331 " in their example layer stacks\n");
332
333 result.append("[--describe|-d] <hash>\n");
334 result.append(" Prints the example layer stack and prediction statistics for <hash>\n");
335
336 result.append("[--help|-h]\n");
337 result.append(" Shows this message\n");
338
339 result.append("[--similar|-s] <plan>\n");
340 result.append(" Prints the example layer names for similar stacks matching <plan>\n");
341
342 result.append("[--layers|-l]\n");
343 result.append(" Prints the current layers\n");
344 }
345
346 } // namespace android::compositionengine::impl::planner
347