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

Commit 82dbc742 authored by Jamie Gennis's avatar Jamie Gennis
Browse files

SurfaceFlinger: refactor frame time tracking

This change moves the frame time history tracking code out of Layer and into a
new class called FrameTracker.  It also changes the tracking to use signal
timestamps from fences when available for more accurate results.

Change-Id: I323c5f075c58bf86ce363b52af885ad0f6365f2b
parent e64b38fa
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <utils/String8.h>
#include <utils/Timers.h>

struct ANativeWindowBuffer;

@@ -40,6 +41,10 @@ class Fence
public:
    static const sp<Fence> NO_FENCE;

    // TIMEOUT_NEVER may be passed to the wait method to indicate that it
    // should wait indefinitely for the fence to signal.
    enum { TIMEOUT_NEVER = -1 };

    // Construct a new Fence object with an invalid file descriptor.  This
    // should be done when the Fence object will be set up by unflattening
    // serialized data.
@@ -69,10 +74,6 @@ public:
    // the caller and will be included in the log message.
    status_t waitForever(unsigned int warningTimeout, const char* logname);

    // TIMEOUT_NEVER may be passed to the wait method to indicate that it
    // should wait indefinitely for the fence to signal.
    enum { TIMEOUT_NEVER = -1 };

    // merge combines two Fence objects, creating a new Fence object that
    // becomes signaled when both f1 and f2 are signaled (even if f1 or f2 is
    // destroyed before it becomes signaled).  The name argument specifies the
@@ -85,6 +86,12 @@ public:
    // be returned and errno will indicate the problem.
    int dup() const;

    // getSignalTime returns the system monotonic clock time at which the
    // fence transitioned to the signaled state.  If the fence is not signaled
    // then INT64_MAX is returned.  If the fence is invalid or if an error
    // occurs then -1 is returned.
    nsecs_t getSignalTime() const;

    // Flattenable interface
    size_t getFlattenedSize() const;
    size_t getFdCount() const;
+29 −0
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0

 // This is needed for stdint.h to define INT64_MAX in C++
 #define __STDC_LIMIT_MACROS

#include <sync/sync.h>
#include <ui/Fence.h>
#include <unistd.h>
@@ -86,6 +89,32 @@ int Fence::dup() const {
    return ::dup(mFenceFd);
}

nsecs_t Fence::getSignalTime() const {
    if (mFenceFd == -1) {
        return -1;
    }

    struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
    if (finfo == NULL) {
        ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
        return -1;
    }
    if (finfo->status != 1) {
        return INT64_MAX;
    }

    struct sync_pt_info* pinfo = NULL;
    uint64_t timestamp = 0;
    while ((pinfo = sync_pt_info(finfo, pinfo)) != NULL) {
        if (pinfo->timestamp_ns > timestamp) {
            timestamp = pinfo->timestamp_ns;
        }
    }
    sync_fence_info_free(finfo);

    return nsecs_t(timestamp);
}

size_t Fence::getFlattenedSize() const {
    return 0;
}
+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
    Client.cpp                              \
    DisplayDevice.cpp                       \
    EventThread.cpp                         \
    FrameTracker.cpp                        \
    Layer.cpp                               \
    LayerBase.cpp                           \
    LayerDim.cpp                            \
+132 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 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.
 */

// This is needed for stdint.h to define INT64_MAX in C++
#define __STDC_LIMIT_MACROS

#include <ui/Fence.h>

#include <utils/String8.h>

#include "FrameTracker.h"

namespace android {

FrameTracker::FrameTracker() :
        mOffset(0),
        mNumFences(0) {
}

void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
    mFrameRecords[mOffset].desiredPresentTime = presentTime;
}

void FrameTracker::setFrameReadyTime(nsecs_t readyTime) {
    mFrameRecords[mOffset].frameReadyTime = readyTime;
}

void FrameTracker::setFrameReadyFence(const sp<Fence>& readyFence) {
    mFrameRecords[mOffset].frameReadyFence = readyFence;
    mNumFences++;
}

void FrameTracker::setActualPresentTime(nsecs_t presentTime) {
    mFrameRecords[mOffset].actualPresentTime = presentTime;
}

void FrameTracker::setActualPresentFence(const sp<Fence>& readyFence) {
    mFrameRecords[mOffset].actualPresentFence = readyFence;
    mNumFences++;
}

void FrameTracker::advanceFrame() {
    mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
    mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
    mFrameRecords[mOffset].frameReadyTime = INT64_MAX;
    mFrameRecords[mOffset].actualPresentTime = INT64_MAX;

    if (mFrameRecords[mOffset].frameReadyFence != NULL) {
        // We're clobbering an unsignaled fence, so we need to decrement the
        // fence count.
        mFrameRecords[mOffset].frameReadyFence = NULL;
        mNumFences--;
    }

    if (mFrameRecords[mOffset].actualPresentFence != NULL) {
        // We're clobbering an unsignaled fence, so we need to decrement the
        // fence count.
        mFrameRecords[mOffset].actualPresentFence = NULL;
        mNumFences--;
    }

    // Clean up the signaled fences to keep the number of open fence FDs in
    // this process reasonable.
    processFences();
}

void FrameTracker::clear() {
    for (size_t i = 0; i < NUM_FRAME_RECORDS; i++) {
        mFrameRecords[i].desiredPresentTime = 0;
        mFrameRecords[i].frameReadyTime = 0;
        mFrameRecords[i].actualPresentTime = 0;
        mFrameRecords[i].frameReadyFence.clear();
        mFrameRecords[i].actualPresentFence.clear();
    }
    mNumFences = 0;
}

void FrameTracker::processFences() const {
    FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
    int& numFences = const_cast<int&>(mNumFences);

    for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
        size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;

        const sp<Fence>& rfence = records[idx].frameReadyFence;
        if (rfence != NULL) {
            records[idx].frameReadyTime = rfence->getSignalTime();
            if (records[idx].frameReadyTime < INT64_MAX) {
                records[idx].frameReadyFence = NULL;
                numFences--;
            }
        }

        const sp<Fence>& pfence = records[idx].actualPresentFence;
        if (pfence != NULL) {
            records[idx].actualPresentTime = pfence->getSignalTime();
            if (records[idx].actualPresentTime < INT64_MAX) {
                records[idx].actualPresentFence = NULL;
                numFences--;
            }
        }
    }
}

void FrameTracker::dump(String8& result) const {
    processFences();

    const size_t o = mOffset;
    for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
        const size_t index = (o+i) % NUM_FRAME_RECORDS;
        result.appendFormat("%lld\t%lld\t%lld\n",
            mFrameRecords[index].desiredPresentTime,
            mFrameRecords[index].actualPresentTime,
            mFrameRecords[index].frameReadyTime);
    }
    result.append("\n");
}

} // namespace android
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 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.
 */

#ifndef ANDROID_FRAMETRACKER_H
#define ANDROID_FRAMETRACKER_H

#include <stddef.h>

#include <utils/Timers.h>
#include <utils/RefBase.h>

namespace android {

class String8;
class Fence;

// FrameTracker tracks information about the most recently rendered frames. It
// uses a circular buffer of frame records, and is *NOT* thread-safe -
// mutexing must be done at a higher level if multi-threaded access is
// possible.
//
// Some of the time values tracked may be set either as a specific timestamp
// or a fence.  When a non-NULL fence is set for a given time value, the
// signal time of that fence is used instead of the timestamp.
class FrameTracker {

public:
    // NUM_FRAME_RECORDS is the size of the circular buffer used to track the
    // frame time history.
    enum { NUM_FRAME_RECORDS = 128 };

    FrameTracker();

    // setDesiredPresentTime sets the time at which the current frame
    // should be presented to the user under ideal (i.e. zero latency)
    // conditions.
    void setDesiredPresentTime(nsecs_t desiredPresentTime);

    // setFrameReadyTime sets the time at which the current frame became ready
    // to be presented to the user.  For example, if the frame contents is
    // being written to memory by some asynchronous hardware, this would be
    // the time at which those writes completed.
    void setFrameReadyTime(nsecs_t readyTime);

    // setFrameReadyFence sets the fence that is used to get the time at which
    // the current frame became ready to be presented to the user.
    void setFrameReadyFence(const sp<Fence>& readyFence);

    // setActualPresentTime sets the timestamp at which the current frame became
    // visible to the user.
    void setActualPresentTime(nsecs_t displayTime);

    // setActualPresentFence sets the fence that is used to get the time
    // at which the current frame became visible to the user.
    void setActualPresentFence(const sp<Fence>& fence);

    // advanceFrame advances the frame tracker to the next frame.
    void advanceFrame();

    // clear resets all the tracked frame data to zero.
    void clear();

    // dump appends the current frame display time history to the result string.
    void dump(String8& result) const;

private:
    struct FrameRecord {
        FrameRecord() :
            desiredPresentTime(0),
            frameReadyTime(0),
            actualPresentTime(0) {}
        nsecs_t desiredPresentTime;
        nsecs_t frameReadyTime;
        nsecs_t actualPresentTime;
        sp<Fence> frameReadyFence;
        sp<Fence> actualPresentFence;
    };

    // processFences iterates over all the frame records that have a fence set
    // and replaces that fence with a timestamp if the fence has signaled.  If
    // the fence is not signaled the record's displayTime is set to INT64_MAX.
    //
    // This method is const because although it modifies the frame records it
    // does so in such a way that the information represented should not
    // change.  This allows it to be called from the dump method.
    void processFences() const;

    // mFrameRecords is the circular buffer storing the tracked data for each
    // frame.
    FrameRecord mFrameRecords[NUM_FRAME_RECORDS];

    // mOffset is the offset into mFrameRecords of the current frame.
    size_t mOffset;

    // mNumFences is the total number of fences set in the frame records.  It
    // is incremented each time a fence is added and decremented each time a
    // signaled fence is removed in processFences or if advanceFrame clobbers
    // a fence.
    //
    // The number of fences is tracked so that the run time of processFences
    // doesn't grow with NUM_FRAME_RECORDS.
    int mNumFences;
};

}

#endif // ANDROID_FRAMETRACKER_H
Loading