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