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