| /* |
| * 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. |
| */ |
| |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #include <inttypes.h> |
| #include "FenceTracker.h" |
| #include "Layer.h" |
| #include <utils/Trace.h> |
| |
| namespace android { |
| |
| FenceTracker::FenceTracker() : |
| mFrameCounter(0), |
| mOffset(0), |
| mFrames(), |
| mMutex() { |
| } |
| |
| void FenceTracker::dump(String8* outString) { |
| Mutex::Autolock lock(mMutex); |
| checkFencesForCompletion(); |
| |
| for (size_t i = 0; i < MAX_FRAME_HISTORY; i++) { |
| int index = (mOffset + i) % MAX_FRAME_HISTORY; |
| const FrameRecord& frame = mFrames[index]; |
| |
| outString->appendFormat("Frame %" PRIu64 "\n", frame.frameId); |
| outString->appendFormat("- Refresh start\t%" PRId64 "\n", |
| frame.refreshStartTime); |
| |
| if (frame.glesCompositionDoneTime) { |
| outString->appendFormat("- GLES done\t%" PRId64 "\n", |
| frame.glesCompositionDoneTime); |
| } else if (frame.glesCompositionDoneFence != Fence::NO_FENCE) { |
| outString->append("- GLES done\tNot signaled\n"); |
| } |
| if (frame.retireTime) { |
| outString->appendFormat("- Retire\t%" PRId64 "\n", |
| frame.retireTime); |
| } else { |
| outString->append("- Retire\tNot signaled\n"); |
| } |
| for (const auto& kv : frame.layers) { |
| const LayerRecord& layer = kv.second; |
| outString->appendFormat("-- %s\n", layer.name.string()); |
| outString->appendFormat("---- Frame # %" PRIu64 " (%s)\n", |
| layer.frameNumber, |
| layer.isGlesComposition ? "GLES" : "HWC"); |
| outString->appendFormat("---- Req.Present.\t%" PRId64 "\n", |
| layer.requestedPresentTime); |
| if (layer.acquireTime) { |
| outString->appendFormat("---- Acquire\t%" PRId64 "\n", |
| layer.acquireTime); |
| } else { |
| outString->append("---- Acquire\tNot signaled\n"); |
| } |
| if (layer.releaseTime) { |
| outString->appendFormat("---- Release\t%" PRId64 "\n", |
| layer.releaseTime); |
| } else { |
| outString->append("---- Release\tNot signaled\n"); |
| } |
| } |
| } |
| } |
| |
| static inline bool isValidTimestamp(nsecs_t time) { |
| return time > 0 && time < INT64_MAX; |
| } |
| |
| void FenceTracker::checkFencesForCompletion() { |
| ATRACE_CALL(); |
| for (auto& frame : mFrames) { |
| if (frame.retireFence != Fence::NO_FENCE) { |
| nsecs_t time = frame.retireFence->getSignalTime(); |
| if (isValidTimestamp(time)) { |
| frame.retireTime = time; |
| frame.retireFence = Fence::NO_FENCE; |
| } |
| } |
| if (frame.glesCompositionDoneFence != Fence::NO_FENCE) { |
| nsecs_t time = frame.glesCompositionDoneFence->getSignalTime(); |
| if (isValidTimestamp(time)) { |
| frame.glesCompositionDoneTime = time; |
| frame.glesCompositionDoneFence = Fence::NO_FENCE; |
| } |
| } |
| for (auto& kv : frame.layers) { |
| LayerRecord& layer = kv.second; |
| if (layer.acquireFence != Fence::NO_FENCE) { |
| nsecs_t time = layer.acquireFence->getSignalTime(); |
| if (isValidTimestamp(time)) { |
| layer.acquireTime = time; |
| layer.acquireFence = Fence::NO_FENCE; |
| } |
| } |
| if (layer.releaseFence != Fence::NO_FENCE) { |
| nsecs_t time = layer.releaseFence->getSignalTime(); |
| if (isValidTimestamp(time)) { |
| layer.releaseTime = time; |
| layer.releaseFence = Fence::NO_FENCE; |
| } |
| } |
| } |
| } |
| } |
| |
| void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence, |
| const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence) { |
| ATRACE_CALL(); |
| Mutex::Autolock lock(mMutex); |
| FrameRecord& frame = mFrames[mOffset]; |
| FrameRecord& prevFrame = mFrames[(mOffset + MAX_FRAME_HISTORY - 1) % |
| MAX_FRAME_HISTORY]; |
| frame.layers.clear(); |
| |
| bool wasGlesCompositionDone = false; |
| const size_t count = layers.size(); |
| for (size_t i = 0; i < count; i++) { |
| String8 name; |
| uint64_t frameNumber; |
| bool glesComposition; |
| nsecs_t requestedPresentTime; |
| sp<Fence> acquireFence; |
| sp<Fence> prevReleaseFence; |
| int32_t layerId = layers[i]->getSequence(); |
| |
| layers[i]->getFenceData(&name, &frameNumber, &glesComposition, |
| &requestedPresentTime, &acquireFence, &prevReleaseFence); |
| #ifdef USE_HWC2 |
| if (glesComposition) { |
| frame.layers.emplace(std::piecewise_construct, |
| std::forward_as_tuple(layerId), |
| std::forward_as_tuple(name, frameNumber, glesComposition, |
| requestedPresentTime, 0, 0, acquireFence, |
| prevReleaseFence)); |
| wasGlesCompositionDone = true; |
| } else { |
| frame.layers.emplace(std::piecewise_construct, |
| std::forward_as_tuple(layerId), |
| std::forward_as_tuple(name, frameNumber, glesComposition, |
| requestedPresentTime, 0, 0, acquireFence, Fence::NO_FENCE)); |
| auto prevLayer = prevFrame.layers.find(layerId); |
| if (prevLayer != prevFrame.layers.end()) { |
| prevLayer->second.releaseFence = prevReleaseFence; |
| } |
| } |
| #else |
| frame.layers.emplace(std::piecewise_construct, |
| std::forward_as_tuple(layerId), |
| std::forward_as_tuple(name, frameNumber, glesComposition, |
| requestedPresentTime, 0, 0, acquireFence, |
| glesComposition ? Fence::NO_FENCE : prevReleaseFence)); |
| if (glesComposition) { |
| wasGlesCompositionDone = true; |
| } |
| #endif |
| frame.layers.emplace(std::piecewise_construct, |
| std::forward_as_tuple(layerId), |
| std::forward_as_tuple(name, frameNumber, glesComposition, |
| requestedPresentTime, 0, 0, acquireFence, prevReleaseFence)); |
| } |
| |
| frame.frameId = mFrameCounter; |
| frame.refreshStartTime = refreshStartTime; |
| frame.retireTime = 0; |
| frame.glesCompositionDoneTime = 0; |
| prevFrame.retireFence = retireFence; |
| frame.retireFence = Fence::NO_FENCE; |
| frame.glesCompositionDoneFence = wasGlesCompositionDone ? glDoneFence : |
| Fence::NO_FENCE; |
| |
| mOffset = (mOffset + 1) % MAX_FRAME_HISTORY; |
| mFrameCounter++; |
| } |
| |
| bool FenceTracker::getFrameTimestamps(const Layer& layer, |
| uint64_t frameNumber, FrameTimestamps* outTimestamps) { |
| Mutex::Autolock lock(mMutex); |
| checkFencesForCompletion(); |
| int32_t layerId = layer.getSequence(); |
| |
| size_t i = 0; |
| for (; i < MAX_FRAME_HISTORY; i++) { |
| if (mFrames[i].layers.count(layerId) && |
| mFrames[i].layers[layerId].frameNumber == frameNumber) { |
| break; |
| } |
| } |
| if (i == MAX_FRAME_HISTORY) { |
| return false; |
| } |
| |
| const FrameRecord& frameRecord = mFrames[i]; |
| const LayerRecord& layerRecord = mFrames[i].layers[layerId]; |
| outTimestamps->frameNumber = frameNumber; |
| outTimestamps->requestedPresentTime = layerRecord.requestedPresentTime; |
| outTimestamps->acquireTime = layerRecord.acquireTime; |
| outTimestamps->refreshStartTime = frameRecord.refreshStartTime; |
| outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime; |
| outTimestamps->displayRetireTime = frameRecord.retireTime; |
| outTimestamps->releaseTime = layerRecord.releaseTime; |
| return true; |
| } |
| |
| } // namespace android |