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