/* * Copyright 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define LOG_TAG "FrameEvents" #include #include #include // For CC_[UN]LIKELY #include #include #include #include #include namespace android { using base::StringAppendF; // ============================================================================ // FrameEvents // ============================================================================ bool FrameEvents::hasPostedInfo() const { return FrameEvents::isValidTimestamp(postedTime); } bool FrameEvents::hasRequestedPresentInfo() const { return FrameEvents::isValidTimestamp(requestedPresentTime); } bool FrameEvents::hasLatchInfo() const { return FrameEvents::isValidTimestamp(latchTime); } bool FrameEvents::hasFirstRefreshStartInfo() const { return FrameEvents::isValidTimestamp(firstRefreshStartTime); } bool FrameEvents::hasLastRefreshStartInfo() const { // The last refresh start time may continue to update until a new frame // is latched. We know we have the final value once the release info is set. return addReleaseCalled; } bool FrameEvents::hasDequeueReadyInfo() const { return FrameEvents::isValidTimestamp(dequeueReadyTime); } bool FrameEvents::hasAcquireInfo() const { return acquireFence->isValid(); } bool FrameEvents::hasGpuCompositionDoneInfo() const { // We may not get a gpuCompositionDone in addPostComposite if // client/gles compositing isn't needed. return addPostCompositeCalled; } bool FrameEvents::hasDisplayPresentInfo() const { // We may not get a displayPresent in addPostComposite for HWC1. return addPostCompositeCalled; } bool FrameEvents::hasReleaseInfo() const { return addReleaseCalled; } void FrameEvents::checkFencesForCompletion() { acquireFence->getSignalTime(); gpuCompositionDoneFence->getSignalTime(); displayPresentFence->getSignalTime(); releaseFence->getSignalTime(); } static void dumpFenceTime(std::string& outString, const char* name, bool pending, const FenceTime& fenceTime) { StringAppendF(&outString, "--- %s", name); nsecs_t signalTime = fenceTime.getCachedSignalTime(); if (Fence::isValidTimestamp(signalTime)) { StringAppendF(&outString, "%" PRId64 "\n", signalTime); } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) { outString.append("Pending\n"); } else if (&fenceTime == FenceTime::NO_FENCE.get()){ outString.append("N/A\n"); } else { outString.append("Error\n"); } } void FrameEvents::dump(std::string& outString) const { if (!valid) { return; } StringAppendF(&outString, "-- Frame %" PRIu64 "\n", frameNumber); StringAppendF(&outString, "--- Posted \t%" PRId64 "\n", postedTime); StringAppendF(&outString, "--- Req. Present\t%" PRId64 "\n", requestedPresentTime); outString.append("--- Latched \t"); if (FrameEvents::isValidTimestamp(latchTime)) { StringAppendF(&outString, "%" PRId64 "\n", latchTime); } else { outString.append("Pending\n"); } outString.append("--- Refresh (First)\t"); if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) { StringAppendF(&outString, "%" PRId64 "\n", firstRefreshStartTime); } else { outString.append("Pending\n"); } outString.append("--- Refresh (Last)\t"); if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) { StringAppendF(&outString, "%" PRId64 "\n", lastRefreshStartTime); } else { outString.append("Pending\n"); } dumpFenceTime(outString, "Acquire \t", true, *acquireFence); dumpFenceTime(outString, "GPU Composite Done\t", !addPostCompositeCalled, *gpuCompositionDoneFence); dumpFenceTime(outString, "Display Present \t", !addPostCompositeCalled, *displayPresentFence); outString.append("--- DequeueReady \t"); if (FrameEvents::isValidTimestamp(dequeueReadyTime)) { StringAppendF(&outString, "%" PRId64 "\n", dequeueReadyTime); } else { outString.append("Pending\n"); } dumpFenceTime(outString, "Release \t", true, *releaseFence); } // ============================================================================ // FrameEventHistory // ============================================================================ namespace { struct FrameNumberEqual { explicit FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {} bool operator()(const FrameEvents& frame) { return frame.valid && mFrameNumber == frame.frameNumber; } const uint64_t mFrameNumber; }; } // namespace const size_t FrameEventHistory::MAX_FRAME_HISTORY = sysprop::LibGuiProperties::frame_event_history_size().value_or(8); FrameEventHistory::FrameEventHistory() : mFrames(std::vector(MAX_FRAME_HISTORY)) {} FrameEventHistory::~FrameEventHistory() = default; FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) { auto frame = std::find_if( mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber)); return frame == mFrames.end() ? nullptr : &(*frame); } FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) { *iHint = std::min(*iHint, mFrames.size()); auto hint = mFrames.begin() + *iHint; auto frame = std::find_if( hint, mFrames.end(), FrameNumberEqual(frameNumber)); if (frame == mFrames.end()) { frame = std::find_if( mFrames.begin(), hint, FrameNumberEqual(frameNumber)); if (frame == hint) { return nullptr; } } *iHint = static_cast(std::distance(mFrames.begin(), frame)); return &(*frame); } void FrameEventHistory::checkFencesForCompletion() { for (auto& frame : mFrames) { frame.checkFencesForCompletion(); } } // Uses !|valid| as the MSB. static bool FrameNumberLessThan( const FrameEvents& lhs, const FrameEvents& rhs) { if (lhs.valid == rhs.valid) { return lhs.frameNumber < rhs.frameNumber; } return lhs.valid; } void FrameEventHistory::dump(std::string& outString) const { auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); if (!earliestFrame->valid) { outString.append("-- N/A\n"); return; } for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { frame->dump(outString); } for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) { frame->dump(outString); } } // ============================================================================ // ProducerFrameEventHistory // ============================================================================ ProducerFrameEventHistory::~ProducerFrameEventHistory() = default; nsecs_t ProducerFrameEventHistory::snapToNextTick( nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) { nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval; // Integer modulo rounds towards 0 and not -inf before taking the remainder, // so adjust the offset if it is negative. if (tickOffset < 0) { tickOffset += tickInterval; } return timestamp + tickOffset; } nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline( const nsecs_t now) const{ return snapToNextTick( now, mCompositorTiming.deadline, mCompositorTiming.interval); } void ProducerFrameEventHistory::updateAcquireFence( uint64_t frameNumber, std::shared_ptr&& acquire) { FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset); if (frame == nullptr) { ALOGE("updateAcquireFence: Did not find frame."); return; } if (acquire->isValid()) { mAcquireTimeline.push(acquire); frame->acquireFence = std::move(acquire); } else { // If there isn't an acquire fence, assume that buffer was // ready for the consumer when posted. frame->acquireFence = std::make_shared(frame->postedTime); } } void ProducerFrameEventHistory::applyDelta( const FrameEventHistoryDelta& delta) { mCompositorTiming = delta.mCompositorTiming; for (auto& d : delta.mDeltas) { // Avoid out-of-bounds access. if (CC_UNLIKELY(d.mIndex >= mFrames.size())) { ALOGE("applyDelta: Bad index."); return; } FrameEvents& frame = mFrames[d.mIndex]; frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0; frame.addReleaseCalled = d.mAddReleaseCalled != 0; frame.postedTime = d.mPostedTime; frame.requestedPresentTime = d.mRequestedPresentTime; frame.latchTime = d.mLatchTime; frame.firstRefreshStartTime = d.mFirstRefreshStartTime; frame.lastRefreshStartTime = d.mLastRefreshStartTime; frame.dequeueReadyTime = d.mDequeueReadyTime; if (frame.frameNumber != d.mFrameNumber) { // We got a new frame. Initialize some of the fields. frame.frameNumber = d.mFrameNumber; frame.acquireFence = FenceTime::NO_FENCE; frame.gpuCompositionDoneFence = FenceTime::NO_FENCE; frame.displayPresentFence = FenceTime::NO_FENCE; frame.releaseFence = FenceTime::NO_FENCE; // The consumer only sends valid frames. frame.valid = true; } applyFenceDelta(&mGpuCompositionDoneTimeline, &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence); applyFenceDelta(&mPresentTimeline, &frame.displayPresentFence, d.mDisplayPresentFence); applyFenceDelta(&mReleaseTimeline, &frame.releaseFence, d.mReleaseFence); } } void ProducerFrameEventHistory::updateSignalTimes() { mAcquireTimeline.updateSignalTimes(); mGpuCompositionDoneTimeline.updateSignalTimes(); mPresentTimeline.updateSignalTimes(); mReleaseTimeline.updateSignalTimes(); } void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline, std::shared_ptr* dst, const FenceTime::Snapshot& src) const { if (CC_UNLIKELY(dst == nullptr || dst->get() == nullptr)) { ALOGE("applyFenceDelta: dst is null."); return; } switch (src.state) { case FenceTime::Snapshot::State::EMPTY: return; case FenceTime::Snapshot::State::FENCE: ALOGE_IF((*dst)->isValid(), "applyFenceDelta: Unexpected fence."); *dst = createFenceTime(src.fence); timeline->push(*dst); return; case FenceTime::Snapshot::State::SIGNAL_TIME: if ((*dst)->isValid()) { (*dst)->applyTrustedSnapshot(src); } else { *dst = std::make_shared(src.signalTime); } return; } } std::shared_ptr ProducerFrameEventHistory::createFenceTime( const sp& fence) const { return std::make_shared(fence); } // ============================================================================ // ConsumerFrameEventHistory // ============================================================================ ConsumerFrameEventHistory::ConsumerFrameEventHistory() : mFramesDirty(std::vector(MAX_FRAME_HISTORY)) {} ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; void ConsumerFrameEventHistory::onDisconnect() { mCurrentConnectId++; mProducerWantsEvents = false; } void ConsumerFrameEventHistory::setProducerWantsEvents() { mProducerWantsEvents = true; } void ConsumerFrameEventHistory::initializeCompositorTiming( const CompositorTiming& compositorTiming) { mCompositorTiming = compositorTiming; } void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) { // Overwrite all fields of the frame with default values unless set here. FrameEvents newTimestamps; newTimestamps.connectId = mCurrentConnectId; newTimestamps.frameNumber = newEntry.frameNumber; newTimestamps.postedTime = newEntry.postedTime; newTimestamps.requestedPresentTime = newEntry.requestedPresentTime; newTimestamps.acquireFence = newEntry.acquireFence; newTimestamps.valid = true; mFrames[mQueueOffset] = newTimestamps; // Note: We avoid sending the acquire fence back to the caller since // they have the original one already, so there is no need to set the // acquire dirty bit. mFramesDirty[mQueueOffset].setDirty(); mQueueOffset = (mQueueOffset + 1) % mFrames.size(); } void ConsumerFrameEventHistory::addLatch( uint64_t frameNumber, nsecs_t latchTime) { FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame."); return; } frame->latchTime = latchTime; mFramesDirty[mCompositionOffset].setDirty(); } void ConsumerFrameEventHistory::addPreComposition( uint64_t frameNumber, nsecs_t refreshStartTime) { FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addPreComposition: Did not find frame."); return; } frame->lastRefreshStartTime = refreshStartTime; mFramesDirty[mCompositionOffset].setDirty(); if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) { frame->firstRefreshStartTime = refreshStartTime; mFramesDirty[mCompositionOffset].setDirty(); } } void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber, const std::shared_ptr& gpuCompositionDone, const std::shared_ptr& displayPresent, const CompositorTiming& compositorTiming) { mCompositorTiming = compositorTiming; FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addPostComposition: Did not find frame."); return; } // Only get GPU and present info for the first composite. if (!frame->addPostCompositeCalled) { frame->addPostCompositeCalled = true; frame->gpuCompositionDoneFence = gpuCompositionDone; mFramesDirty[mCompositionOffset].setDirty(); if (!frame->displayPresentFence->isValid()) { frame->displayPresentFence = displayPresent; mFramesDirty[mCompositionOffset].setDirty(); } } } void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime, std::shared_ptr&& release) { FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addRelease: Did not find frame."); return; } frame->addReleaseCalled = true; frame->dequeueReadyTime = dequeueReadyTime; frame->releaseFence = std::move(release); mFramesDirty[mReleaseOffset].setDirty(); } void ConsumerFrameEventHistory::getFrameDelta(FrameEventHistoryDelta* delta, const std::vector::iterator& frame) { mProducerWantsEvents = true; size_t i = static_cast(std::distance(mFrames.begin(), frame)); if (mFramesDirty[i].anyDirty()) { // Make sure only to send back deltas for the current connection // since the producer won't have the correct state to apply a delta // from a previous connection. if (mFrames[i].connectId == mCurrentConnectId) { delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]); } mFramesDirty[i].reset(); } } void ConsumerFrameEventHistory::getAndResetDelta( FrameEventHistoryDelta* delta) { mProducerWantsEvents = true; delta->mCompositorTiming = mCompositorTiming; // Write these in order of frame number so that it is easy to // add them to a FenceTimeline in the proper order producer side. delta->mDeltas.reserve(mFramesDirty.size()); auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { getFrameDelta(delta, frame); } for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) { getFrameDelta(delta, frame); } } // ============================================================================ // FrameEventsDelta // ============================================================================ FrameEventsDelta::FrameEventsDelta( size_t index, const FrameEvents& frameTimestamps, const FrameEventDirtyFields& dirtyFields) : mIndex(index), mFrameNumber(frameTimestamps.frameNumber), mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled), mAddReleaseCalled(frameTimestamps.addReleaseCalled), mPostedTime(frameTimestamps.postedTime), mRequestedPresentTime(frameTimestamps.requestedPresentTime), mLatchTime(frameTimestamps.latchTime), mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime), mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime), mDequeueReadyTime(frameTimestamps.dequeueReadyTime) { if (dirtyFields.isDirty()) { mGpuCompositionDoneFence = frameTimestamps.gpuCompositionDoneFence->getSnapshot(); } if (dirtyFields.isDirty()) { mDisplayPresentFence = frameTimestamps.displayPresentFence->getSnapshot(); } if (dirtyFields.isDirty()) { mReleaseFence = frameTimestamps.releaseFence->getSnapshot(); } } constexpr size_t FrameEventsDelta::minFlattenedSize() { return sizeof(FrameEventsDelta::mFrameNumber) + sizeof(uint16_t) + // mIndex sizeof(uint8_t) + // mAddPostCompositeCalled sizeof(uint8_t) + // mAddReleaseCalled sizeof(FrameEventsDelta::mPostedTime) + sizeof(FrameEventsDelta::mRequestedPresentTime) + sizeof(FrameEventsDelta::mLatchTime) + sizeof(FrameEventsDelta::mFirstRefreshStartTime) + sizeof(FrameEventsDelta::mLastRefreshStartTime) + sizeof(FrameEventsDelta::mDequeueReadyTime); } // Flattenable implementation size_t FrameEventsDelta::getFlattenedSize() const { auto fences = allFences(this); return minFlattenedSize() + std::accumulate(fences.begin(), fences.end(), size_t(0), [](size_t a, const FenceTime::Snapshot* fence) { return a + fence->getFlattenedSize(); }); } size_t FrameEventsDelta::getFdCount() const { auto fences = allFences(this); return std::accumulate(fences.begin(), fences.end(), size_t(0), [](size_t a, const FenceTime::Snapshot* fence) { return a + fence->getFdCount(); }); } status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { if (size < getFlattenedSize() || count < getFdCount()) { return NO_MEMORY; } if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY || mIndex > std::numeric_limits::max()) { return BAD_VALUE; } FlattenableUtils::write(buffer, size, mFrameNumber); // These are static_cast to uint16_t/uint8_t for alignment. FlattenableUtils::write(buffer, size, static_cast(mIndex)); FlattenableUtils::write( buffer, size, static_cast(mAddPostCompositeCalled)); FlattenableUtils::write( buffer, size, static_cast(mAddReleaseCalled)); FlattenableUtils::write(buffer, size, mPostedTime); FlattenableUtils::write(buffer, size, mRequestedPresentTime); FlattenableUtils::write(buffer, size, mLatchTime); FlattenableUtils::write(buffer, size, mFirstRefreshStartTime); FlattenableUtils::write(buffer, size, mLastRefreshStartTime); FlattenableUtils::write(buffer, size, mDequeueReadyTime); // Fences for (auto fence : allFences(this)) { status_t status = fence->flatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < minFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, mFrameNumber); // These were written as uint16_t/uint8_t for alignment. uint16_t temp16 = 0; FlattenableUtils::read(buffer, size, temp16); mIndex = temp16; if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } uint8_t temp8 = 0; FlattenableUtils::read(buffer, size, temp8); mAddPostCompositeCalled = static_cast(temp8); FlattenableUtils::read(buffer, size, temp8); mAddReleaseCalled = static_cast(temp8); FlattenableUtils::read(buffer, size, mPostedTime); FlattenableUtils::read(buffer, size, mRequestedPresentTime); FlattenableUtils::read(buffer, size, mLatchTime); FlattenableUtils::read(buffer, size, mFirstRefreshStartTime); FlattenableUtils::read(buffer, size, mLastRefreshStartTime); FlattenableUtils::read(buffer, size, mDequeueReadyTime); // Fences for (auto fence : allFences(this)) { status_t status = fence->unflatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } // ============================================================================ // FrameEventHistoryDelta // ============================================================================ FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( FrameEventHistoryDelta&& src) noexcept { mCompositorTiming = src.mCompositorTiming; if (CC_UNLIKELY(!mDeltas.empty())) { ALOGE("FrameEventHistoryDelta assign clobbering history."); } mDeltas = std::move(src.mDeltas); return *this; } constexpr size_t FrameEventHistoryDelta::minFlattenedSize() { return sizeof(uint32_t) + // mDeltas.size() sizeof(mCompositorTiming); } size_t FrameEventHistoryDelta::getFlattenedSize() const { return minFlattenedSize() + std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0), [](size_t a, const FrameEventsDelta& delta) { return a + delta.getFlattenedSize(); }); } size_t FrameEventHistoryDelta::getFdCount() const { return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0), [](size_t a, const FrameEventsDelta& delta) { return a + delta.getFdCount(); }); } status_t FrameEventHistoryDelta::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } if (size < getFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, mCompositorTiming); FlattenableUtils::write( buffer, size, static_cast(mDeltas.size())); for (auto& d : mDeltas) { status_t status = d.flatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } status_t FrameEventHistoryDelta::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < minFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, mCompositorTiming); uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } mDeltas.resize(deltaCount); for (auto& d : mDeltas) { status_t status = d.unflatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } } // namespace android