• 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 <android-base/stringprintf.h>
24 #include <compositionengine/impl/OutputCompositionState.h>
25 #include <compositionengine/impl/planner/CachedSet.h>
26 #include <math/HashCombine.h>
27 #include <renderengine/DisplaySettings.h>
28 #include <renderengine/RenderEngine.h>
29 #include <ui/DebugUtils.h>
30 #include <utils/Trace.h>
31 
32 #include <utils/Trace.h>
33 
34 namespace android::compositionengine::impl::planner {
35 
36 const bool CachedSet::sDebugHighlighLayers =
37         base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
38 
durationString(std::chrono::milliseconds duration)39 std::string durationString(std::chrono::milliseconds duration) {
40     using namespace std::chrono_literals;
41 
42     std::string result;
43 
44     if (duration >= 1h) {
45         const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
46         base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
47         duration -= hours;
48     }
49     if (duration >= 1min) {
50         const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
51         base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
52         duration -= minutes;
53     }
54     base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
55 
56     return result;
57 }
58 
Layer(const LayerState * state,std::chrono::steady_clock::time_point lastUpdate)59 CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
60       : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
61 
CachedSet(const LayerState * layer,std::chrono::steady_clock::time_point lastUpdate)62 CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
63       : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
64     addLayer(layer, lastUpdate);
65 }
66 
CachedSet(Layer layer)67 CachedSet::CachedSet(Layer layer)
68       : mFingerprint(layer.getHash()),
69         mLastUpdate(layer.getLastUpdate()),
70         mBounds(layer.getDisplayFrame()),
71         mVisibleRegion(layer.getVisibleRegion()) {
72     mLayers.emplace_back(std::move(layer));
73 }
74 
addLayer(const LayerState * layer,std::chrono::steady_clock::time_point lastUpdate)75 void CachedSet::addLayer(const LayerState* layer,
76                          std::chrono::steady_clock::time_point lastUpdate) {
77     mLayers.emplace_back(layer, lastUpdate);
78 
79     Region boundingRegion;
80     boundingRegion.orSelf(mBounds);
81     boundingRegion.orSelf(layer->getDisplayFrame());
82     mBounds = boundingRegion.getBounds();
83     mVisibleRegion.orSelf(layer->getVisibleRegion());
84 }
85 
getNonBufferHash() const86 NonBufferHash CachedSet::getNonBufferHash() const {
87     if (mLayers.size() == 1) {
88         return mFingerprint;
89     }
90 
91     // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
92     // necessary (and therefore we need to match implementations).
93     size_t hash = 0;
94     android::hashCombineSingle(hash, mBounds);
95     android::hashCombineSingle(hash, mOutputDataspace);
96     android::hashCombineSingle(hash, mOrientation);
97     return hash;
98 }
99 
getComponentDisplayCost() const100 size_t CachedSet::getComponentDisplayCost() const {
101     size_t displayCost = 0;
102 
103     for (const Layer& layer : mLayers) {
104         displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
105                                            layer.getDisplayFrame().height());
106     }
107 
108     return displayCost;
109 }
110 
getCreationCost() const111 size_t CachedSet::getCreationCost() const {
112     if (mLayers.size() == 1) {
113         return 0;
114     }
115 
116     // Reads
117     size_t creationCost = getComponentDisplayCost();
118 
119     // Write - assumes that the output buffer only gets written once per pixel
120     creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
121 
122     return creationCost;
123 }
124 
getDisplayCost() const125 size_t CachedSet::getDisplayCost() const {
126     return static_cast<size_t>(mBounds.width() * mBounds.height());
127 }
128 
hasBufferUpdate() const129 bool CachedSet::hasBufferUpdate() const {
130     for (const Layer& layer : mLayers) {
131         if (layer.getFramesSinceBufferUpdate() == 0) {
132             return true;
133         }
134     }
135     return false;
136 }
137 
hasReadyBuffer() const138 bool CachedSet::hasReadyBuffer() const {
139     return mTexture && mDrawFence->getStatus() == Fence::Status::Signaled;
140 }
141 
decompose() const142 std::vector<CachedSet> CachedSet::decompose() const {
143     std::vector<CachedSet> layers;
144 
145     std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
146                    [](Layer layer) { return CachedSet(std::move(layer)); });
147 
148     return layers;
149 }
150 
updateAge(std::chrono::steady_clock::time_point now)151 void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
152     LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
153                         __func__);
154 
155     if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
156         mLastUpdate = now;
157         mAge = 0;
158     }
159 }
160 
render(renderengine::RenderEngine & renderEngine,TexturePool & texturePool,const OutputCompositionState & outputState,bool deviceHandlesColorTransform)161 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
162                        const OutputCompositionState& outputState,
163                        bool deviceHandlesColorTransform) {
164     ATRACE_CALL();
165     const Rect& viewport = outputState.layerStackSpace.getContent();
166     const ui::Dataspace& outputDataspace = outputState.dataspace;
167     const ui::Transform::RotationFlags orientation =
168             ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
169 
170     renderengine::DisplaySettings displaySettings{
171             .physicalDisplay = outputState.framebufferSpace.getContent(),
172             .clip = viewport,
173             .outputDataspace = outputDataspace,
174             .colorTransform = outputState.colorTransformMatrix,
175             .deviceHandlesColorTransform = deviceHandlesColorTransform,
176             .orientation = orientation,
177             .targetLuminanceNits = outputState.displayBrightnessNits,
178     };
179 
180     LayerFE::ClientCompositionTargetSettings targetSettings{
181             .clip = Region(viewport),
182             .needsFiltering = false,
183             .isSecure = outputState.isSecure,
184             .supportsProtectedContent = false,
185             .viewport = viewport,
186             .dataspace = outputDataspace,
187             .realContentIsVisible = true,
188             .clearContent = false,
189             .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
190             .whitePointNits = outputState.displayBrightnessNits,
191     };
192 
193     std::vector<renderengine::LayerSettings> layerSettings;
194     renderengine::LayerSettings highlight;
195     for (const auto& layer : mLayers) {
196         const auto clientCompositionList =
197                 layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
198                         targetSettings);
199         layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
200                              clientCompositionList.cend());
201     }
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         layerSettings.push_back(blurLayerSettings);
218     }
219 
220     renderengine::LayerSettings holePunchSettings;
221     renderengine::LayerSettings holePunchBackgroundSettings;
222     if (mHolePunchLayer) {
223         auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE();
224         auto clientCompositionList = layerFE.prepareClientCompositionList(targetSettings);
225         // Assume that the final layer contains the buffer that we want to
226         // replace with a hole punch.
227         holePunchSettings = clientCompositionList.back();
228         // This mimics Layer::prepareClearClientComposition
229         holePunchSettings.source.buffer.buffer = nullptr;
230         holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
231         holePunchSettings.disableBlending = true;
232         holePunchSettings.alpha = 0.0f;
233         holePunchSettings.name =
234                 android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName());
235         layerSettings.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         layerSettings.emplace(layerSettings.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         layerSettings.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     constexpr bool kUseFramebufferCache = false;
278 
279     auto fenceResult =
280             toFenceResult(renderEngine
281                                   .drawLayers(displaySettings, layerSettings, texture->get(),
282                                               kUseFramebufferCache, std::move(bufferFence))
283                                   .get());
284 
285     if (fenceStatus(fenceResult) == NO_ERROR) {
286         mDrawFence = std::move(fenceResult).value_or(Fence::NO_FENCE);
287         mOutputSpace = outputState.framebufferSpace;
288         mTexture = texture;
289         mTexture->setReadyFence(mDrawFence);
290         mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation());
291         mOutputDataspace = outputDataspace;
292         mOrientation = orientation;
293         mSkipCount = 0;
294     } else {
295         mTexture.reset();
296     }
297 }
298 
requiresHolePunch() const299 bool CachedSet::requiresHolePunch() const {
300     // In order for the hole punch to be beneficial, the layer must be updating
301     // regularly, meaning  it should not have been merged with other layers.
302     if (getLayerCount() != 1) {
303         return false;
304     }
305 
306     // There is no benefit to a hole punch unless the layer has a buffer.
307     if (!mLayers[0].getBuffer()) {
308         return false;
309     }
310 
311     if (hasUnsupportedDataspace()) {
312         return false;
313     }
314 
315     const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
316     const auto* compositionState = layerFE.getCompositionState();
317     if (compositionState->forceClientComposition) {
318         return false;
319     }
320 
321     if (compositionState->blendMode != hal::BlendMode::NONE) {
322         return false;
323     }
324 
325     return layerFE.hasRoundedCorners();
326 }
327 
hasBlurBehind() const328 bool CachedSet::hasBlurBehind() const {
329     return std::any_of(mLayers.cbegin(), mLayers.cend(),
330                        [](const Layer& layer) { return layer.getState()->hasBlurBehind(); });
331 }
332 
333 namespace {
contains(const Rect & outer,const Rect & inner)334 bool contains(const Rect& outer, const Rect& inner) {
335     return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
336             outer.bottom >= inner.bottom;
337 }
338 }; // namespace
339 
addHolePunchLayerIfFeasible(const CachedSet & holePunchLayer,bool isFirstLayer)340 void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
341     // Verify that this CachedSet is opaque where the hole punch layer
342     // will draw.
343     const Rect& holePunchBounds = holePunchLayer.getBounds();
344     for (const auto& layer : mLayers) {
345         // The first layer is considered opaque because nothing is behind it.
346         // Note that isOpaque is always false for a layer with rounded
347         // corners, even if the interior is opaque. In theory, such a layer
348         // could be used for a hole punch, but this is unlikely to happen in
349         // practice.
350         const auto* outputLayer = layer.getState()->getOutputLayer();
351         if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
352             (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
353             mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
354             return;
355         }
356     }
357 }
358 
addBackgroundBlurLayer(const CachedSet & blurLayer)359 void CachedSet::addBackgroundBlurLayer(const CachedSet& blurLayer) {
360     mBlurLayer = blurLayer.getFirstLayer().getState();
361 }
362 
getHolePunchLayer() const363 compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
364     return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
365 }
366 
getBlurLayer() const367 compositionengine::OutputLayer* CachedSet::getBlurLayer() const {
368     return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
369 }
370 
hasUnsupportedDataspace() const371 bool CachedSet::hasUnsupportedDataspace() const {
372     return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
373         auto dataspace = layer.getState()->getDataspace();
374         const auto transfer = static_cast<ui::Dataspace>(dataspace & ui::Dataspace::TRANSFER_MASK);
375         if (transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG) {
376             // Skip HDR.
377             return true;
378         }
379 
380         if ((dataspace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT601_625) {
381             // RenderEngine does not match some DPUs, so skip
382             // to avoid flickering/color differences.
383             return true;
384         }
385         return false;
386     });
387 }
388 
hasProtectedLayers() const389 bool CachedSet::hasProtectedLayers() const {
390     return std::any_of(mLayers.cbegin(), mLayers.cend(),
391                        [](const Layer& layer) { return layer.getState()->isProtected(); });
392 }
393 
hasSolidColorLayers() const394 bool CachedSet::hasSolidColorLayers() const {
395     return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
396         return layer.getState()->hasSolidColorCompositionType();
397     });
398 }
399 
dump(std::string & result) const400 void CachedSet::dump(std::string& result) const {
401     const auto now = std::chrono::steady_clock::now();
402 
403     const auto lastUpdate =
404             std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
405     base::StringAppendF(&result, "  + Fingerprint %016zx, last update %sago, age %zd\n",
406                         mFingerprint, durationString(lastUpdate).c_str(), mAge);
407     {
408         const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
409         base::StringAppendF(&result, "    Override buffer: %p\n", b);
410     }
411     base::StringAppendF(&result, "    HolePunchLayer: %p\t%s\n", mHolePunchLayer,
412                         mHolePunchLayer
413                                 ? mHolePunchLayer->getOutputLayer()->getLayerFE().getDebugName()
414                                 : "");
415 
416     if (mLayers.size() == 1) {
417         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
418         if (auto* buffer = mLayers[0].getBuffer().get()) {
419             base::StringAppendF(&result, "    Buffer %p", buffer);
420             base::StringAppendF(&result, "    Format %s",
421                                 decodePixelFormat(buffer->getPixelFormat()).c_str());
422         }
423         base::StringAppendF(&result, "    Protected [%s]\n",
424                             mLayers[0].getState()->isProtected() ? "true" : "false");
425     } else {
426         result.append("    Cached set of:\n");
427         for (const Layer& layer : mLayers) {
428             base::StringAppendF(&result, "      Layer [%s]\n", layer.getName().c_str());
429             if (auto* buffer = layer.getBuffer().get()) {
430                 base::StringAppendF(&result, "       Buffer %p", buffer);
431                 base::StringAppendF(&result, "    Format[%s]",
432                                     decodePixelFormat(buffer->getPixelFormat()).c_str());
433             }
434             base::StringAppendF(&result, "       Protected [%s]\n",
435                                 layer.getState()->isProtected() ? "true" : "false");
436         }
437     }
438 
439     base::StringAppendF(&result, "    Creation cost: %zd\n", getCreationCost());
440     base::StringAppendF(&result, "    Display cost: %zd\n", getDisplayCost());
441 }
442 
443 } // namespace android::compositionengine::impl::planner
444