/* * Copyright 2015 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "FrameRenderTracker" #include #include #include #include namespace android { FrameRenderTracker::FrameRenderTracker() : mLastRenderTimeNs(-1), mComponentName("unknown component") { } FrameRenderTracker::~FrameRenderTracker() { } void FrameRenderTracker::setComponentName(const AString &componentName) { mComponentName = componentName; } void FrameRenderTracker::clear(nsecs_t lastRenderTimeNs) { mRenderQueue.clear(); mLastRenderTimeNs = lastRenderTimeNs; } void FrameRenderTracker::onFrameQueued( int64_t mediaTimeUs, const sp &graphicBuffer, const sp &fence) { mRenderQueue.emplace_back(mediaTimeUs, graphicBuffer, fence); } FrameRenderTracker::Info *FrameRenderTracker::updateInfoForDequeuedBuffer( ANativeWindowBuffer *buf, int fenceFd, int index) { if (index < 0) { return NULL; } // see if this is a buffer that was to be rendered std::list::iterator renderInfo = mRenderQueue.end(); for (std::list::iterator it = mRenderQueue.begin(); it != mRenderQueue.end(); ++it) { if (it->mGraphicBuffer->handle == buf->handle) { renderInfo = it; break; } } if (renderInfo == mRenderQueue.end()) { // could have been canceled after fence has signaled return NULL; } if (renderInfo->mIndex >= 0) { // buffer has been dequeued before, so there is nothing to do return NULL; } // was this frame dropped (we could also infer this if the fence is invalid or a dup of // the queued fence; however, there is no way to figure that out.) if (fenceFd < 0) { // frame is new or was dropped mRenderQueue.erase(renderInfo); return NULL; } // store dequeue fence and buffer index renderInfo->mFence = new Fence(::dup(fenceFd)); renderInfo->mIndex = index; return &*renderInfo; } status_t FrameRenderTracker::onFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) { // ensure monotonic timestamps if (mLastRenderTimeNs >= systemNano) { ALOGW("[%s] Ignoring out of order/stale system nano %lld for media time %lld from codec.", mComponentName.c_str(), (long long)systemNano, (long long)mediaTimeUs); return BAD_VALUE; } nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (systemNano > now) { ALOGW("[%s] Ignoring system nano %lld in the future for media time %lld from codec.", mComponentName.c_str(), (long long)systemNano, (long long)mediaTimeUs); return OK; } mRenderQueue.emplace_back(mediaTimeUs, systemNano); mLastRenderTimeNs = systemNano; return OK; } std::list FrameRenderTracker::checkFencesAndGetRenderedFrames( const FrameRenderTracker::Info *until, bool dropIncomplete) { std::list done; // complete any frames queued prior to this and drop any incomplete ones if requested for (std::list::iterator it = mRenderQueue.begin(); it != mRenderQueue.end(); ) { bool drop = false; // whether to drop each frame if (it->mIndex < 0) { // frame not yet dequeued (or already rendered on a tunneled surface) drop = dropIncomplete; } else if (it->mFence != NULL) { // check if fence signaled nsecs_t signalTime = it->mFence->getSignalTime(); if (signalTime < 0) { // invalid fence drop = true; } else if (signalTime == INT64_MAX) { // unsignaled fence drop = dropIncomplete; } else { // signaled // save render time it->mFence.clear(); it->mRenderTimeNs = signalTime; } } bool foundFrame = (Info *)&*it == until; // Return frames with signaled fences at the start of the queue, as they are // in submit order, and we don't have to wait for any in-between frames. // Also return any dropped frames. if (drop || (it->mFence == NULL && it == mRenderQueue.begin())) { // (unrendered) dropped frames have their mRenderTimeNs still set to -1 done.splice(done.end(), mRenderQueue, it++); } else { ++it; } if (foundFrame) { break; } } return done; } void FrameRenderTracker::untrackFrame(const FrameRenderTracker::Info *info, ssize_t index) { if (info == NULL && index == SSIZE_MAX) { // nothing to do return; } for (std::list::iterator it = mRenderQueue.begin(); it != mRenderQueue.end(); ) { if (&*it == info) { mRenderQueue.erase(it++); } else { if (it->mIndex > index) { --(it->mIndex); } ++it; } } } void FrameRenderTracker::dumpRenderQueue() const { ALOGI("[%s] Render Queue: (last render time: %lldns)", mComponentName.c_str(), (long long)mLastRenderTimeNs); for (std::list::const_iterator it = mRenderQueue.cbegin(); it != mRenderQueue.cend(); ++it) { if (it->mFence == NULL) { ALOGI(" RENDERED: handle: %p, media time: %lldus, index: %zd, render time: %lldns", it->mGraphicBuffer == NULL ? NULL : it->mGraphicBuffer->handle, (long long)it->mMediaTimeUs, it->mIndex, (long long)it->mRenderTimeNs); } else if (it->mIndex < 0) { ALOGI(" QUEUED: handle: %p, media time: %lldus, fence: %s", it->mGraphicBuffer->handle, (long long)it->mMediaTimeUs, it->mFence->isValid() ? "YES" : "NO"); } else { ALOGI(" DEQUEUED: handle: %p, media time: %lldus, index: %zd", it->mGraphicBuffer->handle, (long long)it->mMediaTimeUs, it->mIndex); } } } } // namespace android