You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
25 KiB
713 lines
25 KiB
/*
|
|
* 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 <gui/FrameTimestamps.h>
|
|
|
|
#define LOG_TAG "FrameEvents"
|
|
|
|
#include <LibGuiProperties.sysprop.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <cutils/compiler.h> // For CC_[UN]LIKELY
|
|
#include <inttypes.h>
|
|
#include <utils/Log.h>
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <numeric>
|
|
|
|
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<FrameEvents>(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<size_t>(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<FenceTime>&& 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<FenceTime>(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<FenceTime>* 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<FenceTime>(src.signalTime);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime(
|
|
const sp<Fence>& fence) const {
|
|
return std::make_shared<FenceTime>(fence);
|
|
}
|
|
|
|
|
|
// ============================================================================
|
|
// ConsumerFrameEventHistory
|
|
// ============================================================================
|
|
|
|
ConsumerFrameEventHistory::ConsumerFrameEventHistory()
|
|
: mFramesDirty(std::vector<FrameEventDirtyFields>(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<FrameEvent::POSTED>();
|
|
|
|
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<FrameEvent::LATCH>();
|
|
}
|
|
|
|
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<FrameEvent::LAST_REFRESH_START>();
|
|
if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) {
|
|
frame->firstRefreshStartTime = refreshStartTime;
|
|
mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
|
|
}
|
|
}
|
|
|
|
void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
|
|
const std::shared_ptr<FenceTime>& gpuCompositionDone,
|
|
const std::shared_ptr<FenceTime>& 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<FrameEvent::GPU_COMPOSITION_DONE>();
|
|
if (!frame->displayPresentFence->isValid()) {
|
|
frame->displayPresentFence = displayPresent;
|
|
mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
|
|
nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& 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<FrameEvent::RELEASE>();
|
|
}
|
|
|
|
void ConsumerFrameEventHistory::getFrameDelta(FrameEventHistoryDelta* delta,
|
|
const std::vector<FrameEvents>::iterator& frame) {
|
|
mProducerWantsEvents = true;
|
|
size_t i = static_cast<size_t>(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<FrameEvent::GPU_COMPOSITION_DONE>()) {
|
|
mGpuCompositionDoneFence =
|
|
frameTimestamps.gpuCompositionDoneFence->getSnapshot();
|
|
}
|
|
if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) {
|
|
mDisplayPresentFence =
|
|
frameTimestamps.displayPresentFence->getSnapshot();
|
|
}
|
|
if (dirtyFields.isDirty<FrameEvent::RELEASE>()) {
|
|
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<uint16_t>::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<uint16_t>(mIndex));
|
|
FlattenableUtils::write(
|
|
buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
|
|
FlattenableUtils::write(
|
|
buffer, size, static_cast<uint8_t>(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<bool>(temp8);
|
|
FlattenableUtils::read(buffer, size, temp8);
|
|
mAddReleaseCalled = static_cast<bool>(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<uint32_t>(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
|