• 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 #undef LOG_TAG
18 #define LOG_TAG "Planner"
19 // #define LOG_NDEBUG 0
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21 
22 #include <android-base/properties.h>
23 #include <compositionengine/impl/OutputCompositionState.h>
24 #include <compositionengine/impl/planner/CachedSet.h>
25 #include <math/HashCombine.h>
26 #include <renderengine/DisplaySettings.h>
27 #include <renderengine/RenderEngine.h>
28 #include <utils/Trace.h>
29 
30 #include <utils/Trace.h>
31 
32 namespace android::compositionengine::impl::planner {
33 
34 const bool CachedSet::sDebugHighlighLayers =
35         base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
36 
durationString(std::chrono::milliseconds duration)37 std::string durationString(std::chrono::milliseconds duration) {
38     using namespace std::chrono_literals;
39 
40     std::string result;
41 
42     if (duration >= 1h) {
43         const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
44         base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
45         duration -= hours;
46     }
47     if (duration >= 1min) {
48         const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
49         base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
50         duration -= minutes;
51     }
52     base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
53 
54     return result;
55 }
56 
Layer(const LayerState * state,std::chrono::steady_clock::time_point lastUpdate)57 CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
58       : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
59 
CachedSet(const LayerState * layer,std::chrono::steady_clock::time_point lastUpdate)60 CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
61       : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
62     addLayer(layer, lastUpdate);
63 }
64 
CachedSet(Layer layer)65 CachedSet::CachedSet(Layer layer)
66       : mFingerprint(layer.getHash()),
67         mLastUpdate(layer.getLastUpdate()),
68         mBounds(layer.getDisplayFrame()),
69         mVisibleRegion(layer.getVisibleRegion()) {
70     mLayers.emplace_back(std::move(layer));
71 }
72 
addLayer(const LayerState * layer,std::chrono::steady_clock::time_point lastUpdate)73 void CachedSet::addLayer(const LayerState* layer,
74                          std::chrono::steady_clock::time_point lastUpdate) {
75     mLayers.emplace_back(layer, lastUpdate);
76 
77     Region boundingRegion;
78     boundingRegion.orSelf(mBounds);
79     boundingRegion.orSelf(layer->getDisplayFrame());
80     mBounds = boundingRegion.getBounds();
81     mVisibleRegion.orSelf(layer->getVisibleRegion());
82 }
83 
getNonBufferHash() const84 NonBufferHash CachedSet::getNonBufferHash() const {
85     if (mLayers.size() == 1) {
86         return mFingerprint;
87     }
88 
89     // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
90     // necessary (and therefore we need to match implementations).
91     size_t hash = 0;
92     android::hashCombineSingle(hash, mBounds);
93     android::hashCombineSingle(hash, mOutputDataspace);
94     android::hashCombineSingle(hash, mOrientation);
95     return hash;
96 }
97 
getComponentDisplayCost() const98 size_t CachedSet::getComponentDisplayCost() const {
99     size_t displayCost = 0;
100 
101     for (const Layer& layer : mLayers) {
102         displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
103                                            layer.getDisplayFrame().height());
104     }
105 
106     return displayCost;
107 }
108 
getCreationCost() const109 size_t CachedSet::getCreationCost() const {
110     if (mLayers.size() == 1) {
111         return 0;
112     }
113 
114     // Reads
115     size_t creationCost = getComponentDisplayCost();
116 
117     // Write - assumes that the output buffer only gets written once per pixel
118     creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
119 
120     return creationCost;
121 }
122 
getDisplayCost() const123 size_t CachedSet::getDisplayCost() const {
124     return static_cast<size_t>(mBounds.width() * mBounds.height());
125 }
126 
hasBufferUpdate() const127 bool CachedSet::hasBufferUpdate() const {
128     for (const Layer& layer : mLayers) {
129         if (layer.getFramesSinceBufferUpdate() == 0) {
130             return true;
131         }
132     }
133     return false;
134 }
135 
hasReadyBuffer() const136 bool CachedSet::hasReadyBuffer() const {
137     return mTexture && mDrawFence->getStatus() == Fence::Status::Signaled;
138 }
139 
decompose() const140 std::vector<CachedSet> CachedSet::decompose() const {
141     std::vector<CachedSet> layers;
142 
143     std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
144                    [](Layer layer) { return CachedSet(std::move(layer)); });
145 
146     return layers;
147 }
148 
updateAge(std::chrono::steady_clock::time_point now)149 void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
150     LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
151                         __func__);
152 
153     if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
154         mLastUpdate = now;
155         mAge = 0;
156     }
157 }
158 
render(renderengine::RenderEngine & renderEngine,TexturePool & texturePool,const OutputCompositionState & outputState)159 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
160                        const OutputCompositionState& outputState) {
161     ATRACE_CALL();
162     const Rect& viewport = outputState.layerStackSpace.content;
163     const ui::Dataspace& outputDataspace = outputState.dataspace;
164     const ui::Transform::RotationFlags orientation =
165             ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
166 
167     renderengine::DisplaySettings displaySettings{
168             .physicalDisplay = outputState.framebufferSpace.content,
169             .clip = viewport,
170             .outputDataspace = outputDataspace,
171             .orientation = orientation,
172     };
173 
174     Region clearRegion = Region::INVALID_REGION;
175     LayerFE::ClientCompositionTargetSettings targetSettings{
176             .clip = Region(viewport),
177             .needsFiltering = false,
178             .isSecure = outputState.isSecure,
179             .supportsProtectedContent = false,
180             .clearRegion = clearRegion,
181             .viewport = viewport,
182             .dataspace = outputDataspace,
183             .realContentIsVisible = true,
184             .clearContent = false,
185             .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
186     };
187 
188     std::vector<renderengine::LayerSettings> layerSettings;
189     renderengine::LayerSettings highlight;
190     for (const auto& layer : mLayers) {
191         const auto clientCompositionList =
192                 layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
193                         targetSettings);
194         layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
195                              clientCompositionList.cend());
196     }
197 
198     std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
199     std::transform(layerSettings.cbegin(), layerSettings.cend(),
200                    std::back_inserter(layerSettingsPointers),
201                    [](const renderengine::LayerSettings& settings) { return &settings; });
202 
203     renderengine::LayerSettings blurLayerSettings;
204     if (mBlurLayer) {
205         auto blurSettings = targetSettings;
206         blurSettings.blurSetting =
207                 LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
208         auto clientCompositionList =
209                 mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
210                         blurSettings);
211         blurLayerSettings = clientCompositionList.back();
212         // This mimics Layer::prepareClearClientComposition
213         blurLayerSettings.skipContentDraw = true;
214         blurLayerSettings.name = std::string("blur layer");
215         // Clear out the shadow settings
216         blurLayerSettings.shadow = {};
217         layerSettingsPointers.push_back(&blurLayerSettings);
218     }
219 
220     renderengine::LayerSettings holePunchSettings;
221     renderengine::LayerSettings holePunchBackgroundSettings;
222     if (mHolePunchLayer) {
223         auto clientCompositionList =
224                 mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
225                         targetSettings);
226         // Assume that the final layer contains the buffer that we want to
227         // replace with a hole punch.
228         holePunchSettings = clientCompositionList.back();
229         // This mimics Layer::prepareClearClientComposition
230         holePunchSettings.source.buffer.buffer = nullptr;
231         holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
232         holePunchSettings.disableBlending = true;
233         holePunchSettings.alpha = 0.0f;
234         holePunchSettings.name = std::string("hole punch layer");
235         layerSettingsPointers.push_back(&holePunchSettings);
236 
237         // Add a solid background as the first layer in case there is no opaque
238         // buffer behind the punch hole
239         holePunchBackgroundSettings.alpha = 1.0f;
240         holePunchBackgroundSettings.name = std::string("holePunchBackground");
241         holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries;
242         holePunchBackgroundSettings.geometry.positionTransform =
243                 holePunchSettings.geometry.positionTransform;
244         layerSettingsPointers.insert(layerSettingsPointers.begin(), &holePunchBackgroundSettings);
245     }
246 
247     if (sDebugHighlighLayers) {
248         highlight = {
249                 .geometry =
250                         renderengine::Geometry{
251                                 .boundaries = FloatRect(0.0f, 0.0f,
252                                                         static_cast<float>(mBounds.getWidth()),
253                                                         static_cast<float>(mBounds.getHeight())),
254                         },
255                 .source =
256                         renderengine::PixelSource{
257                                 .solidColor = half3(0.25f, 0.0f, 0.5f),
258                         },
259                 .alpha = half(0.05f),
260         };
261 
262         layerSettingsPointers.emplace_back(&highlight);
263     }
264 
265     auto texture = texturePool.borrowTexture();
266     LOG_ALWAYS_FATAL_IF(texture->get()->getBuffer()->initCheck() != OK);
267 
268     base::unique_fd bufferFence;
269     if (texture->getReadyFence()) {
270         // Bail out if the buffer is not ready, because there is some pending GPU work left.
271         if (texture->getReadyFence()->getStatus() != Fence::Status::Signaled) {
272             return;
273         }
274         bufferFence.reset(texture->getReadyFence()->dup());
275     }
276 
277     base::unique_fd drawFence;
278     status_t result =
279             renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false,
280                                     std::move(bufferFence), &drawFence);
281 
282     if (result == NO_ERROR) {
283         mDrawFence = new Fence(drawFence.release());
284         mOutputSpace = outputState.framebufferSpace;
285         mTexture = texture;
286         mTexture->setReadyFence(mDrawFence);
287         mOutputSpace.orientation = outputState.framebufferSpace.orientation;
288         mOutputDataspace = outputDataspace;
289         mOrientation = orientation;
290         mSkipCount = 0;
291     } else {
292         mTexture.reset();
293     }
294 }
295 
requiresHolePunch() const296 bool CachedSet::requiresHolePunch() const {
297     // In order for the hole punch to be beneficial, the layer must be updating
298     // regularly, meaning  it should not have been merged with other layers.
299     if (getLayerCount() != 1) {
300         return false;
301     }
302 
303     // There is no benefit to a hole punch unless the layer has a buffer.
304     if (!mLayers[0].getBuffer()) {
305         return false;
306     }
307 
308     if (hasUnsupportedDataspace()) {
309         return false;
310     }
311 
312     const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
313     if (layerFE.getCompositionState()->forceClientComposition) {
314         return false;
315     }
316 
317     return layerFE.hasRoundedCorners();
318 }
319 
hasBlurBehind() const320 bool CachedSet::hasBlurBehind() const {
321     return std::any_of(mLayers.cbegin(), mLayers.cend(),
322                        [](const Layer& layer) { return layer.getState()->hasBlurBehind(); });
323 }
324 
325 namespace {
contains(const Rect & outer,const Rect & inner)326 bool contains(const Rect& outer, const Rect& inner) {
327     return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
328             outer.bottom >= inner.bottom;
329 }
330 }; // namespace
331 
addHolePunchLayerIfFeasible(const CachedSet & holePunchLayer,bool isFirstLayer)332 void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
333     // Verify that this CachedSet is opaque where the hole punch layer
334     // will draw.
335     const Rect& holePunchBounds = holePunchLayer.getBounds();
336     for (const auto& layer : mLayers) {
337         // The first layer is considered opaque because nothing is behind it.
338         // Note that isOpaque is always false for a layer with rounded
339         // corners, even if the interior is opaque. In theory, such a layer
340         // could be used for a hole punch, but this is unlikely to happen in
341         // practice.
342         const auto* outputLayer = layer.getState()->getOutputLayer();
343         if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
344             (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
345             mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
346             return;
347         }
348     }
349 }
350 
addBackgroundBlurLayer(const CachedSet & blurLayer)351 void CachedSet::addBackgroundBlurLayer(const CachedSet& blurLayer) {
352     mBlurLayer = blurLayer.getFirstLayer().getState();
353 }
354 
getHolePunchLayer() const355 compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
356     return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
357 }
358 
getBlurLayer() const359 compositionengine::OutputLayer* CachedSet::getBlurLayer() const {
360     return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
361 }
362 
hasUnsupportedDataspace() const363 bool CachedSet::hasUnsupportedDataspace() const {
364     return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
365         auto dataspace = layer.getState()->getDataspace();
366         const auto transfer = static_cast<ui::Dataspace>(dataspace & ui::Dataspace::TRANSFER_MASK);
367         if (transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG) {
368             // Skip HDR.
369             return true;
370         }
371 
372         if ((dataspace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT601_625) {
373             // RenderEngine does not match some DPUs, so skip
374             // to avoid flickering/color differences.
375             return true;
376         }
377         return false;
378     });
379 }
380 
hasProtectedLayers() const381 bool CachedSet::hasProtectedLayers() const {
382     return std::any_of(mLayers.cbegin(), mLayers.cend(),
383                        [](const Layer& layer) { return layer.getState()->isProtected(); });
384 }
385 
dump(std::string & result) const386 void CachedSet::dump(std::string& result) const {
387     const auto now = std::chrono::steady_clock::now();
388 
389     const auto lastUpdate =
390             std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
391     base::StringAppendF(&result, "  + Fingerprint %016zx, last update %sago, age %zd\n",
392                         mFingerprint, durationString(lastUpdate).c_str(), mAge);
393     {
394         const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
395         base::StringAppendF(&result, "    Override buffer: %p\n", b);
396     }
397     base::StringAppendF(&result, "    HolePunchLayer: %p\n", mHolePunchLayer);
398 
399     if (mLayers.size() == 1) {
400         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
401         base::StringAppendF(&result, "    Buffer %p", mLayers[0].getBuffer().get());
402         base::StringAppendF(&result, "    Protected [%s]",
403                             mLayers[0].getState()->isProtected() ? "true" : "false");
404     } else {
405         result.append("    Cached set of:");
406         for (const Layer& layer : mLayers) {
407             base::StringAppendF(&result, "\n      Layer [%s]", layer.getName().c_str());
408             base::StringAppendF(&result, "\n      Protected [%s]",
409                                 layer.getState()->isProtected() ? "true" : "false");
410         }
411     }
412 
413     base::StringAppendF(&result, "\n    Creation cost: %zd", getCreationCost());
414     base::StringAppendF(&result, "\n    Display cost: %zd\n", getDisplayCost());
415 }
416 
417 } // namespace android::compositionengine::impl::planner
418