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:
parent
e64b38fad2
commit
82dbc7429f
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
|
||||
Client.cpp \
|
||||
DisplayDevice.cpp \
|
||||
EventThread.cpp \
|
||||
FrameTracker.cpp \
|
||||
Layer.cpp \
|
||||
LayerBase.cpp \
|
||||
LayerDim.cpp \
|
||||
|
132
services/surfaceflinger/FrameTracker.cpp
Normal file
132
services/surfaceflinger/FrameTracker.cpp
Normal 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
|
120
services/surfaceflinger/FrameTracker.h
Normal file
120
services/surfaceflinger/FrameTracker.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user