SF: Merge BufferStateLayer into Layer
Test: go/wm-smoke
Bug: 238781169
Change-Id: I1521ffa6995ce41a04a664ff65516431aead4dca
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 930ae72..b692720 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -29,6 +29,7 @@
#include <android-base/stringprintf.h>
#include <android/native_window.h>
#include <binder/IPCThreadState.h>
+#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
@@ -39,8 +40,10 @@
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <gui/BufferItem.h>
+#include <gui/GLConsumer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
+#include <gui/TraceUtils.h>
#include <math.h>
#include <private/android_filesystem_config.h>
#include <renderengine/RenderEngine.h>
@@ -62,7 +65,6 @@
#include <mutex>
#include <sstream>
-#include "Colorizer.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
#include "EffectLayer.h"
@@ -74,11 +76,72 @@
#include "TunnelModeEnabledReporter.h"
#define DEBUG_RESIZE 0
+#define EARLY_RELEASE_ENABLED false
namespace android {
namespace {
constexpr int kDumpTableRowLength = 159;
+
+static constexpr float defaultMaxLuminance = 1000.0;
+
const ui::Transform kIdentityTransform;
+
+constexpr mat4 inverseOrientation(uint32_t transform) {
+ const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ mat4 tr;
+
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ tr = tr * rot90;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ tr = tr * flipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ tr = tr * flipV;
+ }
+ return inverse(tr);
+}
+
+bool assignTransform(ui::Transform* dst, ui::Transform& from) {
+ if (*dst == from) {
+ return false;
+ }
+ *dst = from;
+ return true;
+}
+
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+ using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+ using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+ const auto frameRateCompatibility = [frameRate] {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return FrameRateCompatibility::Default;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return FrameRateCompatibility::ExactOrMultiple;
+ default:
+ return FrameRateCompatibility::Undefined;
+ }
+ }();
+
+ const auto seamlessness = [frameRate] {
+ switch (frameRate.seamlessness) {
+ case scheduler::Seamlessness::OnlySeamless:
+ return Seamlessness::ShouldBeSeamless;
+ case scheduler::Seamlessness::SeamedAndSeamless:
+ return Seamlessness::NotRequired;
+ default:
+ return Seamlessness::Undefined;
+ }
+ }();
+
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ .frameRateCompatibility = frameRateCompatibility,
+ .seamlessness = seamlessness};
+}
+
} // namespace
using namespace ftl::flag_operators;
@@ -100,7 +163,12 @@
mWindowType(static_cast<WindowInfo::Type>(
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
mLayerCreationFlags(args.flags),
- mBorderEnabled(false) {
+ mBorderEnabled(false),
+ mTextureName(args.textureName),
+ mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()},
+ mHwcSlotGenerator(sp<HwcSlotGenerator>::make()) {
+ ALOGV("Creating Layer %s", getDebugName());
+
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -166,6 +234,11 @@
mOwnerUid = mCallingUid;
mOwnerPid = mCallingPid;
}
+
+ mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+ mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+ mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
+ mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
}
void Layer::onFirstRef() {
@@ -173,6 +246,28 @@
}
Layer::~Layer() {
+ // The original layer and the clone layer share the same texture and buffer. Therefore, only
+ // one of the layers, in this case the original layer, needs to handle the deletion. The
+ // original layer and the clone should be removed at the same time so there shouldn't be any
+ // issue with the clone layer trying to use the texture.
+ if (mBufferInfo.mBuffer != nullptr) {
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+ mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
+ mBufferInfo.mFence,
+ mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+ mOwnerUid));
+ }
+ if (!isClone()) {
+ // The original layer and the clone layer share the same texture. Therefore, only one of
+ // the layers, in this case the original layer, needs to handle the deletion. The original
+ // layer and the clone should be removed at the same time so there shouldn't be any issue
+ // with the clone layer trying to use the deleted texture.
+ mFlinger->deleteTextureAsync(mTextureName);
+ }
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->onDestroy(layerId);
+ mFlinger->mFrameTracer->onDestroy(layerId);
+
sp<Client> c(mClientRef.promote());
if (c != 0) {
c->detachLayer(this);
@@ -493,6 +588,46 @@
// Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
// LayerFECompositionState where it would be visible to Flattener.
compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
+
+ if (hasBufferOrSidebandStream()) {
+ preparePerFrameBufferCompositionState();
+ } else {
+ preparePerFrameEffectsCompositionState();
+ }
+}
+
+void Layer::preparePerFrameBufferCompositionState() {
+ // Sideband layers
+ auto* compositionState = editCompositionState();
+ if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
+ return;
+ } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ } else {
+ // Normal buffer layers
+ compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
+ compositionState->compositionType = mPotentialCursor
+ ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+ : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
+ }
+
+ compositionState->buffer = getBuffer();
+ compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+ ? 0
+ : mBufferInfo.mBufferSlot;
+ compositionState->acquireFence = mBufferInfo.mFence;
+ compositionState->frameNumber = mBufferInfo.mFrameNumber;
+ compositionState->sidebandStreamHasFrame = false;
+}
+
+void Layer::preparePerFrameEffectsCompositionState() {
+ auto* compositionState = editCompositionState();
+ compositionState->color = getColor();
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
}
void Layer::prepareCursorCompositionState() {
@@ -549,6 +684,29 @@
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+ prepareClientCompositionInternal(targetSettings);
+ // Nothing to render.
+ if (!layerSettings) {
+ return {};
+ }
+
+ // HWC requests to clear this layer.
+ if (targetSettings.clearContent) {
+ prepareClearClientComposition(*layerSettings, false /* blackout */);
+ return layerSettings;
+ }
+
+ // set the shadow for the layer if needed
+ prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+
+ return layerSettings;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionInternal(
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ ATRACE_CALL();
+
if (!getCompositionState()) {
return {};
}
@@ -610,6 +768,13 @@
layerSettings.stretchEffect = getStretchEffect();
// Record the name of the layer for debugging further down the stack.
layerSettings.name = getName();
+
+ if (hasEffect()) {
+ prepareEffectsClientComposition(layerSettings, targetSettings);
+ return layerSettings;
+ }
+
+ prepareBufferStateClientComposition(layerSettings, targetSettings);
return layerSettings;
}
@@ -626,6 +791,132 @@
layerSettings.name = getName();
}
+void Layer::prepareEffectsClientComposition(
+ compositionengine::LayerFE::LayerSettings& layerSettings,
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+ if (targetSettings.realContentIsVisible && fillsColor()) {
+ // Set color for color fill settings.
+ layerSettings.source.solidColor = getColor().rgb;
+ } else if (hasBlur() || drawShadows()) {
+ layerSettings.skipContentDraw = true;
+ }
+}
+
+void Layer::prepareBufferStateClientComposition(
+ compositionengine::LayerFE::LayerSettings& layerSettings,
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
+ // For surfaceview of tv sideband, there is no activeBuffer
+ // in bufferqueue, we need return LayerSettings.
+ return;
+ }
+ const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+ ((isSecure() || isProtected()) && !targetSettings.isSecure);
+ const bool bufferCanBeUsedAsHwTexture =
+ mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+ if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+ ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+ mName.c_str());
+ prepareClearClientComposition(layerSettings, true /* blackout */);
+ return;
+ }
+
+ const State& s(getDrawingState());
+ layerSettings.source.buffer.buffer = mBufferInfo.mBuffer;
+ layerSettings.source.buffer.isOpaque = isOpaque(s);
+ layerSettings.source.buffer.fence = mBufferInfo.mFence;
+ layerSettings.source.buffer.textureName = mTextureName;
+ layerSettings.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+ layerSettings.source.buffer.isY410BT2020 = isHdrY410();
+ bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ float maxLuminance = 0.f;
+ if (hasSmpte2086 && hasCta861_3) {
+ maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
+ mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
+ } else if (hasSmpte2086) {
+ maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
+ } else if (hasCta861_3) {
+ maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
+ } else {
+ switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // Behavior-match previous releases for HDR content
+ maxLuminance = defaultMaxLuminance;
+ break;
+ }
+ }
+ layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
+ layerSettings.frameNumber = mCurrentFrameNumber;
+ layerSettings.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
+
+ const bool useFiltering =
+ targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
+
+ // Query the texture matrix given our current filtering mode.
+ float textureMatrix[16];
+ getDrawingTransformMatrix(useFiltering, textureMatrix);
+
+ if (getTransformToDisplayInverse()) {
+ /*
+ * the code below applies the primary display's inverse transform to
+ * the texture transform
+ */
+ uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ mat4 tr = inverseOrientation(transform);
+
+ /**
+ * TODO(b/36727915): This is basically a hack.
+ *
+ * Ensure that regardless of the parent transformation,
+ * this buffer is always transformed from native display
+ * orientation to display orientation. For example, in the case
+ * of a camera where the buffer remains in native orientation,
+ * we want the pixels to always be upright.
+ */
+ sp<Layer> p = mDrawingParent.promote();
+ if (p != nullptr) {
+ const auto parentTransform = p->getTransform();
+ tr = tr * inverseOrientation(parentTransform.getOrientation());
+ }
+
+ // and finally apply it to the original texture matrix
+ const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+ memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+ }
+
+ const Rect win{getBounds()};
+ float bufferWidth = getBufferSize(s).getWidth();
+ float bufferHeight = getBufferSize(s).getHeight();
+
+ // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+ // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+ // ignore them.
+ if (!getBufferSize(s).isValid()) {
+ bufferWidth = float(win.right) - float(win.left);
+ bufferHeight = float(win.bottom) - float(win.top);
+ }
+
+ const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+ const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+ const float translateY = float(win.top) / bufferHeight;
+ const float translateX = float(win.left) / bufferWidth;
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
+ mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
+ mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
+ mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
+
+ layerSettings.source.buffer.useTextureFiltering = useFiltering;
+ layerSettings.source.buffer.textureTransform =
+ mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+ return;
+}
+
aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
@@ -2415,6 +2706,40 @@
void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
cloneDrawingState(clonedFrom.get());
mClonedFrom = clonedFrom;
+ mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha;
+ mPotentialCursor = clonedFrom->mPotentialCursor;
+ mProtectedByApp = clonedFrom->mProtectedByApp;
+ updateCloneBufferInfo();
+}
+
+void Layer::updateCloneBufferInfo() {
+ if (!isClone() || !isClonedFromAlive()) {
+ return;
+ }
+
+ sp<Layer> clonedFrom = getClonedFrom();
+ mBufferInfo = clonedFrom->mBufferInfo;
+ mSidebandStream = clonedFrom->mSidebandStream;
+ surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+ mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+ mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+ // After buffer info is updated, the drawingState from the real layer needs to be copied into
+ // the cloned. This is because some properties of drawingState can change when latchBuffer is
+ // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+ // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+ // the cloned drawingState again.
+ wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+ SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+ wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+ WindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
+ cloneDrawingState(clonedFrom.get());
+
+ mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
+ mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+ mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+ mDrawingState.inputInfo = tmpInputInfo;
}
void Layer::updateMirrorInfo() {
@@ -2622,6 +2947,1299 @@
mDrawingState.callbackHandles = {};
}
+void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+ const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+ const sp<Fence>& releaseFence,
+ uint32_t currentMaxAcquiredBufferCount) {
+ if (!listener) {
+ return;
+ }
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
+ listener->onReleaseBuffer({buffer->getId(), framenumber},
+ releaseFence ? releaseFence : Fence::NO_FENCE,
+ currentMaxAcquiredBufferCount);
+}
+
+void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
+ // If we are displayed on multiple displays in a single composition cycle then we would
+ // need to do careful tracking to enable the use of the mLastClientCompositionFence.
+ // For example we can only use it if all the displays are client comp, and we need
+ // to merge all the client comp fences. We could do this, but for now we just
+ // disable the optimization when a layer is composed on multiple displays.
+ if (mClearClientCompositionFenceOnLayerDisplayed) {
+ mLastClientCompositionFence = nullptr;
+ } else {
+ mClearClientCompositionFenceOnLayerDisplayed = true;
+ }
+
+ // The previous release fence notifies the client that SurfaceFlinger is done with the previous
+ // buffer that was presented on this layer. The first transaction that came in this frame that
+ // replaced the previous buffer on this layer needs this release fence, because the fence will
+ // let the client know when that previous buffer is removed from the screen.
+ //
+ // Every other transaction on this layer does not need a release fence because no other
+ // Transactions that were set on this layer this frame are going to have their preceding buffer
+ // removed from the display this frame.
+ //
+ // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
+ // buffer so it doesn't need a previous release fence because the layer still needs the previous
+ // buffer. The second transaction contains a buffer so it needs a previous release fence because
+ // the previous buffer will be released this frame. The third transaction also contains a
+ // buffer. It replaces the buffer in the second transaction. The buffer in the second
+ // transaction will now no longer be presented so it is released immediately and the third
+ // transaction doesn't need a previous release fence.
+ sp<CallbackHandle> ch;
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->releasePreviousBuffer &&
+ mDrawingState.releaseBufferEndpoint == handle->listener) {
+ ch = handle;
+ break;
+ }
+ }
+
+ // Prevent tracing the same release multiple times.
+ if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+ mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+ }
+
+ if (ch != nullptr) {
+ ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+ ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
+ ch->name = mName;
+ }
+}
+
+void Layer::onSurfaceFrameCreated(
+ const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+ while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
+ // Too many SurfaceFrames pending classification. The front of the deque is probably not
+ // tracked by FrameTimeline and will never be presented. This will only result in a memory
+ // leak.
+ ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+ mName.c_str());
+ std::string miniDump = mPendingJankClassifications.front()->miniDump();
+ ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+ mPendingJankClassifications.pop_front();
+ }
+ mPendingJankClassifications.emplace_back(surfaceFrame);
+}
+
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+ for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->transformHint = mTransformHint;
+ handle->dequeueReadyTime = dequeueReadyTime;
+ handle->currentMaxAcquiredBufferCount =
+ mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
+ ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
+ handle->previousReleaseCallbackId.framenumber);
+ }
+
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->releasePreviousBuffer &&
+ mDrawingState.releaseBufferEndpoint == handle->listener) {
+ handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+ break;
+ }
+ }
+
+ std::vector<JankData> jankData;
+ jankData.reserve(mPendingJankClassifications.size());
+ while (!mPendingJankClassifications.empty() &&
+ mPendingJankClassifications.front()->getJankType()) {
+ std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
+ mPendingJankClassifications.front();
+ mPendingJankClassifications.pop_front();
+ jankData.emplace_back(
+ JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+ }
+
+ mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles,
+ jankData);
+
+ sp<Fence> releaseFence = Fence::NO_FENCE;
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->releasePreviousBuffer &&
+ mDrawingState.releaseBufferEndpoint == handle->listener) {
+ releaseFence =
+ handle->previousReleaseFence ? handle->previousReleaseFence : Fence::NO_FENCE;
+ break;
+ }
+ }
+
+ mDrawingState.callbackHandles = {};
+}
+
+bool Layer::willPresentCurrentTransaction() const {
+ // Returns true if the most recent Transaction applied to CurrentState will be presented.
+ return (getSidebandStreamChanged() || getAutoRefresh() ||
+ (mDrawingState.modified &&
+ (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
+}
+
+bool Layer::setTransform(uint32_t transform) {
+ if (mDrawingState.bufferTransform == transform) return false;
+ mDrawingState.bufferTransform = transform;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
+ if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
+ mDrawingState.sequence++;
+ mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setBufferCrop(const Rect& bufferCrop) {
+ if (mDrawingState.bufferCrop == bufferCrop) return false;
+
+ mDrawingState.sequence++;
+ mDrawingState.bufferCrop = bufferCrop;
+
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setDestinationFrame(const Rect& destinationFrame) {
+ if (mDrawingState.destinationFrame == destinationFrame) return false;
+
+ mDrawingState.sequence++;
+ mDrawingState.destinationFrame = destinationFrame;
+
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+bool Layer::updateGeometry() {
+ if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
+ mDrawingState.destinationFrame.isEmpty()) {
+ // If destination frame is not set, use the requested transform set via
+ // Layer::setPosition and Layer::setMatrix.
+ return assignTransform(&mDrawingState.transform, mRequestedTransform);
+ }
+
+ Rect destRect = mDrawingState.destinationFrame;
+ int32_t destW = destRect.width();
+ int32_t destH = destRect.height();
+ if (destRect.left < 0) {
+ destRect.left = 0;
+ destRect.right = destW;
+ }
+ if (destRect.top < 0) {
+ destRect.top = 0;
+ destRect.bottom = destH;
+ }
+
+ if (!mDrawingState.buffer) {
+ ui::Transform t;
+ t.set(destRect.left, destRect.top);
+ return assignTransform(&mDrawingState.transform, t);
+ }
+
+ uint32_t bufferWidth = mDrawingState.buffer->getWidth();
+ uint32_t bufferHeight = mDrawingState.buffer->getHeight();
+ // Undo any transformations on the buffer.
+ if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (mDrawingState.transformToDisplayInverse) {
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ float sx = destW / static_cast<float>(bufferWidth);
+ float sy = destH / static_cast<float>(bufferHeight);
+ ui::Transform t;
+ t.set(sx, 0, 0, sy);
+ t.set(destRect.left, destRect.top);
+ return assignTransform(&mDrawingState.transform, t);
+}
+
+bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
+ if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+ mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
+ return false;
+ }
+
+ mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+
+ mDrawingState.sequence++;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ return true;
+}
+
+bool Layer::setPosition(float x, float y) {
+ if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
+ return false;
+ }
+
+ mRequestedTransform.set(x, y);
+
+ mDrawingState.sequence++;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ return true;
+}
+
+bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
+ bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
+ const FrameTimelineInfo& info) {
+ ATRACE_CALL();
+
+ if (!buffer) {
+ return false;
+ }
+
+ const bool frameNumberChanged =
+ bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
+ const uint64_t frameNumber =
+ frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
+
+ if (mDrawingState.buffer) {
+ mReleasePreviousBuffer = true;
+ if (!mBufferInfo.mBuffer ||
+ (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
+ mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
+ // If mDrawingState has a buffer, and we are about to update again
+ // before swapping to drawing state, then the first buffer will be
+ // dropped and we should decrement the pending buffer count and
+ // call any release buffer callbacks if set.
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+ mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+ mDrawingState.acquireFence,
+ mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+ mOwnerUid));
+ decrementPendingBufferCount();
+ if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
+ mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
+ addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
+ mDrawingState.bufferSurfaceFrameTX.reset();
+ }
+ } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+ mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+ mLastClientCompositionFence,
+ mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+ mOwnerUid));
+ mLastClientCompositionFence = nullptr;
+ }
+ }
+
+ mDrawingState.frameNumber = frameNumber;
+ mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
+ mDrawingState.buffer = std::move(buffer);
+ mDrawingState.clientCacheId = bufferData.cachedBuffer;
+
+ mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
+ ? bufferData.acquireFence
+ : Fence::NO_FENCE;
+ mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+ if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+ // We latched this buffer unsiganled, so we need to pass the acquire fence
+ // on the callback instead of just the acquire time, since it's unknown at
+ // this point.
+ mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence;
+ } else {
+ mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
+ }
+
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
+ mOwnerUid, postTime, getGameMode());
+ mDrawingState.desiredPresentTime = desiredPresentTime;
+ mDrawingState.isAutoTimestamp = isAutoTimestamp;
+
+ const nsecs_t presentTime = [&] {
+ if (!isAutoTimestamp) return desiredPresentTime;
+
+ const auto prediction =
+ mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
+ if (prediction.has_value()) return prediction->presentTime;
+
+ return static_cast<nsecs_t>(0);
+ }();
+
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+
+ setFrameTimelineVsyncForBufferTransaction(info, postTime);
+
+ if (dequeueTime && *dequeueTime != 0) {
+ const uint64_t bufferId = mDrawingState.buffer->getId();
+ mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+ FrameTracer::FrameEvent::DEQUEUE);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
+ FrameTracer::FrameEvent::QUEUE);
+ }
+
+ mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
+ return true;
+}
+
+bool Layer::setDataspace(ui::Dataspace dataspace) {
+ mDrawingState.dataspaceRequested = true;
+ if (mDrawingState.dataspace == dataspace) return false;
+ mDrawingState.dataspace = dataspace;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
+ if (mDrawingState.hdrMetadata == hdrMetadata) return false;
+ mDrawingState.hdrMetadata = hdrMetadata;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+ mDrawingState.surfaceDamageRegion = surfaceDamage;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setApi(int32_t api) {
+ if (mDrawingState.api == api) return false;
+ mDrawingState.api = api;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+ if (mDrawingState.sidebandStream == sidebandStream) return false;
+
+ if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
+ mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+ } else if (sidebandStream != nullptr) {
+ mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
+ }
+
+ mDrawingState.sidebandStream = sidebandStream;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ if (!mSidebandStreamChanged.exchange(true)) {
+ // mSidebandStreamChanged was false
+ mFlinger->onLayerUpdate();
+ }
+ return true;
+}
+
+bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) {
+ // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+ if (handles.empty()) {
+ mReleasePreviousBuffer = false;
+ return false;
+ }
+
+ const bool willPresent = willPresentCurrentTransaction();
+
+ for (const auto& handle : handles) {
+ // If this transaction set a buffer on this layer, release its previous buffer
+ handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+ // If this layer will be presented in this frame
+ if (willPresent) {
+ // If this transaction set an acquire fence on this layer, set its acquire time
+ handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
+ handle->frameNumber = mDrawingState.frameNumber;
+
+ // Store so latched time and release fence can be set
+ mDrawingState.callbackHandles.push_back(handle);
+
+ } else { // If this layer will NOT need to be relatched and presented this frame
+ // Notify the transaction completed thread this handle is done
+ mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+ }
+ }
+
+ mReleasePreviousBuffer = false;
+ mCallbackHandleAcquireTimeOrFence = -1;
+
+ return willPresent;
+}
+
+Rect Layer::getBufferSize(const State& /*s*/) const {
+ // for buffer state layers we use the display frame size as the buffer size.
+
+ if (mBufferInfo.mBuffer == nullptr) {
+ return Rect::INVALID_RECT;
+ }
+
+ uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
+}
+
+FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const {
+ if (mBufferInfo.mBuffer == nullptr) {
+ return parentBounds;
+ }
+
+ return getBufferSize(getDrawingState()).toFloatRect();
+}
+
+bool Layer::fenceHasSignaled() const {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
+ return true;
+ }
+
+ const bool fenceSignaled =
+ getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+ if (!fenceSignaled) {
+ mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+ TimeStats::LatchSkipReason::LateAcquire);
+ }
+
+ return fenceSignaled;
+}
+
+bool Layer::onPreComposition(nsecs_t refreshStartTime) {
+ for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->refreshStartTime = refreshStartTime;
+ }
+ return hasReadyFrame();
+}
+
+void Layer::setAutoRefresh(bool autoRefresh) {
+ mDrawingState.autoRefresh = autoRefresh;
+}
+
+bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) {
+ // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+ editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
+
+ if (mSidebandStreamChanged.exchange(false)) {
+ const State& s(getDrawingState());
+ // mSidebandStreamChanged was true
+ mSidebandStream = s.sidebandStream;
+ editCompositionState()->sidebandStream = mSidebandStream;
+ if (mSidebandStream != nullptr) {
+ setTransactionFlags(eTransactionNeeded);
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ }
+ recomputeVisibleRegions = true;
+
+ return true;
+ }
+ return false;
+}
+
+bool Layer::hasFrameUpdate() const {
+ const State& c(getDrawingState());
+ return (mDrawingStateModified || mDrawingState.modified) &&
+ (c.buffer != nullptr || c.bgColorLayer != nullptr);
+}
+
+void Layer::updateTexImage(nsecs_t latchTime) {
+ const State& s(getDrawingState());
+
+ if (!s.buffer) {
+ if (s.bgColorLayer) {
+ for (auto& handle : mDrawingState.callbackHandles) {
+ handle->latchTime = latchTime;
+ }
+ }
+ return;
+ }
+
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->frameNumber == mDrawingState.frameNumber) {
+ handle->latchTime = latchTime;
+ }
+ }
+
+ const int32_t layerId = getSequence();
+ const uint64_t bufferId = mDrawingState.buffer->getId();
+ const uint64_t frameNumber = mDrawingState.frameNumber;
+ const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+ mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
+ mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
+
+ mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
+ FrameTracer::FrameEvent::ACQUIRE_FENCE);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+ FrameTracer::FrameEvent::LATCH);
+
+ auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+ if (bufferSurfaceFrame != nullptr &&
+ bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+ // Update only if the bufferSurfaceFrame wasn't already presented. A Presented
+ // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
+ // are processing the next state.
+ addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
+ mDrawingState.acquireFenceTime->getSignalTime(),
+ latchTime);
+ mDrawingState.bufferSurfaceFrameTX.reset();
+ }
+
+ std::deque<sp<CallbackHandle>> remainingHandles;
+ mFlinger->getTransactionCallbackInvoker()
+ .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+ mDrawingState.callbackHandles = remainingHandles;
+
+ mDrawingStateModified = false;
+}
+
+void Layer::gatherBufferInfo() {
+ if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
+ decrementPendingBufferCount();
+ }
+
+ mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
+ mBufferInfo.mBuffer = mDrawingState.buffer;
+ mBufferInfo.mFence = mDrawingState.acquireFence;
+ mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
+ mBufferInfo.mPixelFormat =
+ !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
+ mBufferInfo.mFrameLatencyNeeded = true;
+ mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
+ mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+ mBufferInfo.mFence = mDrawingState.acquireFence;
+ mBufferInfo.mTransform = mDrawingState.bufferTransform;
+ auto lastDataspace = mBufferInfo.mDataspace;
+ mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
+ if (lastDataspace != mBufferInfo.mDataspace) {
+ mFlinger->mSomeDataspaceChanged = true;
+ }
+ mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
+ mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
+ mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
+ mBufferInfo.mApi = mDrawingState.api;
+ mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
+ mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(mDrawingState.clientCacheId);
+}
+
+Rect Layer::computeBufferCrop(const State& s) {
+ if (s.buffer && !s.bufferCrop.isEmpty()) {
+ Rect bufferCrop;
+ s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop);
+ return bufferCrop;
+ } else if (s.buffer) {
+ return s.buffer->getBounds();
+ } else {
+ return s.bufferCrop;
+ }
+}
+
+sp<Layer> Layer::createClone() {
+ LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
+ args.textureName = mTextureName;
+ sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+ layer->mHwcSlotGenerator = mHwcSlotGenerator;
+ layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
+ return layer;
+}
+
+bool Layer::bufferNeedsFiltering() const {
+ const State& s(getDrawingState());
+ if (!s.buffer) {
+ return false;
+ }
+
+ int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth());
+ int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight());
+
+ // Undo any transformations on the buffer and return the result.
+ if (s.bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+
+ if (s.transformToDisplayInverse) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ const Rect layerSize{getBounds()};
+ int32_t layerWidth = layerSize.getWidth();
+ int32_t layerHeight = layerSize.getHeight();
+
+ // Align the layer orientation with the buffer before comparism
+ if (mTransformHint & ui::Transform::ROT_90) {
+ std::swap(layerWidth, layerHeight);
+ }
+
+ return layerWidth != bufferWidth || layerHeight != bufferHeight;
+}
+
+void Layer::decrementPendingBufferCount() {
+ int32_t pendingBuffers = --mPendingBufferTransactions;
+ tracePendingBufferCount(pendingBuffers);
+}
+
+void Layer::tracePendingBufferCount(int32_t pendingBuffers) {
+ ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
+}
+
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because Layer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform Layer::getInputTransform() const {
+ if (!hasBufferOrSidebandStream()) {
+ return getTransform();
+ }
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent == nullptr) {
+ return ui::Transform();
+ }
+
+ return parent->getTransform();
+}
+
+/**
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds don't include the buffer transform, where the input assumes
+ * that's already included.
+ */
+Rect Layer::getInputBounds() const {
+ if (!hasBufferOrSidebandStream()) {
+ return getCroppedBufferSize(getDrawingState());
+ }
+
+ Rect bufferBounds = getCroppedBufferSize(getDrawingState());
+ if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
+ return bufferBounds;
+ }
+ return mDrawingState.transform.transform(bufferBounds);
+}
+
+bool Layer::simpleBufferUpdate(const layer_state_t& s) const {
+ const uint64_t requiredFlags = layer_state_t::eBufferChanged;
+
+ const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
+ layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
+ layer_state_t::eReparent;
+
+ const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged |
+ layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged |
+ layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged |
+ layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged |
+ layer_state_t::eInputInfoChanged;
+
+ if ((s.what & requiredFlags) != requiredFlags) {
+ ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+ (s.what | requiredFlags) & ~s.what);
+ return false;
+ }
+
+ if (s.what & deniedFlags) {
+ ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags);
+ return false;
+ }
+
+ if (s.what & allowedFlags) {
+ ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags);
+ }
+
+ if (s.what & layer_state_t::ePositionChanged) {
+ if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
+ ALOGV("%s: false [ePositionChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eAlphaChanged) {
+ if (mDrawingState.color.a != s.alpha) {
+ ALOGV("%s: false [eAlphaChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eColorTransformChanged) {
+ if (mDrawingState.colorTransform != s.colorTransform) {
+ ALOGV("%s: false [eColorTransformChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eBackgroundColorChanged) {
+ if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) {
+ ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eMatrixChanged) {
+ if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
+ mRequestedTransform.dtdy() != s.matrix.dtdy ||
+ mRequestedTransform.dtdx() != s.matrix.dtdx ||
+ mRequestedTransform.dsdy() != s.matrix.dsdy) {
+ ALOGV("%s: false [eMatrixChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eCornerRadiusChanged) {
+ if (mDrawingState.cornerRadius != s.cornerRadius) {
+ ALOGV("%s: false [eCornerRadiusChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
+ ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eTransformChanged) {
+ if (mDrawingState.bufferTransform != s.transform) {
+ ALOGV("%s: false [eTransformChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
+ if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
+ ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eCropChanged) {
+ if (mDrawingState.crop != s.crop) {
+ ALOGV("%s: false [eCropChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eDataspaceChanged) {
+ if (mDrawingState.dataspace != s.dataspace) {
+ ALOGV("%s: false [eDataspaceChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eHdrMetadataChanged) {
+ if (mDrawingState.hdrMetadata != s.hdrMetadata) {
+ ALOGV("%s: false [eHdrMetadataChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eSidebandStreamChanged) {
+ if (mDrawingState.sidebandStream != s.sidebandStream) {
+ ALOGV("%s: false [eSidebandStreamChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
+ if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
+ ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eShadowRadiusChanged) {
+ if (mDrawingState.shadowRadius != s.shadowRadius) {
+ ALOGV("%s: false [eShadowRadiusChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eFixedTransformHintChanged) {
+ if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
+ ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eTrustedOverlayChanged) {
+ if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
+ ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eStretchChanged) {
+ StretchEffect temp = s.stretchEffect;
+ temp.sanitize();
+ if (mDrawingState.stretchEffect != temp) {
+ ALOGV("%s: false [eStretchChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eBufferCropChanged) {
+ if (mDrawingState.bufferCrop != s.bufferCrop) {
+ ALOGV("%s: false [eBufferCropChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eDestinationFrameChanged) {
+ if (mDrawingState.destinationFrame != s.destinationFrame) {
+ ALOGV("%s: false [eDestinationFrameChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ if (s.what & layer_state_t::eDimmingEnabledChanged) {
+ if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
+ ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+ return false;
+ }
+ }
+
+ ALOGV("%s: true", __func__);
+ return true;
+}
+
+bool Layer::isHdrY410() const {
+ // pixel format is HDR Y410 masquerading as RGBA_1010102
+ return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
+ mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
+ mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+ // There's no need to get a CE Layer if the layer isn't going to draw anything.
+ if (hasSomethingToDraw()) {
+ return asLayerFE();
+ } else {
+ return nullptr;
+ }
+}
+
+compositionengine::LayerFECompositionState* Layer::editCompositionState() {
+ return mCompositionState.get();
+}
+
+const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
+ return mCompositionState.get();
+}
+
+void Layer::useSurfaceDamage() {
+ if (mFlinger->mForceFullDamage) {
+ surfaceDamageRegion = Region::INVALID_REGION;
+ } else {
+ surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
+ }
+}
+
+void Layer::useEmptyDamage() {
+ surfaceDamageRegion.clear();
+}
+
+bool Layer::isOpaque(const Layer::State& s) const {
+ // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+ // layer's opaque flag.
+ if (!hasSomethingToDraw()) {
+ return false;
+ }
+
+ // if the layer has the opaque flag, then we're always opaque
+ if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) {
+ return true;
+ }
+
+ // If the buffer has no alpha channel, then we are opaque
+ if (hasBufferOrSidebandStream() && isOpaqueFormat(getPixelFormat())) {
+ return true;
+ }
+
+ // Lastly consider the layer opaque if drawing a color with alpha == 1.0
+ return fillsColor() && getAlpha() == 1.0_hf;
+}
+
+bool Layer::canReceiveInput() const {
+ return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
+}
+
+bool Layer::isVisible() const {
+ if (!hasSomethingToDraw()) {
+ return false;
+ }
+
+ if (isHiddenByPolicy()) {
+ return false;
+ }
+
+ return getAlpha() > 0.0f || hasBlur();
+}
+
+void Layer::onPostComposition(const DisplayDevice* display,
+ const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const CompositorTiming& compositorTiming) {
+ // mFrameLatencyNeeded is true when a new frame was latched for the
+ // composition.
+ if (!mBufferInfo.mFrameLatencyNeeded) return;
+
+ for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->gpuCompositionDoneFence = glDoneFence;
+ handle->compositorTiming = compositorTiming;
+ }
+
+ // Update mFrameTracker.
+ nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
+ mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
+
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer && outputLayer->requiresClientComposition()) {
+ nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+ mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+ clientCompositionTimestamp,
+ FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+ // Update the SurfaceFrames in the drawing state
+ if (mDrawingState.bufferSurfaceFrameTX) {
+ mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+ }
+ for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+ surfaceFrame->setGpuComposition();
+ }
+ }
+
+ std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
+ if (frameReadyFence->isValid()) {
+ mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+ } else {
+ // There was no fence for this frame, so assume that it was ready
+ // to be presented at the desired present time.
+ mFrameTracker.setFrameReadyTime(desiredPresentTime);
+ }
+
+ if (display) {
+ const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+ const std::optional<Fps> renderRate =
+ mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+
+ const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
+ const auto gameMode = getGameMode();
+
+ if (presentFence->isValid()) {
+ mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
+ refreshRate, renderRate, vote, gameMode);
+ mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+ presentFence,
+ FrameTracer::FrameEvent::PRESENT_FENCE);
+ mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+ } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+ displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+ // The HWC doesn't support present fences, so use the refresh
+ // timestamp instead.
+ const nsecs_t actualPresentTime = display->getRefreshTimestamp();
+ mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
+ refreshRate, renderRate, vote, gameMode);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
+ mCurrentFrameNumber, actualPresentTime,
+ FrameTracer::FrameEvent::PRESENT_FENCE);
+ mFrameTracker.setActualPresentTime(actualPresentTime);
+ }
+ }
+
+ mFrameTracker.advanceFrame();
+ mBufferInfo.mFrameLatencyNeeded = false;
+}
+
+bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+ ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
+ getDrawingState().frameNumber);
+
+ bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
+
+ if (refreshRequired) {
+ return refreshRequired;
+ }
+
+ // If the head buffer's acquire fence hasn't signaled yet, return and
+ // try again later
+ if (!fenceHasSignaled()) {
+ ATRACE_NAME("!fenceHasSignaled()");
+ mFlinger->onLayerUpdate();
+ return false;
+ }
+
+ updateTexImage(latchTime);
+ if (mDrawingState.buffer == nullptr) {
+ return false;
+ }
+
+ // Capture the old state of the layer for comparisons later
+ BufferInfo oldBufferInfo = mBufferInfo;
+ const bool oldOpacity = isOpaque(mDrawingState);
+ mPreviousFrameNumber = mCurrentFrameNumber;
+ mCurrentFrameNumber = mDrawingState.frameNumber;
+ gatherBufferInfo();
+
+ if (oldBufferInfo.mBuffer == nullptr) {
+ // the first time we receive a buffer, we need to trigger a
+ // geometry invalidation.
+ recomputeVisibleRegions = true;
+ }
+
+ if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
+ (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
+ (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
+ (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
+ recomputeVisibleRegions = true;
+ }
+
+ if (oldBufferInfo.mBuffer != nullptr) {
+ uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+ if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
+ bufHeight != oldBufferInfo.mBuffer->getHeight()) {
+ recomputeVisibleRegions = true;
+ }
+ }
+
+ if (oldOpacity != isOpaque(mDrawingState)) {
+ recomputeVisibleRegions = true;
+ }
+
+ return true;
+}
+
+bool Layer::hasReadyFrame() const {
+ return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
+}
+
+bool Layer::isProtected() const {
+ return (mBufferInfo.mBuffer != nullptr) &&
+ (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+}
+
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool Layer::isOpaqueFormat(PixelFormat format) {
+ if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+ return true;
+ }
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_BGRA_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_R_8:
+ return false;
+ }
+ // in all other case, we have no blending (also for unknown formats)
+ return true;
+}
+
+bool Layer::needsFiltering(const DisplayDevice* display) const {
+ if (!hasBufferOrSidebandStream()) {
+ return false;
+ }
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer == nullptr) {
+ return false;
+ }
+
+ // We need filtering if the sourceCrop rectangle size does not match the
+ // displayframe rectangle size (not a 1:1 render)
+ const auto& compositionState = outputLayer->getState();
+ const auto displayFrame = compositionState.displayFrame;
+ const auto sourceCrop = compositionState.sourceCrop;
+ return sourceCrop.getHeight() != displayFrame.getHeight() ||
+ sourceCrop.getWidth() != displayFrame.getWidth();
+}
+
+bool Layer::needsFilteringForScreenshots(const DisplayDevice* display,
+ const ui::Transform& inverseParentTransform) const {
+ if (!hasBufferOrSidebandStream()) {
+ return false;
+ }
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer == nullptr) {
+ return false;
+ }
+
+ // We need filtering if the sourceCrop rectangle size does not match the
+ // viewport rectangle size (not a 1:1 render)
+ const auto& compositionState = outputLayer->getState();
+ const ui::Transform& displayTransform = display->getTransform();
+ const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
+ // Undo the transformation of the displayFrame so that we're back into
+ // layer-stack space.
+ const Rect frame = inverseTransform.transform(compositionState.displayFrame);
+ const FloatRect sourceCrop = compositionState.sourceCrop;
+
+ int32_t frameHeight = frame.getHeight();
+ int32_t frameWidth = frame.getWidth();
+ // If the display transform had a rotational component then undo the
+ // rotation so that the orientation matches the source crop.
+ if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
+ std::swap(frameHeight, frameWidth);
+ }
+ return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
+}
+
+void Layer::latchAndReleaseBuffer() {
+ if (hasReadyFrame()) {
+ bool ignored = false;
+ latchBuffer(ignored, systemTime());
+ }
+ releasePendingBuffer(systemTime());
+}
+
+PixelFormat Layer::getPixelFormat() const {
+ return mBufferInfo.mPixelFormat;
+}
+
+bool Layer::getTransformToDisplayInverse() const {
+ return mBufferInfo.mTransformToDisplayInverse;
+}
+
+Rect Layer::getBufferCrop() const {
+ // this is the crop rectangle that applies to the buffer
+ // itself (as opposed to the window)
+ if (!mBufferInfo.mCrop.isEmpty()) {
+ // if the buffer crop is defined, we use that
+ return mBufferInfo.mCrop;
+ } else if (mBufferInfo.mBuffer != nullptr) {
+ // otherwise we use the whole buffer
+ return mBufferInfo.mBuffer->getBounds();
+ } else {
+ // if we don't have a buffer yet, we use an empty/invalid crop
+ return Rect();
+ }
+}
+
+uint32_t Layer::getBufferTransform() const {
+ return mBufferInfo.mTransform;
+}
+
+ui::Dataspace Layer::getDataSpace() const {
+ return mDrawingState.dataspaceRequested ? getRequestedDataSpace() : ui::Dataspace::UNKNOWN;
+}
+
+ui::Dataspace Layer::getRequestedDataSpace() const {
+ return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
+}
+
+ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
+ ui::Dataspace updatedDataspace = dataspace;
+ // translate legacy dataspaces to modern dataspaces
+ switch (dataspace) {
+ case ui::Dataspace::SRGB:
+ updatedDataspace = ui::Dataspace::V0_SRGB;
+ break;
+ case ui::Dataspace::SRGB_LINEAR:
+ updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ break;
+ case ui::Dataspace::JFIF:
+ updatedDataspace = ui::Dataspace::V0_JFIF;
+ break;
+ case ui::Dataspace::BT601_625:
+ updatedDataspace = ui::Dataspace::V0_BT601_625;
+ break;
+ case ui::Dataspace::BT601_525:
+ updatedDataspace = ui::Dataspace::V0_BT601_525;
+ break;
+ case ui::Dataspace::BT709:
+ updatedDataspace = ui::Dataspace::V0_BT709;
+ break;
+ default:
+ break;
+ }
+
+ return updatedDataspace;
+}
+
+sp<GraphicBuffer> Layer::getBuffer() const {
+ return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
+}
+
+void Layer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const {
+ sp<GraphicBuffer> buffer = getBuffer();
+ if (!buffer) {
+ ALOGE("Buffer should not be null!");
+ return;
+ }
+ GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
+ buffer->getPixelFormat(), mBufferInfo.mCrop,
+ mBufferInfo.mTransform, filteringEnabled);
+}
+
+void Layer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+ mTransformHint = getFixedTransformHint();
+ if (mTransformHint == ui::Transform::ROT_INVALID) {
+ mTransformHint = displayTransformHint;
+ }
+}
+
+const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
+ return mBufferInfo.mBuffer;
+}
+
+bool Layer::setColor(const half3& color) {
+ if (mDrawingState.color.r == color.r && mDrawingState.color.g == color.g &&
+ mDrawingState.color.b == color.b) {
+ return false;
+ }
+
+ mDrawingState.sequence++;
+ mDrawingState.color.r = color.r;
+ mDrawingState.color.g = color.g;
+ mDrawingState.color.b = color.b;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::fillsColor() const {
+ return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf &&
+ mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf;
+}
+
+bool Layer::hasBlur() const {
+ return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {