• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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