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
buildFlattenerTuneables()35 std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() {
36 if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) {
37 return std::nullopt;
38 }
39
40 auto renderDuration = std::chrono::nanoseconds(
41 base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"),
42 Flattener::CachedSetRenderSchedulingTunables::
43 kDefaultCachedSetRenderDuration.count()));
44
45 auto maxDeferRenderAttempts = base::GetUintProperty<
46 size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"),
47 Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts);
48
49 return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>(
50 Flattener::CachedSetRenderSchedulingTunables{
51 .cachedSetRenderDuration = renderDuration,
52 .maxDeferRenderAttempts = maxDeferRenderAttempts,
53 });
54 }
55
56 } // namespace
57
Planner(renderengine::RenderEngine & renderEngine)58 Planner::Planner(renderengine::RenderEngine& renderEngine)
59 // Implicitly, layer caching must also be enabled for the hole punch or
60 // predictor to have any effect.
61 // E.g., setprop debug.sf.enable_layer_caching 1, or
62 // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
63 : mFlattener(renderEngine,
64 base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true),
65 buildFlattenerTuneables()) {
66 mPredictorEnabled =
67 base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
68 }
69
setDisplaySize(ui::Size size)70 void Planner::setDisplaySize(ui::Size size) {
71 mFlattener.setDisplaySize(size);
72 }
73
plan(compositionengine::Output::OutputLayersEnumerator<compositionengine::Output> && layers)74 void Planner::plan(
75 compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
76 ATRACE_CALL();
77 std::unordered_set<LayerId> removedLayers;
78 removedLayers.reserve(mPreviousLayers.size());
79
80 std::transform(mPreviousLayers.begin(), mPreviousLayers.end(),
81 std::inserter(removedLayers, removedLayers.begin()),
82 [](const auto& layer) { return layer.first; });
83
84 std::vector<LayerId> currentLayerIds;
85 for (auto layer : layers) {
86 LayerId id = layer->getLayerFE().getSequence();
87 if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) {
88 // Track changes from previous info
89 LayerState& state = layerEntry->second;
90 Flags<LayerStateField> differences = state.update(layer);
91 if (differences.get() == 0) {
92 state.incrementFramesSinceBufferUpdate();
93 } else {
94 ALOGV("Layer %s changed: %s", state.getName().c_str(),
95 differences.string().c_str());
96
97 if (differences.test(LayerStateField::Buffer)) {
98 state.resetFramesSinceBufferUpdate();
99 } else {
100 state.incrementFramesSinceBufferUpdate();
101 }
102 }
103 } else {
104 LayerState state(layer);
105 ALOGV("Added layer %s", state.getName().c_str());
106 mPreviousLayers.emplace(std::make_pair(id, std::move(state)));
107 }
108
109 currentLayerIds.emplace_back(id);
110
111 if (const auto found = removedLayers.find(id); found != removedLayers.end()) {
112 removedLayers.erase(found);
113 }
114 }
115
116 mCurrentLayers.clear();
117 mCurrentLayers.reserve(currentLayerIds.size());
118 std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
119 std::back_inserter(mCurrentLayers), [this](LayerId id) {
120 LayerState* state = &mPreviousLayers.at(id);
121 state->getOutputLayer()->editState().overrideInfo = {};
122 return state;
123 });
124
125 const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
126 mFlattenedHash =
127 mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
128 const bool layersWereFlattened = hash != mFlattenedHash;
129
130 ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
131
132 if (mPredictorEnabled) {
133 mPredictedPlan =
134 mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
135 : mCurrentLayers,
136 mFlattenedHash);
137 if (mPredictedPlan) {
138 ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
139 } else {
140 ALOGV("[%s] No prediction found\n", __func__);
141 }
142 }
143
144 // Clean up the set of previous layers now that the view of the LayerStates in the flattener are
145 // up-to-date.
146 for (LayerId removedLayer : removedLayers) {
147 if (const auto layerEntry = mPreviousLayers.find(removedLayer);
148 layerEntry != mPreviousLayers.end()) {
149 const auto& [id, state] = *layerEntry;
150 ALOGV("Removed layer %s", state.getName().c_str());
151 mPreviousLayers.erase(removedLayer);
152 }
153 }
154 }
155
reportFinalPlan(compositionengine::Output::OutputLayersEnumerator<compositionengine::Output> && layers)156 void Planner::reportFinalPlan(
157 compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
158 ATRACE_CALL();
159 if (!mPredictorEnabled) {
160 return;
161 }
162
163 Plan finalPlan;
164 const GraphicBuffer* currentOverrideBuffer = nullptr;
165 bool hasSkippedLayers = false;
166 for (auto layer : layers) {
167 if (!layer->getState().overrideInfo.buffer) {
168 continue;
169 }
170
171 const GraphicBuffer* overrideBuffer =
172 layer->getState().overrideInfo.buffer->getBuffer().get();
173 if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
174 // Skip this layer since it is part of a previous cached set
175 hasSkippedLayers = true;
176 continue;
177 }
178
179 currentOverrideBuffer = overrideBuffer;
180
181 const bool forcedOrRequestedClient =
182 layer->getState().forceClientComposition || layer->requiresClientComposition();
183
184 finalPlan.addLayerType(
185 forcedOrRequestedClient
186 ? hardware::graphics::composer::hal::Composition::CLIENT
187 : layer->getLayerFE().getCompositionState()->compositionType);
188 }
189
190 mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
191 finalPlan);
192 }
193
renderCachedSets(const OutputCompositionState & outputState,std::optional<std::chrono::steady_clock::time_point> renderDeadline)194 void Planner::renderCachedSets(
195 const OutputCompositionState& outputState,
196 std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
197 ATRACE_CALL();
198 mFlattener.renderCachedSets(outputState, renderDeadline);
199 }
200
dump(const Vector<String16> & args,std::string & result)201 void Planner::dump(const Vector<String16>& args, std::string& result) {
202 if (args.size() > 1) {
203 const String8 command(args[1]);
204 if (command == "--compare" || command == "-c") {
205 if (args.size() < 4) {
206 base::StringAppendF(&result,
207 "Expected two layer stack hashes, e.g. '--planner %s "
208 "<left_hash> <right_hash>'\n",
209 command.string());
210 return;
211 }
212 if (args.size() > 4) {
213 base::StringAppendF(&result,
214 "Too many arguments found, expected '--planner %s <left_hash> "
215 "<right_hash>'\n",
216 command.string());
217 return;
218 }
219
220 const String8 leftHashString(args[2]);
221 size_t leftHash = 0;
222 int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash);
223 if (fieldsRead != 1) {
224 base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
225 leftHashString.string());
226 return;
227 }
228
229 const String8 rightHashString(args[3]);
230 size_t rightHash = 0;
231 fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash);
232 if (fieldsRead != 1) {
233 base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
234 rightHashString.string());
235 return;
236 }
237
238 if (mPredictorEnabled) {
239 mPredictor.compareLayerStacks(leftHash, rightHash, result);
240 }
241 } else if (command == "--describe" || command == "-d") {
242 if (args.size() < 3) {
243 base::StringAppendF(&result,
244 "Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
245 command.string());
246 return;
247 }
248 if (args.size() > 3) {
249 base::StringAppendF(&result,
250 "Too many arguments found, expected '--planner %s <hash>'\n",
251 command.string());
252 return;
253 }
254
255 const String8 hashString(args[2]);
256 size_t hash = 0;
257 const int fieldsRead = sscanf(hashString.string(), "%zx", &hash);
258 if (fieldsRead != 1) {
259 base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
260 hashString.string());
261 return;
262 }
263
264 if (mPredictorEnabled) {
265 mPredictor.describeLayerStack(hash, result);
266 }
267 } else if (command == "--help" || command == "-h") {
268 dumpUsage(result);
269 } else if (command == "--similar" || command == "-s") {
270 if (args.size() < 3) {
271 base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
272 command.string());
273 return;
274 }
275 if (args.size() > 3) {
276 base::StringAppendF(&result,
277 "Too many arguments found, expected '--planner %s <plan>'\n",
278 command.string());
279 return;
280 }
281
282 const String8 planString(args[2]);
283 std::optional<Plan> plan = Plan::fromString(std::string(planString.string()));
284 if (!plan) {
285 base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string());
286 return;
287 }
288
289 if (mPredictorEnabled) {
290 mPredictor.listSimilarStacks(*plan, result);
291 }
292 } else if (command == "--layers" || command == "-l") {
293 mFlattener.dumpLayers(result);
294 } else {
295 base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
296 dumpUsage(result);
297 }
298 return;
299 }
300
301 // If there are no specific commands, dump the usual state
302
303 mFlattener.dump(result);
304 result.append("\n");
305
306 if (mPredictorEnabled) {
307 mPredictor.dump(result);
308 }
309 }
310
dumpUsage(std::string & result) const311 void Planner::dumpUsage(std::string& result) const {
312 result.append("Planner command line interface usage\n");
313 result.append(" dumpsys SurfaceFlinger --planner <command> [arguments]\n\n");
314
315 result.append("If run without a command, dumps current Planner state\n\n");
316
317 result.append("Commands:\n");
318
319 result.append("[--compare|-c] <left_hash> <right_hash>\n");
320 result.append(" Compares the predictions <left_hash> and <right_hash> by showing differences"
321 " in their example layer stacks\n");
322
323 result.append("[--describe|-d] <hash>\n");
324 result.append(" Prints the example layer stack and prediction statistics for <hash>\n");
325
326 result.append("[--help|-h]\n");
327 result.append(" Shows this message\n");
328
329 result.append("[--similar|-s] <plan>\n");
330 result.append(" Prints the example layer names for similar stacks matching <plan>\n");
331
332 result.append("[--layers|-l]\n");
333 result.append(" Prints the current layers\n");
334 }
335
336 } // namespace android::compositionengine::impl::planner
337