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 <ui/HdrRenderTypeUtils.h>
31 #include <utils/Trace.h>
32
33 namespace android::compositionengine::impl::planner {
34
35 const bool CachedSet::sDebugHighlighLayers =
36 base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
37
durationString(std::chrono::milliseconds duration)38 std::string durationString(std::chrono::milliseconds duration) {
39 using namespace std::chrono_literals;
40
41 std::string result;
42
43 if (duration >= 1h) {
44 const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
45 base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
46 duration -= hours;
47 }
48 if (duration >= 1min) {
49 const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
50 base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
51 duration -= minutes;
52 }
53 base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
54
55 return result;
56 }
57
Layer(const LayerState * state,std::chrono::steady_clock::time_point lastUpdate)58 CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
59 : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
60
CachedSet(const LayerState * layer,std::chrono::steady_clock::time_point lastUpdate)61 CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
62 : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
63 addLayer(layer, lastUpdate);
64 }
65
CachedSet(Layer layer)66 CachedSet::CachedSet(Layer layer)
67 : mFingerprint(layer.getHash()),
68 mLastUpdate(layer.getLastUpdate()),
69 mBounds(layer.getDisplayFrame()),
70 mVisibleRegion(layer.getVisibleRegion()) {
71 mLayers.emplace_back(std::move(layer));
72 }
73
addLayer(const LayerState * layer,std::chrono::steady_clock::time_point lastUpdate)74 void CachedSet::addLayer(const LayerState* layer,
75 std::chrono::steady_clock::time_point lastUpdate) {
76 mLayers.emplace_back(layer, lastUpdate);
77
78 Region boundingRegion;
79 boundingRegion.orSelf(mBounds);
80 boundingRegion.orSelf(layer->getDisplayFrame());
81 mBounds = boundingRegion.getBounds();
82 mVisibleRegion.orSelf(layer->getVisibleRegion());
83 }
84
getNonBufferHash() const85 NonBufferHash CachedSet::getNonBufferHash() const {
86 if (mLayers.size() == 1) {
87 return mFingerprint;
88 }
89
90 // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
91 // necessary (and therefore we need to match implementations).
92 size_t hash = 0;
93 android::hashCombineSingle(hash, mBounds);
94 android::hashCombineSingle(hash, mOutputDataspace);
95 android::hashCombineSingle(hash, mOrientation);
96 return hash;
97 }
98
getComponentDisplayCost() const99 size_t CachedSet::getComponentDisplayCost() const {
100 size_t displayCost = 0;
101
102 for (const Layer& layer : mLayers) {
103 displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
104 layer.getDisplayFrame().height());
105 }
106
107 return displayCost;
108 }
109
getCreationCost() const110 size_t CachedSet::getCreationCost() const {
111 if (mLayers.size() == 1) {
112 return 0;
113 }
114
115 // Reads
116 size_t creationCost = getComponentDisplayCost();
117
118 // Write - assumes that the output buffer only gets written once per pixel
119 creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
120
121 return creationCost;
122 }
123
getDisplayCost() const124 size_t CachedSet::getDisplayCost() const {
125 return static_cast<size_t>(mBounds.width() * mBounds.height());
126 }
127
hasBufferUpdate() const128 bool CachedSet::hasBufferUpdate() const {
129 for (const Layer& layer : mLayers) {
130 if (layer.getFramesSinceBufferUpdate() == 0) {
131 return true;
132 }
133 }
134 return false;
135 }
136
hasReadyBuffer() const137 bool CachedSet::hasReadyBuffer() const {
138 return mTexture && mDrawFence->getStatus() == Fence::Status::Signaled;
139 }
140
decompose() const141 std::vector<CachedSet> CachedSet::decompose() const {
142 std::vector<CachedSet> layers;
143
144 std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
145 [](Layer layer) { return CachedSet(std::move(layer)); });
146
147 return layers;
148 }
149
updateAge(std::chrono::steady_clock::time_point now)150 void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
151 LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
152 __func__);
153
154 if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
155 mLastUpdate = now;
156 mAge = 0;
157 }
158 }
159
render(renderengine::RenderEngine & renderEngine,TexturePool & texturePool,const OutputCompositionState & outputState,bool deviceHandlesColorTransform)160 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
161 const OutputCompositionState& outputState,
162 bool deviceHandlesColorTransform) {
163 ATRACE_CALL();
164 if (outputState.powerCallback) {
165 outputState.powerCallback->notifyCpuLoadUp();
166 }
167 const Rect& viewport = outputState.layerStackSpace.getContent();
168 const ui::Dataspace& outputDataspace = outputState.dataspace;
169 const ui::Transform::RotationFlags orientation =
170 ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
171
172 renderengine::DisplaySettings displaySettings{
173 .physicalDisplay = outputState.framebufferSpace.getContent(),
174 .clip = viewport,
175 .outputDataspace = outputDataspace,
176 .colorTransform = outputState.colorTransformMatrix,
177 .deviceHandlesColorTransform = deviceHandlesColorTransform,
178 .orientation = orientation,
179 .targetLuminanceNits = outputState.displayBrightnessNits,
180 };
181
182 LayerFE::ClientCompositionTargetSettings
183 targetSettings{.clip = Region(viewport),
184 .needsFiltering = false,
185 .isSecure = outputState.isSecure,
186 .isProtected = false,
187 .viewport = viewport,
188 .dataspace = outputDataspace,
189 .realContentIsVisible = true,
190 .clearContent = false,
191 .blurSetting =
192 LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
193 .whitePointNits = outputState.displayBrightnessNits,
194 .treat170mAsSrgb = outputState.treat170mAsSrgb};
195
196 std::vector<renderengine::LayerSettings> layerSettings;
197 renderengine::LayerSettings highlight;
198 for (const auto& layer : mLayers) {
199 if (auto clientCompositionSettings =
200 layer.getState()->getOutputLayer()->getLayerFE().prepareClientComposition(
201 targetSettings)) {
202 layerSettings.push_back(std::move(*clientCompositionSettings));
203 }
204 }
205
206 renderengine::LayerSettings blurLayerSettings;
207 if (mBlurLayer) {
208 auto blurSettings = targetSettings;
209 blurSettings.blurSetting =
210 LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
211
212 auto blurLayerSettings =
213 mBlurLayer->getOutputLayer()->getLayerFE().prepareClientComposition(blurSettings);
214 // This mimics Layer::prepareClearClientComposition
215 blurLayerSettings->skipContentDraw = true;
216 blurLayerSettings->name = std::string("blur layer");
217 // Clear out the shadow settings
218 blurLayerSettings->shadow = {};
219 layerSettings.push_back(std::move(*blurLayerSettings));
220 }
221
222 if (mHolePunchLayer) {
223 auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE();
224
225 auto holePunchSettings = layerFE.prepareClientComposition(targetSettings);
226 // This mimics Layer::prepareClearClientComposition
227 holePunchSettings->source.buffer.buffer = nullptr;
228 holePunchSettings->source.solidColor = half3(0.0f, 0.0f, 0.0f);
229 holePunchSettings->disableBlending = true;
230 holePunchSettings->alpha = 0.0f;
231 holePunchSettings->name =
232 android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName());
233
234 // Add a solid background as the first layer in case there is no opaque
235 // buffer behind the punch hole
236 renderengine::LayerSettings holePunchBackgroundSettings;
237 holePunchBackgroundSettings.alpha = 1.0f;
238 holePunchBackgroundSettings.name = std::string("holePunchBackground");
239 holePunchBackgroundSettings.geometry.boundaries = holePunchSettings->geometry.boundaries;
240 holePunchBackgroundSettings.geometry.positionTransform =
241 holePunchSettings->geometry.positionTransform;
242 layerSettings.emplace(layerSettings.begin(), std::move(holePunchBackgroundSettings));
243
244 layerSettings.push_back(std::move(*holePunchSettings));
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 auto fenceResult = renderEngine
278 .drawLayers(displaySettings, layerSettings, texture->get(),
279 std::move(bufferFence))
280 .get();
281
282 if (fenceStatus(fenceResult) == NO_ERROR) {
283 mDrawFence = std::move(fenceResult).value_or(Fence::NO_FENCE);
284 mOutputSpace = outputState.framebufferSpace;
285 mTexture = texture;
286 mTexture->setReadyFence(mDrawFence);
287 mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation());
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 (hasKnownColorShift()) {
309 return false;
310 }
311
312 const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
313 const auto* compositionState = layerFE.getCompositionState();
314 if (compositionState->forceClientComposition) {
315 return false;
316 }
317
318 if (compositionState->blendMode != hal::BlendMode::NONE) {
319 return false;
320 }
321
322 return layerFE.hasRoundedCorners();
323 }
324
hasBlurBehind() const325 bool CachedSet::hasBlurBehind() const {
326 return std::any_of(mLayers.cbegin(), mLayers.cend(),
327 [](const Layer& layer) { return layer.getState()->hasBlurBehind(); });
328 }
329
330 namespace {
contains(const Rect & outer,const Rect & inner)331 bool contains(const Rect& outer, const Rect& inner) {
332 return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
333 outer.bottom >= inner.bottom;
334 }
335 }; // namespace
336
addHolePunchLayerIfFeasible(const CachedSet & holePunchLayer,bool isFirstLayer)337 void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
338 // Verify that this CachedSet is opaque where the hole punch layer
339 // will draw.
340 const Rect& holePunchBounds = holePunchLayer.getBounds();
341 for (const auto& layer : mLayers) {
342 // The first layer is considered opaque because nothing is behind it.
343 // Note that isOpaque is always false for a layer with rounded
344 // corners, even if the interior is opaque. In theory, such a layer
345 // could be used for a hole punch, but this is unlikely to happen in
346 // practice.
347 const auto* outputLayer = layer.getState()->getOutputLayer();
348 if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
349 (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
350 mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
351 return;
352 }
353 }
354 }
355
addBackgroundBlurLayer(const CachedSet & blurLayer)356 void CachedSet::addBackgroundBlurLayer(const CachedSet& blurLayer) {
357 mBlurLayer = blurLayer.getFirstLayer().getState();
358 }
359
getHolePunchLayer() const360 compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
361 return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
362 }
363
getBlurLayer() const364 compositionengine::OutputLayer* CachedSet::getBlurLayer() const {
365 return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
366 }
367
hasKnownColorShift() const368 bool CachedSet::hasKnownColorShift() const {
369 return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
370 auto dataspace = layer.getState()->getDataspace();
371
372 // Layers are never dimmed when rendering a cached set, meaning that we may ask HWC to
373 // dim a cached set. But this means that we can never cache any HDR layers so that we
374 // don't accidentally dim those layers.
375 const auto hdrType = getHdrRenderType(dataspace, layer.getState()->getPixelFormat(),
376 layer.getState()->getHdrSdrRatio());
377 if (hdrType != HdrRenderType::SDR) {
378 return true;
379 }
380
381 // Layers that have dimming disabled pretend that they're HDR.
382 if (!layer.getState()->isDimmingEnabled()) {
383 return true;
384 }
385
386 if ((dataspace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT601_625) {
387 // RenderEngine does not match some DPUs, so skip
388 // to avoid flickering/color differences.
389 return true;
390 }
391 return false;
392 });
393 }
394
hasProtectedLayers() const395 bool CachedSet::hasProtectedLayers() const {
396 return std::any_of(mLayers.cbegin(), mLayers.cend(),
397 [](const Layer& layer) { return layer.getState()->isProtected(); });
398 }
399
cachingHintExcludesLayers() const400 bool CachedSet::cachingHintExcludesLayers() const {
401 const bool shouldExcludeLayers =
402 std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
403 return layer.getState()->getCachingHint() == gui::CachingHint::Disabled;
404 });
405
406 LOG_ALWAYS_FATAL_IF(shouldExcludeLayers && getLayerCount() > 1,
407 "CachedSet is invalid: should be excluded but contains %zu layers",
408 getLayerCount());
409 return shouldExcludeLayers;
410 }
411
dump(std::string & result) const412 void CachedSet::dump(std::string& result) const {
413 const auto now = std::chrono::steady_clock::now();
414
415 const auto lastUpdate =
416 std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
417 base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
418 mFingerprint, durationString(lastUpdate).c_str(), mAge);
419 {
420 const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr;
421 base::StringAppendF(&result, " Override buffer: %p\n", b);
422 }
423 base::StringAppendF(&result, " HolePunchLayer: %p\t%s\n", mHolePunchLayer,
424 mHolePunchLayer
425 ? mHolePunchLayer->getOutputLayer()->getLayerFE().getDebugName()
426 : "");
427
428 if (mLayers.size() == 1) {
429 base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
430 if (const sp<GraphicBuffer> buffer = mLayers[0].getState()->getBuffer().promote()) {
431 base::StringAppendF(&result, " Buffer %p", buffer.get());
432 base::StringAppendF(&result, " Format %s",
433 decodePixelFormat(buffer->getPixelFormat()).c_str());
434 }
435 base::StringAppendF(&result, " Protected [%s]\n",
436 mLayers[0].getState()->isProtected() ? "true" : "false");
437 } else {
438 result.append(" Cached set of:\n");
439 for (const Layer& layer : mLayers) {
440 base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str());
441 if (const sp<GraphicBuffer> buffer = layer.getState()->getBuffer().promote()) {
442 base::StringAppendF(&result, " Buffer %p", buffer.get());
443 base::StringAppendF(&result, " Format[%s]",
444 decodePixelFormat(buffer->getPixelFormat()).c_str());
445 }
446 base::StringAppendF(&result, " Protected [%s]\n",
447 layer.getState()->isProtected() ? "true" : "false");
448 }
449 }
450
451 base::StringAppendF(&result, " Creation cost: %zd\n", getCreationCost());
452 base::StringAppendF(&result, " Display cost: %zd\n", getDisplayCost());
453 }
454
455 } // namespace android::compositionengine::impl::planner
456