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
This commit is contained in:
Jamie Gennis 2012-11-08 19:23:28 -08:00
parent e64b38fad2
commit 82dbc7429f
7 changed files with 320 additions and 34 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
Client.cpp \
DisplayDevice.cpp \
EventThread.cpp \
FrameTracker.cpp \
Layer.cpp \
LayerBase.cpp \
LayerDim.cpp \

View File

@ -0,0 +1,132 @@
/*
* 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

View File

@ -0,0 +1,120 @@
/*
* 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

View File

@ -59,7 +59,6 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client)
mCurrentOpacity(true),
mRefreshPending(false),
mFrameLatencyNeeded(false),
mFrameLatencyOffset(0),
mFormat(PIXEL_FORMAT_NONE),
mGLExtensions(GLExtensions::getInstance()),
mOpaqueLayer(true),
@ -507,12 +506,30 @@ bool Layer::onPreComposition() {
void Layer::onPostComposition() {
if (mFrameLatencyNeeded) {
nsecs_t desiredPresentTime = mSurfaceTexture->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
sp<Fence> frameReadyFence = mSurfaceTexture->getCurrentFence();
if (frameReadyFence != NULL) {
mFrameTracker.setFrameReadyFence(frameReadyFence);
} else {
// There was no fence for this frame, so assume that it was ready
// to be presented at the desired present time.
mFrameTracker.setFrameReadyTime(desiredPresentTime);
}
const HWComposer& hwc = mFlinger->getHwComposer();
const size_t offset = mFrameLatencyOffset;
mFrameStats[offset].timestamp = mSurfaceTexture->getTimestamp();
mFrameStats[offset].set = systemTime();
mFrameStats[offset].vsync = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
mFrameLatencyOffset = (mFrameLatencyOffset + 1) % 128;
sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence != NULL) {
mFrameTracker.setActualPresentFence(presentFence);
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
mFrameTracker.setActualPresentTime(presentTime);
}
mFrameTracker.advanceFrame();
mFrameLatencyNeeded = false;
}
}
@ -721,27 +738,16 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const
{
LayerBaseClient::dumpStats(result, buffer, SIZE);
const size_t o = mFrameLatencyOffset;
const nsecs_t period =
mFlinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
result.appendFormat("%lld\n", period);
for (size_t i=0 ; i<128 ; i++) {
const size_t index = (o+i) % 128;
const nsecs_t time_app = mFrameStats[index].timestamp;
const nsecs_t time_set = mFrameStats[index].set;
const nsecs_t time_vsync = mFrameStats[index].vsync;
result.appendFormat("%lld\t%lld\t%lld\n",
time_app,
time_vsync,
time_set);
}
result.append("\n");
mFrameTracker.dump(result);
}
void Layer::clearStats()
{
LayerBaseClient::clearStats();
memset(mFrameStats, 0, sizeof(mFrameStats));
mFrameTracker.clear();
}
uint32_t Layer::getEffectiveUsage(uint32_t usage) const

View File

@ -34,6 +34,7 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
#include "FrameTracker.h"
#include "LayerBase.h"
#include "SurfaceTextureLayer.h"
#include "Transform.h"
@ -129,17 +130,7 @@ private:
bool mCurrentOpacity;
bool mRefreshPending;
bool mFrameLatencyNeeded;
int mFrameLatencyOffset;
struct Statistics {
Statistics() : timestamp(0), set(0), vsync(0) { }
nsecs_t timestamp; // buffer timestamp
nsecs_t set; // buffer displayed timestamp
nsecs_t vsync; // vsync immediately before set
};
// protected by mLock
Statistics mFrameStats[128];
FrameTracker mFrameTracker;
// constants
PixelFormat mFormat;