blob: bc5a6b8d48fe83ee979cddb846c33404c701cbd4 [file]
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <FrontEnd/LayerHierarchy.h>
#include <iterator>
#include <optional>
#include "FrontEnd/LayerSnapshot.h"
#include "Layer.h"
#include "LayerFE.h"
#include "ScreenCaptureOutput.h"
#include "renderengine/impl/ExternalTexture.h"
#include "MergeableHierarchy.h"
namespace android::surfaceflinger::frontend::caching {
void MergeableHierarchy::constructSnapshot(LayerSnapshotBuilder& builder,
const LayerSnapshotBuilder::Args& args,
compositionengine::CompositionEngine& compositionEngine,
std::unordered_map<uint32_t, sp<Layer>>& legacyLayers) {
SFTRACE_CALL();
if (mSnapshot) {
return;
}
if (!mRoot.hierarchy || !mRoot.hierarchy->getLayer()) {
return;
}
auto layer = mRoot.hierarchy->getLayer();
auto localArgs = args;
localArgs.forceUpdate = LayerSnapshotBuilder::ForceUpdateFlags::ALL;
localArgs.root = *mRoot.hierarchy;
auto cropRect = Rect(layer->getCroppedBufferSize(layer->getBufferSize(0)));
auto bounds = Rect(cropRect).offsetToOrigin();
if (!bounds.isEmpty()) {
localArgs.parentCrop = bounds.toFloatRect();
} else {
localArgs.parentCrop = std::nullopt;
}
builder.update(localArgs);
frontend::LayerSnapshot* rootSnapshot = builder.getSnapshot(layer->id);
auto transform = rootSnapshot->localTransform.inverse();
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
auto debugName = std::format("flattenedHierarchy{}", getId());
Rect snapshotBounds;
builder.forEachVisibleSnapshot([&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
if (!snapshot->hasSomethingToDraw()) {
return;
}
if (bounds.isEmpty() && !snapshot->croppedBufferSize.isEmpty()) {
if (!snapshotBounds.isValid()) {
snapshotBounds = Rect(snapshot->transformedBounds);
} else {
snapshotBounds.left =
std::min(snapshotBounds.left,
static_cast<int32_t>(snapshot->transformedBounds.left));
snapshotBounds.top =
std::min(snapshotBounds.top,
static_cast<int32_t>(snapshot->transformedBounds.top));
snapshotBounds.right =
std::max(snapshotBounds.right,
static_cast<int32_t>(snapshot->transformedBounds.right));
snapshotBounds.bottom =
std::max(snapshotBounds.bottom,
static_cast<int32_t>(snapshot->transformedBounds.bottom));
}
}
auto it = legacyLayers.find(static_cast<uint32_t>(snapshot->sequence));
Layer* legacyLayer = (it == legacyLayers.end()) ? nullptr : it->second.get();
sp<LayerFE> layerFE = sp<LayerFE>::make(snapshot->name);
layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
layerFE->mSnapshot->geomLayerTransform = transform * layerFE->mSnapshot->geomLayerTransform;
layerFE->mSnapshot->geomInverseLayerTransform =
layerFE->mSnapshot->geomLayerTransform.inverse();
layers.emplace_back(legacyLayer, std::move(layerFE));
});
if (layers.empty()) {
mSnapshot = nullptr;
return;
}
for (auto& [layer, layerFE] : layers) {
ftl::Future<FenceResult> futureFence = layerFE->createReleaseFenceFuture();
if (layer) {
layer->prepareReleaseCallbacks(std::move(futureFence), ui::UNASSIGNED_LAYER_STACK);
}
}
if (!bounds.isEmpty()) {
snapshotBounds = bounds;
}
if (snapshotBounds.getWidth() <= 0 || snapshotBounds.getHeight() <= 0) {
mSnapshot = nullptr;
return;
}
auto width = static_cast<uint32_t>(snapshotBounds.getWidth());
auto height = static_cast<uint32_t>(snapshotBounds.getHeight());
auto buffer = sp<GraphicBuffer>::make(width, height, PIXEL_FORMAT_RGBA_8888, 1u,
static_cast<uint64_t>(GRALLOC_USAGE_HW_COMPOSER |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_TEXTURE),
debugName);
auto status = buffer->initCheck();
if (status != OK) {
mSnapshot = nullptr;
return;
}
auto texture = std::make_shared<
renderengine::impl::
ExternalTexture>(buffer, compositionEngine.getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::WRITEABLE |
renderengine::impl::ExternalTexture::Usage::READABLE);
auto layerStack = layers.front().second->mSnapshot->outputFilter.layerStack;
std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
ScreenCaptureOutputArgs{.compositionEngine = compositionEngine,
.colorProfile = {},
.layerStack = layerStack,
.sourceCrop = snapshotBounds,
.buffer = texture,
.displayIdVariant = std::nullopt,
.reqBufferSize = ui::Size(width, height),
.sdrWhitePointNits = -1,
.displayBrightnessNits = -1,
.targetBrightness = -1,
.layerAlpha = 0.0f,
.disableBlur = false,
.treat170mAsSrgb = false,
.dimInGammaSpaceForEnhancedScreenshots = false,
.isSecure = true,
.enableLocalTonemapping = false,
.debugName = debugName});
std::vector<sp<compositionengine::LayerFE>> layerFes;
layerFes.reserve(layers.size());
for (auto& [layer, layerFE] : layers) {
layerFes.emplace_back(layerFE);
}
sp<LayerFE> firstLayer = layers.front().second;
compositionengine::CompositionRefreshArgs refreshArgs{
.outputs = {output},
.layers = std::move(layerFes),
.updatingOutputGeometryThisFrame = true,
.updatingGeometryThisFrame = true,
};
compositionEngine.present(refreshArgs);
mSnapshot = std::make_unique<LayerSnapshot>(*rootSnapshot);
mSnapshot->externalTexture = texture;
mSnapshot->acquireFence = output->getRenderSurface()->getClientTargetAcquireFence();
mSnapshot->buffer = texture->getBuffer();
mSnapshot->name.append(" (flattened)");
mSnapshot->debugName.append(" (flattened)");
}
void MergeableHierarchy::dump(std::ostream& out) const {
out << "{id = " << getId() << ", hasSnapshot=" << (mSnapshot != nullptr) << ", Last updated: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::nanoseconds(systemTime() - mRoot.lastUpdateTime))
.count()
<< " ms Ago}";
}
} // namespace android::surfaceflinger::frontend::caching