Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9e57599 authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "Move frame history into jank tracker"

parents f29fdf1d 34781b25
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -202,6 +202,7 @@ cc_defaults {
        "PathTessellator.cpp",
        "PixelBuffer.cpp",
        "ProfileData.cpp",
        "ProfileDataContainer.cpp",
        "ProfileRenderer.cpp",
        "Program.cpp",
        "ProgramCache.cpp",
+29 −60
Original line number Diff line number Diff line
@@ -65,11 +65,8 @@ static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
// and filter it out of the frame profile data
static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;

JankTracker::JankTracker(const DisplayInfo& displayInfo) {
    // By default this will use malloc memory. It may be moved later to ashmem
    // if there is shared space for it and a request comes in to do that.
    mData = new ProfileData;
    reset();
JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) {
    mGlobalData = globalData;
    nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
#if USE_HWC2
    nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
@@ -89,60 +86,6 @@ JankTracker::JankTracker(const DisplayInfo& displayInfo) {
    setFrameInterval(frameIntervalNanos);
}

JankTracker::~JankTracker() {
    freeData();
}

void JankTracker::freeData() {
    if (mIsMapped) {
        munmap(mData, sizeof(ProfileData));
    } else {
        delete mData;
    }
    mIsMapped = false;
    mData = nullptr;
}

void JankTracker::rotateStorage() {
    // If we are mapped we want to stop using the ashmem backend and switch to malloc
    // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
    // If we aren't sitting on top of ashmem then just do a reset() as it's functionally
    // equivalent do a free, malloc, reset.
    if (mIsMapped) {
        freeData();
        mData = new ProfileData;
    }
    reset();
}

void JankTracker::switchStorageToAshmem(int ashmemfd) {
    int regionSize = ashmem_get_size_region(ashmemfd);
    if (regionSize < 0) {
        int err = errno;
        ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err));
        return;
    }
    if (regionSize < static_cast<int>(sizeof(ProfileData))) {
        ALOGW("Ashmem region is too small! Received %d, required %u",
                regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
        return;
    }
    ProfileData* newData = reinterpret_cast<ProfileData*>(
            mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
            MAP_SHARED, ashmemfd, 0));
    if (newData == MAP_FAILED) {
        int err = errno;
        ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
                ashmemfd, err);
        return;
    }

    newData->mergeWith(*mData);
    freeData();
    mData = newData;
    mIsMapped = true;
}

void JankTracker::setFrameInterval(nsecs_t frameInterval) {
    mFrameInterval = frameInterval;
    mThresholds[kMissedVsync] = 1;
@@ -166,7 +109,7 @@ void JankTracker::setFrameInterval(nsecs_t frameInterval) {

}

void JankTracker::addFrame(const FrameInfo& frame) {
void JankTracker::finishFrame(const FrameInfo& frame) {
    // Fast-path for jank-free frames
    int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
    if (mDequeueTimeForgiveness
@@ -187,6 +130,7 @@ void JankTracker::addFrame(const FrameInfo& frame) {
    }
    LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
    mData->reportFrame(totalDuration);
    (*mGlobalData)->reportFrame(totalDuration);

    // Keep the fast path as fast as possible.
    if (CC_LIKELY(totalDuration < mFrameInterval)) {
@@ -199,11 +143,13 @@ void JankTracker::addFrame(const FrameInfo& frame) {
    }

    mData->reportJank();
    (*mGlobalData)->reportJank();

    for (int i = 0; i < NUM_BUCKETS; i++) {
        int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
        if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
            mData->reportJankType((JankType) i);
            (*mGlobalData)->reportJankType((JankType) i);
        }
    }
}
@@ -228,8 +174,31 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, co
    dprintf(fd, "\n");
}

void JankTracker::dumpFrames(int fd) {
    FILE* file = fdopen(fd, "a");
    fprintf(file, "\n\n---PROFILEDATA---\n");
    for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
        fprintf(file, "%s", FrameInfoNames[i].c_str());
        fprintf(file, ",");
    }
    for (size_t i = 0; i < mFrames.size(); i++) {
        FrameInfo& frame = mFrames[i];
        if (frame[FrameInfoIndex::SyncStart] == 0) {
            continue;
        }
        fprintf(file, "\n");
        for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
            fprintf(file, "%" PRId64 ",", frame[i]);
        }
    }
    fprintf(file, "\n---PROFILEDATA---\n\n");
    fflush(file);
}

void JankTracker::reset() {
    mFrames.clear();
    mData->reset();
    (*mGlobalData)->reset();
    sFrameStart = Properties::filterOutTestOverhead
            ? FrameInfoIndex::HandleInputStart
            : FrameInfoIndex::IntendedVsync;
+14 −11
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include "FrameInfo.h"
#include "ProfileData.h"
#include "ProfileDataContainer.h"
#include "renderthread/TimeLord.h"
#include "utils/RingBuffer.h"

@@ -48,26 +49,25 @@ struct ProfileDataDescription {
// TODO: Replace DrawProfiler with this
class JankTracker {
public:
    explicit JankTracker(const DisplayInfo& displayInfo);
    ~JankTracker();
    explicit JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo);

    void setDescription(JankTrackerType type, const std::string&& name) {
        mDescription.type = type;
        mDescription.name = name;
    }

    void addFrame(const FrameInfo& frame);
    FrameInfo* startFrame() { return &mFrames.next(); }
    void finishFrame(const FrameInfo& frame);

    void dump(int fd) { dumpData(fd, &mDescription, mData); }
    void dumpStats(int fd) { dumpData(fd, &mDescription, mData.get()); }
    void dumpFrames(int fd);
    void reset();

    void rotateStorage();
    void switchStorageToAshmem(int ashmemfd);

    uint32_t findPercentile(int p) { return mData->findPercentile(p); }
    // Exposed for FrameInfoVisualizer
    // TODO: Figure out a better way to handle this
    RingBuffer<FrameInfo, 120>& frames() { return mFrames; }

private:
    void freeData();
    void setFrameInterval(nsecs_t frameIntervalNanos);

    static void dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data);
@@ -82,9 +82,12 @@ private:
    // This is only used if we are in pipelined mode and are using HWC2,
    // otherwise it's 0.
    nsecs_t mDequeueTimeForgiveness = 0;
    ProfileData* mData;
    bool mIsMapped = false;
    ProfileDataContainer mData;
    ProfileDataContainer* mGlobalData;
    ProfileDataDescription mDescription;

    // Ring buffer large enough for 2 seconds worth of frames
    RingBuffer<FrameInfo, 120> mFrames;
};

} /* namespace uirenderer */
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 "ProfileDataContainer.h"

#include <log/log.h>
#include <cutils/ashmem.h>

#include <sys/mman.h>

namespace android {
namespace uirenderer {

void ProfileDataContainer::freeData() {
    if (mIsMapped) {
        munmap(mData, sizeof(ProfileData));
    } else {
        delete mData;
    }
    mIsMapped = false;
    mData = nullptr;
}

void ProfileDataContainer::rotateStorage() {
    // If we are mapped we want to stop using the ashmem backend and switch to malloc
    // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
    // If we aren't sitting on top of ashmem then just do a reset() as it's functionally
    // equivalent do a free, malloc, reset.
    if (mIsMapped) {
        freeData();
        mData = new ProfileData;
    }
    mData->reset();
}

void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
    int regionSize = ashmem_get_size_region(ashmemfd);
    if (regionSize < 0) {
        int err = errno;
        ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err));
        return;
    }
    if (regionSize < static_cast<int>(sizeof(ProfileData))) {
        ALOGW("Ashmem region is too small! Received %d, required %u",
                regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
        return;
    }
    ProfileData* newData = reinterpret_cast<ProfileData*>(
            mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
                    MAP_SHARED, ashmemfd, 0));
    if (newData == MAP_FAILED) {
        int err = errno;
        ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
                ashmemfd, err);
        return;
    }

    newData->mergeWith(*mData);
    freeData();
    mData = newData;
    mIsMapped = true;
}

} /* namespace uirenderer */
} /* namespace android */
 No newline at end of file
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

#pragma once

#include "ProfileData.h"
#include "utils/Macros.h"

namespace android {
namespace uirenderer {

class ProfileDataContainer {
    PREVENT_COPY_AND_ASSIGN(ProfileDataContainer);
public:
    explicit ProfileDataContainer() {}

    ~ProfileDataContainer() { freeData(); }

    void rotateStorage();
    void switchStorageToAshmem(int ashmemfd);

    ProfileData* get() { return mData; }
    ProfileData* operator->() { return mData; }

private:
    void freeData();

    // By default this will use malloc memory. It may be moved later to ashmem
    // if there is shared space for it and a request comes in to do that.
    ProfileData* mData = new ProfileData;
    bool mIsMapped = false;
};

} /* namespace uirenderer */
} /* namespace android */
 No newline at end of file
Loading