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

Commit 34781b25 authored by John Reck's avatar John Reck
Browse files

Move frame history into jank tracker

Test: hwui_unit_tests & manual
Change-Id: If761947652750640268217cd8cd97c8382441b44
parent 60b108d7
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