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 if (outputState.powerCallback) {
166 outputState.powerCallback->notifyCpuLoadUp();
167 }
168 const Rect& viewport = outputState.layerStackSpace.getContent();
169 const ui::Dataspace& outputDataspace = outputState.dataspace;
170 const ui::Transform::RotationFlags orientation =
171 ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
172
173 renderengine::DisplaySettings displaySettings{
174 .physicalDisplay = outputState.framebufferSpace.getContent(),
175 .clip = viewport,
176 .outputDataspace = outputDataspace,
177 .colorTransform = outputState.colorTransformMatrix,
178 .deviceHandlesColorTransform = deviceHandlesColorTransform,
179 .orientation = orientation,
180 .targetLuminanceNits = outputState.displayBrightnessNits,
181 };
182
183 LayerFE::ClientCompositionTargetSettings
184 targetSettings{.clip = Region(viewport),
185 .needsFiltering = false,
186 .isSecure = outputState.isSecure,
187 .supportsProtectedContent = false,
188 .viewport = viewport,
189 .dataspace = outputDataspace,
190 .realContentIsVisible = true,
191 .clearContent = false,
192 .blurSetting =
193 LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
194 .whitePointNits = outputState.displayBrightnessNits,
195 .treat170mAsSrgb = outputState.treat170mAsSrgb};
196
197 std::vector<renderengine::LayerSettings> layerSettings;
198 renderengine::LayerSettings highlight;
199 for (const auto& layer : mLayers) {
200 if (auto clientCompositionSettings =
201 layer.getState()->getOutputLayer()->getLayerFE().prepareClientComposition(
202 targetSettings)) {
203 layerSettings.push_back(std::move(*clientCompositionSettings));
204 }
205 }
206
207 renderengine::LayerSettings blurLayerSettings;
208 if (mBlurLayer) {
209 auto blurSettings = targetSettings;
210 blurSettings.blurSetting =
211 LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
212
213 auto blurLayerSettings =
214 mBlurLayer->getOutputLayer()->getLayerFE().prepareClientComposition(blurSettings);
215 // This mimics Layer::prepareClearClientComposition
216 blurLayerSettings->skipContentDraw = true;
217 blurLayerSettings->name = std::string("blur layer");
218 // Clear out the shadow settings
219 blurLayerSettings->shadow = {};
220 layerSettings.push_back(std::move(*blurLayerSettings));
221 }
222
223 if (mHolePunchLayer) {
224 auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE();
225
226 auto holePunchSettings = layerFE.prepareClientComposition(targetSettings);
227 // This mimics Layer::prepareClearClientComposition
228 holePunchSettings->source.buffer.buffer = nullptr;
229 holePunchSettings->source.solidColor = half3(0.0f, 0.0f, 0.0f);
230 holePunchSettings->disableBlending = true;
231 holePunchSettings->alpha = 0.0f;
232 holePunchSettings->name =
233 android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName());
234
235 // Add a solid background as the first layer in case there is no opaque
236 // buffer behind the punch hole
237 renderengine::LayerSettings holePunchBackgroundSettings;
238 holePunchBackgroundSettings.alpha = 1.0f;
239 holePunchBackgroundSettings.name = std::string("holePunchBackground");
240 holePunchBackgroundSettings.geometry.boundaries = holePunchSettings->geometry.boundaries;
241 holePunchBackgroundSettings.geometry.positionTransform =
242 holePunchSettings->geometry.positionTransform;
243 layerSettings.emplace(layerSettings.begin(), std::move(holePunchBackgroundSettings));
244
245 layerSettings.push_back(std::move(*holePunchSettings));
246 }
247
248 if (sDebugHighlighLayers) {
249 highlight = {
250 .geometry =
251 renderengine::Geometry{
252 .boundaries = FloatRect(0.0f, 0.0f,
253 static_cast<float>(mBounds.getWidth()),
254 static_cast<float>(mBounds.getHeight())),
255 },
256 .source =
257 renderengine::PixelSource{
258 .solidColor = half3(0.25f, 0.0f, 0.5f),
259 },
260 .alpha = half(0.05f),
261 };
262
263 layerSettings.emplace_back(highlight);
264 }
265
266 auto texture = texturePool.borrowTexture();
267 LOG_ALWAYS_FATAL_IF(texture->get()->getBuffer()->initCheck() != OK);
268
269 base::unique_fd bufferFence;
270 if (texture->getReadyFence()) {
271 // Bail out if the buffer is not ready, because there is some pending GPU work left.
272 if (texture->getReadyFence()->getStatus() != Fence::Status::Signaled) {
273 return;
274 }
275 bufferFence.reset(texture->getReadyFence()->dup());
276 }
277
278 constexpr bool kUseFramebufferCache = false;
279
280 auto fenceResult = 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 // TODO(b/274804887): temp fix of overdimming issue, skip caching if hsdr/sdr ratio > 1.01f
386 if (layer.getState()->getHdrSdrRatio() > 1.01f) {
387 return true;
388 }
389 return false;
390 });
391 }
392
hasProtectedLayers() const393 bool CachedSet::hasProtectedLayers() const {
394 return std::any_of(mLayers.cbegin(), mLayers.cend(),
395 [](const Layer& layer) { return layer.getState()->isProtected(); });
396 }
397
hasSolidColorLayers() const398 bool CachedSet::hasSolidColorLayers() const {
399 return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
400 return layer.getState()->hasSolidColorCompositionType();
401 });
402 }
403
cachingHintExcludesLayers() const404 bool CachedSet::cachingHintExcludesLayers() const {
405 const bool shouldExcludeLayers =
406 std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
407 return layer.getState()->getCachingHint() == gui::CachingHint::Disabled;
408 });
409
410 LOG_ALWAYS_FATAL_IF(shouldExcludeLayers && getLayerCount() > 1,
411 "CachedSet is invalid: should be excluded but contains %zu layers",
412 getLayerCount());
413 return shouldExcludeLayers;
414 }
415
dump(std::string & result) const416 void CachedSet::dump(std::string& result) const {
417 const auto now = std::chrono::steady_clock::now();
418
419 const auto lastUpdate =
420 std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
421 base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
422 mFingerprint, durationString(lastUpdate).c_str(), mAge);
423 {
424 const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
425 base::StringAppendF(&result, " Override buffer: %p\n", b);
426 }
427 base::StringAppendF(&result, " HolePunchLayer: %p\t%s\n", mHolePunchLayer,
428 mHolePunchLayer
429 ? mHolePunchLayer->getOutputLayer()->getLayerFE().getDebugName()
430 : "");
431
432 if (mLayers.size() == 1) {
433 base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
434 if (const sp<GraphicBuffer> buffer = mLayers[0].getState()->getBuffer().promote()) {
435 base::StringAppendF(&result, " Buffer %p", buffer.get());
436 base::StringAppendF(&result, " Format %s",
437 decodePixelFormat(buffer->getPixelFormat()).c_str());
438 }
439 base::StringAppendF(&result, " Protected [%s]\n",
440 mLayers[0].getState()->isProtected() ? "true" : "false");
441 } else {
442 result.append(" Cached set of:\n");
443 for (const Layer& layer : mLayers) {
444 base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str());
445 if (const sp<GraphicBuffer> buffer = layer.getState()->getBuffer().promote()) {
446 base::StringAppendF(&result, " Buffer %p", buffer.get());
447 base::StringAppendF(&result, " Format[%s]",
448 decodePixelFormat(buffer->getPixelFormat()).c_str());
449 }
450 base::StringAppendF(&result, " Protected [%s]\n",
451 layer.getState()->isProtected() ? "true" : "false");
452 }
453 }
454
455 base::StringAppendF(&result, " Creation cost: %zd\n", getCreationCost());
456 base::StringAppendF(&result, " Display cost: %zd\n", getDisplayCost());
457 }
458
459 } // namespace android::compositionengine::impl::planner
460