| /* | |
| * Copyright (C) 2007 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. | |
| */ | |
| // TODO(b/129481165): remove the #pragma below and fix conversion issues | |
| #pragma clang diagnostic push | |
| #pragma clang diagnostic ignored "-Wconversion" | |
| //#define LOG_NDEBUG 0 | |
| #undef LOG_TAG | |
| #define LOG_TAG "Layer" | |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS | |
| #include <android-base/properties.h> | |
| #include <android-base/stringprintf.h> | |
| #include <binder/IPCThreadState.h> | |
| #include <common/trace.h> | |
| #include <compositionengine/CompositionEngine.h> | |
| #include <compositionengine/Display.h> | |
| #include <compositionengine/LayerFECompositionState.h> | |
| #include <compositionengine/OutputLayer.h> | |
| #include <compositionengine/impl/OutputLayerCompositionState.h> | |
| #include <cutils/compiler.h> | |
| #include <cutils/native_handle.h> | |
| #include <cutils/properties.h> | |
| #include <ftl/enum.h> | |
| #include <ftl/fake_guard.h> | |
| #include <gui/BufferItem.h> | |
| #include <gui/Surface.h> | |
| #include <math.h> | |
| #include <private/android_filesystem_config.h> | |
| #include <renderengine/RenderEngine.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <system/graphics-base-v1.0.h> | |
| #include <ui/DebugUtils.h> | |
| #include <ui/FloatRect.h> | |
| #include <ui/GraphicBuffer.h> | |
| #include <ui/HdrRenderTypeUtils.h> | |
| #include <ui/PixelFormat.h> | |
| #include <ui/Rect.h> | |
| #include <ui/Transform.h> | |
| #include <utils/Errors.h> | |
| #include <utils/Log.h> | |
| #include <utils/NativeHandle.h> | |
| #include <utils/StopWatch.h> | |
| #include <algorithm> | |
| #include <optional> | |
| #include "DisplayDevice.h" | |
| #include "DisplayHardware/HWComposer.h" | |
| #include "FrameTracer/FrameTracer.h" | |
| #include "FrontEnd/LayerCreationArgs.h" | |
| #include "FrontEnd/LayerHandle.h" | |
| #include "Layer.h" | |
| #include "LayerProtoHelper.h" | |
| #include "Scheduler/FrameTimeline.h" | |
| #include "SurfaceFlinger.h" | |
| #include "TimeStats/TimeStats.h" | |
| #include "TransactionCallbackInvoker.h" | |
| #include "TunnelModeEnabledReporter.h" | |
| #include "Utils/FenceUtils.h" | |
| #define DEBUG_RESIZE 0 | |
| #define EARLY_RELEASE_ENABLED false | |
| namespace android { | |
| using namespace std::chrono_literals; | |
| namespace { | |
| constexpr int kDumpTableRowLength = 159; | |
| const ui::Transform kIdentityTransform; | |
| TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) { | |
| using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility; | |
| using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness; | |
| const auto frameRateCompatibility = [frameRate] { | |
| switch (frameRate.vote.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.vote.seamlessness) { | |
| case scheduler::Seamlessness::OnlySeamless: | |
| return Seamlessness::ShouldBeSeamless; | |
| case scheduler::Seamlessness::SeamedAndSeamless: | |
| return Seamlessness::NotRequired; | |
| default: | |
| return Seamlessness::Undefined; | |
| } | |
| }(); | |
| return TimeStats::SetFrameRateVote{.frameRate = frameRate.vote.rate.getValue(), | |
| .frameRateCompatibility = frameRateCompatibility, | |
| .seamlessness = seamlessness}; | |
| } | |
| } // namespace | |
| using namespace ftl::flag_operators; | |
| using base::StringAppendF; | |
| using frontend::LayerSnapshot; | |
| using frontend::RoundedCornerState; | |
| using gui::GameMode; | |
| using gui::LayerMetadata; | |
| using gui::WindowInfo; | |
| using ui::Size; | |
| using PresentState = scheduler::SurfaceFrame::PresentState; | |
| Layer::Layer(const surfaceflinger::LayerCreationArgs& args) | |
| : sequence(args.sequence), | |
| mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)), | |
| mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)), | |
| mWindowType(static_cast<WindowInfo::Type>( | |
| args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) { | |
| ALOGV("Creating Layer %s", getDebugName()); | |
| mDrawingState.crop = {0, 0, -1, -1}; | |
| mDrawingState.sequence = 0; | |
| mDrawingState.transform.set(0, 0); | |
| mDrawingState.frameNumber = 0; | |
| mDrawingState.previousFrameNumber = 0; | |
| mDrawingState.producerId = 0; | |
| mDrawingState.bufferTransform = 0; | |
| mDrawingState.transformToDisplayInverse = false; | |
| mDrawingState.acquireFence = sp<Fence>::make(-1); | |
| mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); | |
| mDrawingState.dataspace = ui::Dataspace::V0_SRGB; | |
| mDrawingState.metadata = args.metadata; | |
| mDrawingState.frameTimelineInfo = {}; | |
| mDrawingState.postTime = -1; | |
| mDeprecatedFrameTracker.setDisplayRefreshPeriod( | |
| args.flinger->mScheduler->getPacesetterVsyncPeriod().ns()); | |
| mOwnerUid = args.ownerUid; | |
| mOwnerPid = args.ownerPid; | |
| mOwnerAppId = mOwnerUid % PER_USER_RANGE; | |
| mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; | |
| mLayerFEs.emplace_back(frontend::LayerHierarchy::TraversalPath{static_cast<uint32_t>(sequence)}, | |
| args.flinger->getFactory().createLayerFE(mName, this)); | |
| } | |
| void Layer::onFirstRef() { | |
| mFlinger->onLayerFirstRef(this); | |
| } | |
| Layer::~Layer() { | |
| LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId, | |
| "Layer destructor called off the main thread."); | |
| if (mBufferInfo.mBuffer != nullptr) { | |
| callReleaseBufferCallback(mDrawingState.releaseBufferListener, | |
| mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, | |
| mBufferInfo.mFence); | |
| } | |
| const int32_t layerId = getSequence(); | |
| mFlinger->mTimeStats->onDestroy(layerId); | |
| mFlinger->mFrameTracer->onDestroy(layerId); | |
| mFlinger->onLayerDestroyed(this); | |
| const auto currentTime = std::chrono::steady_clock::now(); | |
| if (mLastLayerEvent.has_value() && | |
| mTimeSinceLayerEventsUpdate > std::chrono::steady_clock::time_point::min()) { | |
| mLastLayerEvent->timeSinceLastEvent = std::chrono::duration_cast<std::chrono::milliseconds>( | |
| currentTime - mTimeSinceLayerEventsUpdate); | |
| mFlinger->mLayerEvents.emplace_back(mLastLayerEvent.value()); | |
| } | |
| if (mDrawingState.sidebandStream != nullptr) { | |
| mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount(); | |
| } | |
| if (hasTrustedPresentationListener()) { | |
| mFlinger->mNumTrustedPresentationListeners--; | |
| updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/); | |
| } | |
| } | |
| // --------------------------------------------------------------------------- | |
| // set-up | |
| // --------------------------------------------------------------------------- | |
| sp<IBinder> Layer::getHandle() { | |
| Mutex::Autolock _l(mLock); | |
| if (mGetHandleCalled) { | |
| ALOGE("Get handle called twice" ); | |
| return nullptr; | |
| } | |
| mGetHandleCalled = true; | |
| mHandleAlive = true; | |
| return sp<LayerHandle>::make(mFlinger, sp<Layer>::fromExisting(this)); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // h/w composer set-up | |
| // --------------------------------------------------------------------------- | |
| // No early returns. | |
| void Layer::updateTrustedPresentationState(const DisplayDevice* display, | |
| const frontend::LayerSnapshot* snapshot, | |
| int64_t time_in_ms, bool leaveState) { | |
| if (!hasTrustedPresentationListener()) { | |
| return; | |
| } | |
| const bool lastState = mLastComputedTrustedPresentationState; | |
| mLastComputedTrustedPresentationState = false; | |
| if (!leaveState) { | |
| const auto outputLayer = findOutputLayerForDisplay(display, snapshot->path); | |
| if (outputLayer != nullptr) { | |
| if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) { | |
| Region coveredRegion = | |
| *outputLayer->getState().coveredRegionExcludingDisplayOverlays; | |
| mLastComputedTrustedPresentationState = | |
| computeTrustedPresentationState(snapshot->geomLayerBounds, | |
| snapshot->sourceBounds(), coveredRegion, | |
| snapshot->transformedBounds, | |
| snapshot->alpha, | |
| snapshot->geomLayerTransform, | |
| mTrustedPresentationThresholds); | |
| } else { | |
| ALOGE("CoveredRegionExcludingDisplayOverlays was not set for %s. Don't compute " | |
| "TrustedPresentationState", | |
| getDebugName()); | |
| } | |
| } | |
| } | |
| const bool newState = mLastComputedTrustedPresentationState; | |
| if (lastState && !newState) { | |
| // We were in the trusted presentation state, but now we left it, | |
| // emit the callback if needed | |
| if (mLastReportedTrustedPresentationState) { | |
| mLastReportedTrustedPresentationState = false; | |
| mTrustedPresentationListener.invoke(false); | |
| } | |
| // Reset the timer | |
| mEnteredTrustedPresentationStateTime = -1; | |
| } else if (!lastState && newState) { | |
| // We were not in the trusted presentation state, but we entered it, begin the timer | |
| // and make sure this gets called at least once more! | |
| mEnteredTrustedPresentationStateTime = time_in_ms; | |
| mFlinger->forceFutureUpdate(mTrustedPresentationThresholds.stabilityRequirementMs * 1.5); | |
| } | |
| // Has the timer elapsed, but we are still in the state? Emit a callback if needed | |
| if (!mLastReportedTrustedPresentationState && newState && | |
| (time_in_ms - mEnteredTrustedPresentationStateTime > | |
| mTrustedPresentationThresholds.stabilityRequirementMs)) { | |
| mLastReportedTrustedPresentationState = true; | |
| mTrustedPresentationListener.invoke(true); | |
| } | |
| } | |
| /** | |
| * See SurfaceComposerClient.h: setTrustedPresentationCallback for discussion | |
| * of how the parameters and thresholds are interpreted. The general spirit is | |
| * to produce an upper bound on the amount of the buffer which was presented. | |
| */ | |
| bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds, | |
| const Region& coveredRegion, | |
| const FloatRect& screenBounds, float alpha, | |
| const ui::Transform& effectiveTransform, | |
| const TrustedPresentationThresholds& thresholds) { | |
| if (alpha < thresholds.minAlpha) { | |
| return false; | |
| } | |
| if (sourceBounds.getWidth() == 0 || sourceBounds.getHeight() == 0) { | |
| return false; | |
| } | |
| if (screenBounds.getWidth() == 0 || screenBounds.getHeight() == 0) { | |
| return false; | |
| } | |
| const float sx = effectiveTransform.dsdx(); | |
| const float sy = effectiveTransform.dsdy(); | |
| float fractionRendered = std::min(sx * sy, 1.0f); | |
| float boundsOverSourceW = bounds.getWidth() / (float)sourceBounds.getWidth(); | |
| float boundsOverSourceH = bounds.getHeight() / (float)sourceBounds.getHeight(); | |
| fractionRendered *= boundsOverSourceW * boundsOverSourceH; | |
| Region tJunctionFreeRegion = Region::createTJunctionFreeRegion(coveredRegion); | |
| // Compute the size of all the rects since they may be disconnected. | |
| float coveredSize = 0; | |
| for (auto rect = tJunctionFreeRegion.begin(); rect < tJunctionFreeRegion.end(); rect++) { | |
| float size = rect->width() * rect->height(); | |
| coveredSize += size; | |
| } | |
| fractionRendered *= (1 - (coveredSize / (screenBounds.getWidth() * screenBounds.getHeight()))); | |
| if (fractionRendered < thresholds.minFractionRendered) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| Rect Layer::getCroppedBufferSize(const State& s) const { | |
| Rect size = getBufferSize(s); | |
| Rect crop = Rect(getCrop(s)); | |
| if (!crop.isEmpty() && size.isValid()) { | |
| size.intersect(crop, &size); | |
| } else if (!crop.isEmpty()) { | |
| size = crop; | |
| } | |
| return size; | |
| } | |
| const char* Layer::getDebugName() const { | |
| return mName.c_str(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // drawing... | |
| // --------------------------------------------------------------------------- | |
| aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType( | |
| const DisplayDevice& display) const { | |
| const auto outputLayer = findOutputLayerForDisplay(&display); | |
| return getCompositionType(outputLayer); | |
| } | |
| aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType( | |
| const compositionengine::OutputLayer* outputLayer) const { | |
| if (outputLayer == nullptr) { | |
| return aidl::android::hardware::graphics::composer3::Composition::INVALID; | |
| } | |
| if (outputLayer->getState().hwc) { | |
| return (*outputLayer->getState().hwc).hwcCompositionType; | |
| } else { | |
| return aidl::android::hardware::graphics::composer3::Composition::CLIENT; | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // transaction | |
| // ---------------------------------------------------------------------------- | |
| void Layer::commitTransaction() REQUIRES(mFlinger->mStateLock) { | |
| // Set the present state for all bufferlessSurfaceFramesTX to Presented. The | |
| // bufferSurfaceFrameTX will be presented in latchBuffer. | |
| for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { | |
| if (surfaceFrame->getPresentState() != PresentState::Presented) { | |
| // With applyPendingStates, we could end up having presented surfaceframes from previous | |
| // states | |
| surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime); | |
| mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame); | |
| } | |
| } | |
| mDrawingState.bufferlessSurfaceFramesTX.clear(); | |
| } | |
| void Layer::setTransactionFlags(uint32_t mask) { | |
| mTransactionFlags |= mask; | |
| } | |
| bool Layer::setCrop(const FloatRect& crop) { | |
| if (mDrawingState.crop == crop) return false; | |
| mDrawingState.sequence++; | |
| mDrawingState.crop = crop; | |
| setTransactionFlags(eTransactionNeeded); | |
| return true; | |
| } | |
| bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) { | |
| return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE; | |
| }; | |
| void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, | |
| nsecs_t postTime, gui::GameMode gameMode) | |
| REQUIRES(mFlinger->mStateLock) { | |
| mDrawingState.postTime = postTime; | |
| // Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if | |
| // there are two transactions with the same token, the first one without a buffer and the | |
| // second one with a buffer. We promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX | |
| // in that case. | |
| auto it = mDrawingState.bufferlessSurfaceFramesTX.find(info.vsyncId); | |
| if (it != mDrawingState.bufferlessSurfaceFramesTX.end()) { | |
| // Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX | |
| mDrawingState.bufferSurfaceFrameTX = it->second; | |
| mDrawingState.bufferlessSurfaceFramesTX.erase(it); | |
| mDrawingState.bufferSurfaceFrameTX->promoteToBuffer(); | |
| mDrawingState.bufferSurfaceFrameTX->setActualQueueTime(postTime); | |
| } else { | |
| mDrawingState.bufferSurfaceFrameTX = | |
| createSurfaceFrameForBuffer(info, postTime, mTransactionName, gameMode); | |
| } | |
| setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode); | |
| } | |
| void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info, | |
| nsecs_t postTime, | |
| gui::GameMode gameMode) { | |
| mDrawingState.frameTimelineInfo = info; | |
| mDrawingState.postTime = postTime; | |
| setTransactionFlags(eTransactionNeeded); | |
| if (const auto& bufferSurfaceFrameTX = mDrawingState.bufferSurfaceFrameTX; | |
| bufferSurfaceFrameTX != nullptr) { | |
| if (bufferSurfaceFrameTX->getToken() == info.vsyncId) { | |
| // BufferSurfaceFrame takes precedence over BufferlessSurfaceFrame. If the same token is | |
| // being used for BufferSurfaceFrame, don't create a new one. | |
| return; | |
| } | |
| } | |
| // For Transactions without a buffer, we create only one SurfaceFrame per vsyncId. If multiple | |
| // transactions use the same vsyncId, we just treat them as one SurfaceFrame (unless they are | |
| // targeting different vsyncs). | |
| auto it = mDrawingState.bufferlessSurfaceFramesTX.find(info.vsyncId); | |
| if (it == mDrawingState.bufferlessSurfaceFramesTX.end()) { | |
| auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime, gameMode); | |
| mDrawingState.bufferlessSurfaceFramesTX[info.vsyncId] = surfaceFrame; | |
| } else { | |
| if (it->second->getPresentState() == PresentState::Presented) { | |
| // If the SurfaceFrame was already presented, its safe to overwrite it since it must | |
| // have been from previous vsync. | |
| it->second = createSurfaceFrameForTransaction(info, postTime, gameMode); | |
| } | |
| } | |
| setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode); | |
| } | |
| void Layer::addSurfaceFrameDroppedForBuffer(std::shared_ptr<scheduler::SurfaceFrame>& surfaceFrame, | |
| nsecs_t dropTime) { | |
| surfaceFrame->setDropTime(dropTime); | |
| surfaceFrame->setPresentState(PresentState::Dropped); | |
| mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame); | |
| } | |
| void Layer::addSurfaceFramePresentedForBuffer( | |
| std::shared_ptr<scheduler::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime, | |
| nsecs_t currentLatchTime) REQUIRES(mFlinger->mStateLock) { | |
| surfaceFrame->setAcquireFenceTime(acquireFenceTime); | |
| surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime); | |
| mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame); | |
| updateLastLatchTime(currentLatchTime); | |
| } | |
| std::shared_ptr<scheduler::SurfaceFrame> Layer::createSurfaceFrameForTransaction( | |
| const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) | |
| REQUIRES(mFlinger->mStateLock) { | |
| auto surfaceFrame = | |
| mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, | |
| getSequence(), mName, | |
| mTransactionName, | |
| /*isBuffer*/ false, gameMode); | |
| // Buffer hasn't yet been latched, so use mDrawingState | |
| surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); | |
| surfaceFrame->setActualStartTime(info.startTimeNanos); | |
| // For Transactions, the post time is considered to be both queue and acquire fence time. | |
| surfaceFrame->setActualQueueTime(postTime); | |
| surfaceFrame->setAcquireFenceTime(postTime); | |
| const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); | |
| if (fps) { | |
| surfaceFrame->setRenderRate(*fps); | |
| } | |
| return surfaceFrame; | |
| } | |
| std::shared_ptr<scheduler::SurfaceFrame> Layer::createSurfaceFrameForBuffer( | |
| const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName, | |
| gui::GameMode gameMode) REQUIRES(mFlinger->mStateLock) { | |
| auto surfaceFrame = | |
| mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, | |
| getSequence(), mName, debugName, | |
| /*isBuffer*/ true, gameMode); | |
| // Buffer hasn't yet been latched, so use mDrawingState | |
| surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); | |
| surfaceFrame->setActualStartTime(info.startTimeNanos); | |
| // For buffers, acquire fence time will set during latch. | |
| surfaceFrame->setActualQueueTime(queueTime); | |
| const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); | |
| if (fps) { | |
| surfaceFrame->setRenderRate(*fps); | |
| } | |
| return surfaceFrame; | |
| } | |
| void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime, | |
| std::string debugName, gui::GameMode gameMode) | |
| REQUIRES(mFlinger->mStateLock) { | |
| if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) { | |
| return; | |
| } | |
| FrameTimelineInfo skippedFrameTimelineInfo = info; | |
| skippedFrameTimelineInfo.vsyncId = info.skippedFrameVsyncId; | |
| auto surfaceFrame = | |
| mFlinger->mFrameTimeline->createSurfaceFrameForToken(skippedFrameTimelineInfo, | |
| mOwnerPid, mOwnerUid, | |
| getSequence(), mName, debugName, | |
| /*isBuffer*/ false, gameMode); | |
| // Buffer hasn't yet been latched, so use mDrawingState | |
| surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); | |
| surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos); | |
| // For Transactions, the post time is considered to be both queue and acquire fence time. | |
| surfaceFrame->setActualQueueTime(postTime); | |
| surfaceFrame->setAcquireFenceTime(postTime); | |
| const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); | |
| if (fps) { | |
| surfaceFrame->setRenderRate(*fps); | |
| } | |
| addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime); | |
| } | |
| bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps, | |
| nsecs_t now) { | |
| if (mDrawingState.frameRateForLayerTree == frameRate) { | |
| return false; | |
| } | |
| mDrawingState.frameRateForLayerTree = frameRate; | |
| mFlinger->mScheduler | |
| ->recordLayerHistory(sequence, layerProps, now, now, | |
| scheduler::LayerHistory::LayerUpdateType::SetFrameRate); | |
| return true; | |
| } | |
| Layer::FrameRate Layer::getFrameRateForLayerTree() const { | |
| return getDrawingState().frameRateForLayerTree; | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // debugging | |
| // ---------------------------------------------------------------------------- | |
| void Layer::miniDumpHeader(std::string& result) { | |
| result.append(kDumpTableRowLength, '-'); | |
| result.append("\n"); | |
| result.append(" Layer name\n"); | |
| result.append(" Z | "); | |
| result.append(" Window Type | "); | |
| result.append(" Comp Type | "); | |
| result.append(" Transform | "); | |
| result.append(" Disp Frame (LTRB) | "); | |
| result.append(" Source Crop (LTRB) | "); | |
| result.append(" Frame Rate (Explicit) (Seamlessness) [Focused]\n"); | |
| result.append(kDumpTableRowLength, '-'); | |
| result.append("\n"); | |
| } | |
| void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot, | |
| const DisplayDevice& display) const { | |
| const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path); | |
| if (!outputLayer) { | |
| return; | |
| } | |
| StringAppendF(&result, " %s\n", snapshot.debugName.c_str()); | |
| StringAppendF(&result, " %10zu | ", snapshot.globalZ); | |
| StringAppendF(&result, " %10d | ", | |
| snapshot.layerMetadata.getInt32(gui::METADATA_WINDOW_TYPE, 0)); | |
| StringAppendF(&result, "%10s | ", toString(getCompositionType(outputLayer)).c_str()); | |
| const auto& outputLayerState = outputLayer->getState(); | |
| StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str()); | |
| const Rect& frame = outputLayerState.displayFrame; | |
| StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom); | |
| const FloatRect& crop = outputLayerState.sourceCrop; | |
| StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right, | |
| crop.bottom); | |
| const auto frameRate = snapshot.frameRate; | |
| std::string frameRateStr; | |
| if (frameRate.vote.rate.isValid()) { | |
| StringAppendF(&frameRateStr, "%.2f", frameRate.vote.rate.getValue()); | |
| } | |
| if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) { | |
| StringAppendF(&result, "%6s %15s %17s", frameRateStr.c_str(), | |
| ftl::enum_string(frameRate.vote.type).c_str(), | |
| ftl::enum_string(frameRate.vote.seamlessness).c_str()); | |
| } else if (frameRate.category != FrameRateCategory::Default) { | |
| StringAppendF(&result, "%6s %15s %17s", frameRateStr.c_str(), | |
| (std::string("Cat::") + ftl::enum_string(frameRate.category)).c_str(), | |
| ftl::enum_string(frameRate.vote.seamlessness).c_str()); | |
| } else { | |
| result.append(41, ' '); | |
| } | |
| const auto focused = isLayerFocusedBasedOnPriority(snapshot.frameRateSelectionPriority); | |
| StringAppendF(&result, " [%s]\n", focused ? "*" : " "); | |
| result.append(kDumpTableRowLength, '-'); | |
| result.append("\n"); | |
| } | |
| void Layer::dumpFrameStats(std::string& result) const { | |
| if (FlagManager::getInstance().deprecate_frame_tracker()) { | |
| FrameStats fs = FrameStats(); | |
| getFrameStats(&fs); | |
| for (auto desired = fs.desiredPresentTimesNano.begin(), | |
| actual = fs.actualPresentTimesNano.begin(), | |
| ready = fs.frameReadyTimesNano.begin(); | |
| desired != fs.desiredPresentTimesNano.end() && | |
| actual != fs.actualPresentTimesNano.end() && ready != fs.frameReadyTimesNano.end(); | |
| ++desired, ++actual, ++ready) { | |
| result.append(std::format("{}\t{}\t{}\n", *desired, *actual, *ready)); | |
| } | |
| result.push_back('\n'); | |
| } else { | |
| mDeprecatedFrameTracker.dumpStats(result); | |
| } | |
| } | |
| void Layer::clearFrameStats() { | |
| if (FlagManager::getInstance().deprecate_frame_tracker()) { | |
| mFrameStatsHistorySize = 0; | |
| } else { | |
| mDeprecatedFrameTracker.clearStats(); | |
| } | |
| } | |
| void Layer::getFrameStats(FrameStats* outStats) const { | |
| if (FlagManager::getInstance().deprecate_frame_tracker()) { | |
| if (auto ftl = getTimeline()) { | |
| nsecs_t refreshPeriod = | |
| Fps::fromValue(ftl->get().computeFps({getSequence()})).getPeriodNsecs(); | |
| // FPS computation requires some number of layer updates before the calculation can be | |
| // made. If not enough frames are available return the projected FPS based on the | |
| // pacesetter display's native refresh rate. | |
| if (!refreshPeriod) { | |
| refreshPeriod = mFlinger->mScheduler->getPacesetterVsyncPeriod().ns(); | |
| } | |
| ftl->get().generateFrameStats(getSequence(), mFrameStatsHistorySize, outStats); | |
| outStats->refreshPeriodNano = refreshPeriod; | |
| } | |
| } else { | |
| mDeprecatedFrameTracker.getStats(outStats); | |
| } | |
| } | |
| void Layer::onDisconnect() { | |
| const int32_t layerId = getSequence(); | |
| mFlinger->mTimeStats->onDestroy(layerId); | |
| mFlinger->mFrameTracer->onDestroy(layerId); | |
| } | |
| void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto, | |
| ui::LayerStack layerStack) { | |
| ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread. | |
| ftl::FakeGuard mainThreadGuard(kMainThreadContext); | |
| // Only populate for the primary display. | |
| if (const auto display = mFlinger->getDisplayFromLayerStack(layerStack)) { | |
| const auto compositionType = getCompositionType(*display); | |
| layerProto->set_hwc_composition_type( | |
| static_cast<perfetto::protos::HwcCompositionType>(compositionType)); | |
| LayerProtoHelper::writeToProto(getVisibleRegion(display), | |
| [&]() { return layerProto->mutable_visible_region(); }); | |
| } | |
| } | |
| compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( | |
| const DisplayDevice* display) const { | |
| if (!display) return nullptr; | |
| sp<LayerFE> layerFE; | |
| frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)}; | |
| for (auto& [p, layer] : mLayerFEs) { | |
| if (p == path) { | |
| layerFE = layer; | |
| } | |
| } | |
| if (!layerFE) return nullptr; | |
| return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE); | |
| } | |
| compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( | |
| const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const { | |
| if (!display) return nullptr; | |
| sp<LayerFE> layerFE; | |
| for (auto& [p, layer] : mLayerFEs) { | |
| if (p == path) { | |
| layerFE = layer; | |
| } | |
| } | |
| if (!layerFE) return nullptr; | |
| return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE); | |
| } | |
| Region Layer::getVisibleRegion(const DisplayDevice* display) const { | |
| const auto outputLayer = findOutputLayerForDisplay(display); | |
| return outputLayer ? outputLayer->getState().visibleRegion : Region(); | |
| } | |
| void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, | |
| const sp<GraphicBuffer>& buffer, uint64_t framenumber, | |
| const sp<Fence>& releaseFence) { | |
| if (!listener && !mBufferReleaseChannel) { | |
| return; | |
| } | |
| SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); | |
| ReleaseCallbackId callbackId{buffer->getId(), framenumber}; | |
| const sp<Fence>& fence = releaseFence ? releaseFence : Fence::NO_FENCE; | |
| uint32_t currentMaxAcquiredBufferCount = | |
| mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); | |
| if (FlagManager::getInstance().monitor_buffer_fences()) { | |
| buffer->getDependencyMonitor().addEgress(FenceTime::makeValid(fence), "Layer release"); | |
| } | |
| if (listener) { | |
| listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount); | |
| } | |
| if (!mBufferReleaseChannel) { | |
| return; | |
| } | |
| status_t status = mBufferReleaseChannel->writeReleaseFence(callbackId, fence, | |
| currentMaxAcquiredBufferCount); | |
| if (status != OK) { | |
| int error = -status; | |
| // callReleaseBufferCallback is called during Layer's destructor. In this case, it's | |
| // expected to receive connection errors. | |
| if (error != EPIPE && error != ECONNRESET) { | |
| ALOGD("[%s] writeReleaseFence failed. error %d (%s)", getDebugName(), error, | |
| strerror(error)); | |
| } | |
| } | |
| } | |
| sp<CallbackHandle> Layer::findCallbackHandle() { | |
| // 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 && mPreviousReleaseBufferEndpoint == handle->listener) { | |
| ch = handle; | |
| break; | |
| } | |
| } | |
| return ch; | |
| } | |
| void Layer::prepareReleaseCallbacks(ftl::Future<FenceResult> futureFenceResult, | |
| ui::LayerStack layerStack) { | |
| sp<CallbackHandle> ch = findCallbackHandle(); | |
| if (ch != nullptr) { | |
| ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; | |
| ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); | |
| ch->name = mName; | |
| } else { | |
| // If we didn't get a release callback yet (e.g. some scenarios when capturing | |
| // screenshots asynchronously) then make sure we don't drop the fence. | |
| // Older fences for the same layer stack can be dropped when a new fence arrives. | |
| // An assumption here is that RenderEngine performs work sequentially, so an | |
| // incoming fence will not fire before an existing fence. | |
| mAdditionalPreviousReleaseFences.emplace_or_replace(layerStack, | |
| std::move(futureFenceResult)); | |
| } | |
| if (mBufferInfo.mBuffer) { | |
| mPreviouslyPresentedLayerStacks.push_back(layerStack); | |
| } | |
| if (mDrawingState.frameNumber > 0) { | |
| mDrawingState.previousFrameNumber = mDrawingState.frameNumber; | |
| } | |
| } | |
| void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { | |
| for (const auto& handle : mDrawingState.callbackHandles) { | |
| handle->bufferReleaseChannel = mBufferReleaseChannel; | |
| handle->transformHint = mTransformHint; | |
| handle->dequeueReadyTime = dequeueReadyTime; | |
| handle->currentMaxAcquiredBufferCount = | |
| mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); | |
| SFTRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(), | |
| handle->previousReleaseCallbackId.framenumber); | |
| } | |
| for (auto& handle : mDrawingState.callbackHandles) { | |
| if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) { | |
| handle->previousReleaseCallbackId = mPreviousReleaseCallbackId; | |
| break; | |
| } | |
| } | |
| mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles); | |
| mDrawingState.callbackHandles = {}; | |
| } | |
| bool Layer::setTransform(uint32_t transform) { | |
| if (mDrawingState.bufferTransform == transform) return false; | |
| mDrawingState.bufferTransform = transform; | |
| setTransactionFlags(eTransactionNeeded); | |
| return true; | |
| } | |
| bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) { | |
| if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false; | |
| mDrawingState.sequence++; | |
| mDrawingState.transformToDisplayInverse = transformToDisplayInverse; | |
| setTransactionFlags(eTransactionNeeded); | |
| return true; | |
| } | |
| void Layer::releasePreviousBuffer() REQUIRES(mFlinger->mStateLock) { | |
| 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); | |
| const int32_t layerId = getSequence(); | |
| mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber); | |
| decrementPendingBufferCount(); | |
| if (mDrawingState.bufferSurfaceFrameTX != nullptr && | |
| mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) { | |
| addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX, systemTime()); | |
| mDrawingState.bufferSurfaceFrameTX.reset(); | |
| } | |
| } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) { | |
| callReleaseBufferCallback(mDrawingState.releaseBufferListener, | |
| mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, | |
| mLastClientCompositionFence); | |
| mLastClientCompositionFence = nullptr; | |
| } | |
| } | |
| void Layer::resetDrawingStateBufferInfo() { | |
| mDrawingState.producerId = 0; | |
| mDrawingState.frameNumber = 0; | |
| mDrawingState.previousFrameNumber = 0; | |
| mDrawingState.releaseBufferListener = nullptr; | |
| mDrawingState.buffer = nullptr; | |
| mDrawingState.acquireFence = sp<Fence>::make(-1); | |
| mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence); | |
| mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime(); | |
| mDrawingState.releaseBufferEndpoint = nullptr; | |
| } | |
| bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, | |
| const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, | |
| bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) | |
| REQUIRES(mFlinger->mStateLock) { | |
| SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false")); | |
| if (mDrawingState.buffer) { | |
| releasePreviousBuffer(); | |
| } else if (buffer) { | |
| // if we are latching a buffer for the first time then clear the mLastLatchTime since | |
| // we don't want to incorrectly classify a frame if we miss the desired present time. | |
| updateLastLatchTime(0); | |
| } | |
| mDrawingState.desiredPresentTime = desiredPresentTime; | |
| mDrawingState.isAutoTimestamp = isAutoTimestamp; | |
| mDrawingState.latchedVsyncId = info.vsyncId; | |
| mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection; | |
| if (!buffer) { | |
| resetDrawingStateBufferInfo(); | |
| setTransactionFlags(eTransactionNeeded); | |
| mDrawingState.bufferSurfaceFrameTX = nullptr; | |
| setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode); | |
| return true; | |
| } else { | |
| // release sideband stream if it exists and a non null buffer is being set | |
| if (mDrawingState.sidebandStream != nullptr) { | |
| setSidebandStream(nullptr, info, postTime, gameMode); | |
| } | |
| } | |
| mDrawingState.producerId = bufferData.producerId; | |
| mDrawingState.frameNumber = bufferData.frameNumber; | |
| SFTRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), bufferData.frameNumber); | |
| mDrawingState.releaseBufferListener = bufferData.releaseBufferListener; | |
| mDrawingState.previousBuffer = std::move(mDrawingState.buffer); | |
| mDrawingState.buffer = std::move(buffer); | |
| 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(); | |
| } | |
| setTransactionFlags(eTransactionNeeded); | |
| const int32_t layerId = getSequence(); | |
| mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(), | |
| mOwnerUid, postTime, gameMode); | |
| setFrameTimelineVsyncForBufferTransaction(info, postTime, gameMode); | |
| if (bufferData.dequeueTime > 0) { | |
| const uint64_t bufferId = mDrawingState.buffer->getId(); | |
| mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str()); | |
| mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, bufferData.frameNumber, | |
| bufferData.dequeueTime, | |
| FrameTracer::FrameEvent::DEQUEUE); | |
| mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, bufferData.frameNumber, postTime, | |
| FrameTracer::FrameEvent::QUEUE); | |
| } | |
| mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; | |
| // If the layer had been updated a TextureView, this would make sure the present time could be | |
| // same to TextureView update when it's a small dirty, and get the correct heuristic rate. | |
| if (mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) { | |
| if (mDrawingState.useVsyncIdForRefreshRateSelection) { | |
| mUsedVsyncIdForRefreshRateSelection = true; | |
| } | |
| } | |
| return true; | |
| } | |
| void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimestamp) { | |
| mDrawingState.desiredPresentTime = desiredPresentTime; | |
| mDrawingState.isAutoTimestamp = isAutoTimestamp; | |
| } | |
| void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps, nsecs_t now) { | |
| SFTRACE_CALL(); | |
| const nsecs_t presentTime = [&] { | |
| if (!mDrawingState.isAutoTimestamp) { | |
| SFTRACE_FORMAT_INSTANT("desiredPresentTime"); | |
| return mDrawingState.desiredPresentTime; | |
| } | |
| if (mDrawingState.useVsyncIdForRefreshRateSelection) { | |
| const auto prediction = | |
| mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken( | |
| mDrawingState.latchedVsyncId); | |
| if (prediction.has_value()) { | |
| SFTRACE_FORMAT_INSTANT("predictedPresentTime"); | |
| mMaxTimeForUseVsyncId = prediction->presentTime + | |
| scheduler::LayerHistory::kMaxPeriodForHistory.count(); | |
| return prediction->presentTime; | |
| } | |
| } | |
| if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) { | |
| return static_cast<nsecs_t>(0); | |
| } | |
| // If the layer is not an application and didn't set an explicit rate or desiredPresentTime, | |
| // return "0" to tell the layer history that it will use the max refresh rate without | |
| // calculating the adaptive rate. | |
| if (mWindowType != WindowInfo::Type::APPLICATION && | |
| mWindowType != WindowInfo::Type::BASE_APPLICATION) { | |
| return static_cast<nsecs_t>(0); | |
| } | |
| // Return the valid present time only when the layer potentially updated a TextureView so | |
| // LayerHistory could heuristically calculate the rate if the UI is continually updating. | |
| if (mUsedVsyncIdForRefreshRateSelection) { | |
| const auto prediction = | |
| mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken( | |
| mDrawingState.latchedVsyncId); | |
| if (prediction.has_value()) { | |
| if (mMaxTimeForUseVsyncId >= prediction->presentTime) { | |
| return prediction->presentTime; | |
| } | |
| mUsedVsyncIdForRefreshRateSelection = false; | |
| } | |
| } | |
| return static_cast<nsecs_t>(0); | |
| }(); | |
| if (SFTRACE_ENABLED() && presentTime > 0) { | |
| const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now(); | |
| SFTRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str()); | |
| } | |
| mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now, | |
| scheduler::LayerHistory::LayerUpdateType::Buffer); | |
| } | |
| void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps, nsecs_t now) { | |
| const nsecs_t presentTime = | |
| mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime; | |
| mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now, | |
| scheduler::LayerHistory::LayerUpdateType::AnimationTX); | |
| } | |
| bool Layer::setDataspace(ui::Dataspace dataspace) { | |
| if (mDrawingState.dataspace == dataspace) return false; | |
| mDrawingState.dataspace = dataspace; | |
| setTransactionFlags(eTransactionNeeded); | |
| return true; | |
| } | |
| bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio) { | |
| if (mDrawingState.currentHdrSdrRatio == currentBufferRatio && | |
| mDrawingState.desiredHdrSdrRatio == desiredRatio) | |
| return false; | |
| mDrawingState.currentHdrSdrRatio = currentBufferRatio; | |
| mDrawingState.desiredHdrSdrRatio = desiredRatio; | |
| setTransactionFlags(eTransactionNeeded); | |
| return true; | |
| } | |
| bool Layer::setDesiredHdrHeadroom(float desiredRatio) { | |
| if (mDrawingState.desiredHdrSdrRatio == desiredRatio) return false; | |
| mDrawingState.desiredHdrSdrRatio = desiredRatio; | |
| setTransactionFlags(eTransactionNeeded); | |
| return true; | |
| } | |
| bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info, | |
| nsecs_t postTime, gui::GameMode gameMode) | |
| REQUIRES(mFlinger->mStateLock) { | |
| 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; | |
| if (sidebandStream != nullptr && mDrawingState.buffer != nullptr) { | |
| releasePreviousBuffer(); | |
| resetDrawingStateBufferInfo(); | |
| mDrawingState.bufferSurfaceFrameTX = nullptr; | |
| setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode); | |
| } | |
| setTransactionFlags(eTransactionNeeded); | |
| if (!mSidebandStreamChanged.exchange(true)) { | |
| // mSidebandStreamChanged was false | |
| mFlinger->onLayerUpdate(); | |
| } | |
| return true; | |
| } | |
| bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles, | |
| bool willPresent) { | |
| // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return | |
| if (handles.empty()) { | |
| mReleasePreviousBuffer = false; | |
| return false; | |
| } | |
| std::deque<sp<CallbackHandle>> remainingHandles; | |
| 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; | |
| handle->previousFrameNumber = mDrawingState.previousFrameNumber; | |
| handle->previousBuffer = mDrawingState.previousBuffer; | |
| if (mPreviousReleaseBufferEndpoint == handle->listener) { | |
| // Add fence from previous screenshot now so that it can be dispatched to the | |
| // client. | |
| for (auto& [_, future] : mAdditionalPreviousReleaseFences) { | |
| handle->previousReleaseFences.emplace_back(std::move(future)); | |
| } | |
| mAdditionalPreviousReleaseFences.clear(); | |
| } | |
| // 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 | |
| // Queue this handle to be notified below. | |
| remainingHandles.push_back(handle); | |
| } | |
| } | |
| if (!remainingHandles.empty()) { | |
| // Notify the transaction completed threads these handles are done. These are only the | |
| // handles that were not added to the mDrawingState, which will be notified later. | |
| mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles); | |
| } | |
| 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 = SurfaceFlinger::getFrontInternalDisplayRotationFlags(); | |
| if (invTransform & ui::Transform::ROT_90) { | |
| std::swap(bufWidth, bufHeight); | |
| } | |
| } | |
| return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); | |
| } | |
| 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; | |
| } | |
| void Layer::onPreComposition(nsecs_t refreshStartTime) { | |
| for (const auto& handle : mDrawingState.callbackHandles) { | |
| handle->refreshStartTime = refreshStartTime; | |
| } | |
| } | |
| bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) { | |
| if (mSidebandStreamChanged.exchange(false)) { | |
| const State& s(getDrawingState()); | |
| // mSidebandStreamChanged was true | |
| mSidebandStream = s.sidebandStream; | |
| if (mSidebandStream != nullptr) { | |
| setTransactionFlags(eTransactionNeeded); | |
| mFlinger->setTransactionFlags(eTraversalNeeded); | |
| } | |
| recomputeVisibleRegions = true; | |
| return true; | |
| } | |
| return false; | |
| } | |
| void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) REQUIRES(mFlinger->mStateLock) { | |
| const State& s(getDrawingState()); | |
| if (!s.buffer) { | |
| if (bgColorOnly || mBufferInfo.mBuffer) { | |
| 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; | |
| } | |
| void Layer::gatherBufferInfo() { | |
| mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber}; | |
| mPreviousReleaseBufferEndpoint = mBufferInfo.mReleaseBufferEndpoint; | |
| if (!mDrawingState.buffer) { | |
| mBufferInfo = {}; | |
| return; | |
| } | |
| if ((!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer))) { | |
| decrementPendingBufferCount(); | |
| } | |
| mBufferInfo.mBuffer = mDrawingState.buffer; | |
| mBufferInfo.mReleaseBufferEndpoint = mDrawingState.releaseBufferEndpoint; | |
| 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.mTransform = mDrawingState.bufferTransform; | |
| auto lastDataspace = mBufferInfo.mDataspace; | |
| mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace); | |
| if (mBufferInfo.mBuffer != nullptr) { | |
| auto& mapper = GraphicBufferMapper::get(); | |
| // TODO: We should measure if it's faster to do a blind write if we're on newer api levels | |
| // and don't need to possibly remaps buffers. | |
| ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; | |
| status_t err = OK; | |
| { | |
| SFTRACE_NAME("getDataspace"); | |
| err = mapper.getDataspace(mBufferInfo.mBuffer->getBuffer()->handle, &dataspace); | |
| } | |
| if (err != OK || dataspace != mBufferInfo.mDataspace) { | |
| { | |
| SFTRACE_NAME("setDataspace"); | |
| err = mapper.setDataspace(mBufferInfo.mBuffer->getBuffer()->handle, | |
| static_cast<ui::Dataspace>(mBufferInfo.mDataspace)); | |
| } | |
| // Some GPU drivers may cache gralloc metadata which means before we composite we need | |
| // to upsert RenderEngine's caches. Put in a special workaround to be backwards | |
| // compatible with old vendors, with a ticking clock. | |
| static const int32_t kVendorVersion = | |
| base::GetIntProperty("ro.board.api_level", __ANDROID_API_FUTURE__); | |
| if (const auto format = | |
| static_cast<aidl::android::hardware::graphics::common::PixelFormat>( | |
| mBufferInfo.mBuffer->getPixelFormat()); | |
| err == OK && kVendorVersion < __ANDROID_API_U__ && | |
| (format == | |
| aidl::android::hardware::graphics::common::PixelFormat:: | |
| IMPLEMENTATION_DEFINED || | |
| format == aidl::android::hardware::graphics::common::PixelFormat::YCBCR_420_888 || | |
| format == aidl::android::hardware::graphics::common::PixelFormat::YV12 || | |
| format == aidl::android::hardware::graphics::common::PixelFormat::YCBCR_P010)) { | |
| mBufferInfo.mBuffer->remapBuffer(); | |
| } | |
| } | |
| } | |
| if (lastDataspace != mBufferInfo.mDataspace) { | |
| mFlinger->mHdrLayerInfoChanged = true; | |
| } | |
| if (mBufferInfo.mDesiredHdrSdrRatio != mDrawingState.desiredHdrSdrRatio) { | |
| mBufferInfo.mDesiredHdrSdrRatio = mDrawingState.desiredHdrSdrRatio; | |
| mFlinger->mHdrLayerInfoChanged = true; | |
| } | |
| mBufferInfo.mCrop = computeBufferCrop(mDrawingState); | |
| mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse; | |
| // update layer event | |
| // a new layer event instance is added if any defined parameter changes. | |
| if (!mLastLayerEvent.has_value()) { | |
| mLastLayerEvent = | |
| std::make_optional<SurfaceFlinger::LayerEvent>({mOwnerUid, getSequence()}); | |
| } | |
| if (mLastLayerEvent->dataspace != mBufferInfo.mDataspace || | |
| mLastLayerEvent->desiredHdrHeadroom != mBufferInfo.mDesiredHdrSdrRatio || | |
| mLastLayerEvent->useLuts != mDrawingState.useLuts || | |
| mTimeSinceLayerEventsUpdate == std::chrono::steady_clock::time_point::min()) { | |
| const auto currentTime = std::chrono::steady_clock::now(); | |
| if (mTimeSinceLayerEventsUpdate > std::chrono::steady_clock::time_point::min()) { | |
| mLastLayerEvent->timeSinceLastEvent = | |
| std::chrono::duration_cast<std::chrono::milliseconds>( | |
| currentTime - mTimeSinceLayerEventsUpdate); | |
| mFlinger->mLayerEvents.emplace_back(mLastLayerEvent.value()); | |
| } | |
| mTimeSinceLayerEventsUpdate = currentTime; | |
| mLastLayerEvent->dataspace = mBufferInfo.mDataspace; | |
| mLastLayerEvent->useLuts = mDrawingState.useLuts; | |
| mLastLayerEvent->desiredHdrHeadroom = mBufferInfo.mDesiredHdrSdrRatio; | |
| } | |
| } | |
| 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; | |
| } | |
| } | |
| void Layer::decrementPendingBufferCount() { | |
| int32_t pendingBuffers = --mPendingBuffers; | |
| tracePendingBufferCount(pendingBuffers); | |
| } | |
| void Layer::tracePendingBufferCount(int32_t pendingBuffers) { | |
| SFTRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); | |
| } | |
| sp<LayerFE> Layer::getCompositionEngineLayerFE( | |
| const frontend::LayerHierarchy::TraversalPath& path) { | |
| for (auto& [p, layerFE] : mLayerFEs) { | |
| if (p == path) { | |
| return layerFE; | |
| } | |
| } | |
| auto layerFE = mFlinger->getFactory().createLayerFE(mName, this); | |
| mLayerFEs.emplace_back(path, layerFE); | |
| return layerFE; | |
| } | |
| void Layer::onCompositionPresented(const DisplayDevice* display, | |
| const std::shared_ptr<FenceTime>& glDoneFence, | |
| const std::shared_ptr<FenceTime>& presentFence, | |
| const CompositorTiming& compositorTiming, | |
| gui::GameMode gameMode) { | |
| // 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 mDeprecatedFrameTracker. | |
| nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime; | |
| mDeprecatedFrameTracker.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(); | |
| } | |
| } | |
| // The SurfaceFrame's AcquireFence is the same as this. | |
| std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; | |
| if (frameReadyFence->isValid()) { | |
| mDeprecatedFrameTracker.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. | |
| mDeprecatedFrameTracker.setFrameReadyTime(desiredPresentTime); | |
| } | |
| if (display) { | |
| const auto activeMode = display->refreshRateSelector().getActiveMode(); | |
| const Fps refreshRate = activeMode.fps; | |
| const std::optional<Fps> renderRate = | |
| mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); | |
| const auto vote = frameRateToSetFrameRateVotePayload(getFrameRateForLayerTree()); | |
| if (presentFence->isValid()) { | |
| mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, | |
| refreshRate, renderRate, vote, gameMode); | |
| mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, | |
| presentFence, | |
| FrameTracer::FrameEvent::PRESENT_FENCE); | |
| mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); | |
| } else if (const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant()); | |
| displayId.has_value() && mFlinger->getHwComposer().isConnected(*displayId)) { | |
| // The HWC doesn't support present fences, so use the present timestamp instead. | |
| const nsecs_t presentTimestamp = | |
| mFlinger->getHwComposer().getPresentTimestamp(*displayId); | |
| const nsecs_t now = systemTime(CLOCK_MONOTONIC); | |
| const nsecs_t vsyncPeriod = | |
| mFlinger->getHwComposer() | |
| .getDisplayVsyncPeriod(*displayId) | |
| .value_opt() | |
| .value_or(activeMode.modePtr->getVsyncRate().getPeriodNsecs()); | |
| const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod); | |
| mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, | |
| refreshRate, renderRate, vote, gameMode); | |
| mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), | |
| mCurrentFrameNumber, actualPresentTime, | |
| FrameTracer::FrameEvent::PRESENT_FENCE); | |
| mDeprecatedFrameTracker.setActualPresentTime(actualPresentTime); | |
| } | |
| } | |
| mFrameStatsHistorySize++; | |
| mDeprecatedFrameTracker.advanceFrame(); | |
| mBufferInfo.mFrameLatencyNeeded = false; | |
| } | |
| bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) | |
| REQUIRES(mFlinger->mStateLock) { | |
| SFTRACE_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()) { | |
| SFTRACE_NAME("!fenceHasSignaled()"); | |
| mFlinger->onLayerUpdate(); | |
| return false; | |
| } | |
| updateTexImage(latchTime, bgColorOnly); | |
| // Capture the old state of the layer for comparisons later | |
| BufferInfo oldBufferInfo = mBufferInfo; | |
| mPreviousFrameNumber = mCurrentFrameNumber; | |
| mCurrentFrameNumber = mDrawingState.frameNumber; | |
| gatherBufferInfo(); | |
| if (mBufferInfo.mBuffer) { | |
| // We latched a buffer that will be presented soon. Clear the previously presented layer | |
| // stack list. | |
| mPreviouslyPresentedLayerStacks.clear(); | |
| } | |
| if (mDrawingState.buffer == nullptr) { | |
| const bool bufferReleased = oldBufferInfo.mBuffer != nullptr; | |
| recomputeVisibleRegions = bufferReleased; | |
| return bufferReleased; | |
| } | |
| 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.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; | |
| } | |
| } | |
| return true; | |
| } | |
| bool Layer::getTransformToDisplayInverse() const { | |
| return mBufferInfo.mTransformToDisplayInverse; | |
| } | |
| ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { | |
| ui::Dataspace updatedDataspace = dataspace; | |
| // translate legacy dataspaces to modern dataspaces | |
| switch (dataspace) { | |
| // Treat unknown dataspaces as V0_sRGB | |
| case ui::Dataspace::UNKNOWN: | |
| 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; | |
| } | |
| bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, | |
| TrustedPresentationListener const& listener) { | |
| bool hadTrustedPresentationListener = hasTrustedPresentationListener(); | |
| mTrustedPresentationListener = listener; | |
| mTrustedPresentationThresholds = thresholds; | |
| bool haveTrustedPresentationListener = hasTrustedPresentationListener(); | |
| if (!hadTrustedPresentationListener && haveTrustedPresentationListener) { | |
| mFlinger->mNumTrustedPresentationListeners++; | |
| } else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) { | |
| mFlinger->mNumTrustedPresentationListeners--; | |
| } | |
| // Reset trusted presentation states to ensure we start the time again. | |
| mEnteredTrustedPresentationStateTime = -1; | |
| mLastReportedTrustedPresentationState = false; | |
| mLastComputedTrustedPresentationState = false; | |
| // If there's a new trusted presentation listener, the code needs to go through the composite | |
| // path to ensure it recomutes the current state and invokes the TrustedPresentationListener if | |
| // we're already in the requested state. | |
| return haveTrustedPresentationListener; | |
| } | |
| void Layer::setBufferReleaseChannel( | |
| const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) { | |
| mBufferReleaseChannel = channel; | |
| } | |
| void Layer::updateLastLatchTime(nsecs_t latchTime) { | |
| mLastLatchTime = latchTime; | |
| } | |
| void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) { | |
| if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) { | |
| snapshot->isSmallDirty = false; | |
| return; | |
| } | |
| if (mWindowType != WindowInfo::Type::APPLICATION && | |
| mWindowType != WindowInfo::Type::BASE_APPLICATION) { | |
| snapshot->isSmallDirty = false; | |
| return; | |
| } | |
| Rect bounds = snapshot->surfaceDamage.getBounds(); | |
| if (!bounds.isValid()) { | |
| snapshot->isSmallDirty = false; | |
| return; | |
| } | |
| // Transform to screen space. | |
| bounds = snapshot->localTransform.transform(bounds); | |
| // If the damage region is a small dirty, this could give the hint for the layer history that | |
| // it could suppress the heuristic rate when calculating. | |
| snapshot->isSmallDirty = | |
| mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId, | |
| bounds.getWidth() * bounds.getHeight()); | |
| } | |
| } // namespace android | |
| #if defined(__gl_h_) | |
| #error "don't include gl/gl.h in this file" | |
| #endif | |
| #if defined(__gl2_h_) | |
| #error "don't include gl2/gl2.h in this file" | |
| #endif | |
| // TODO(b/129481165): remove the #pragma below and fix conversion issues | |
| #pragma clang diagnostic pop // ignored "-Wconversion" |