Improve SurfaceFlinger PTS estimation
Get the next refresh time from DispSync instead of guessing based on the current time. Change-Id: I8dc72a3217bfd4e9b4c905034194d1a298cad69a
This commit is contained in:
parent
c526c35e73
commit
41d67d7ab4
|
@ -508,4 +508,9 @@ void DispSync::resetErrorLocked() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsecs_t DispSync::computeNextRefresh(int periodOffset) const {
|
||||||
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||||
|
return (((now - mPhase) / mPeriod) + periodOffset + 1) * mPeriod + mPhase;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
@ -83,7 +83,7 @@ public:
|
||||||
bool addResyncSample(nsecs_t timestamp);
|
bool addResyncSample(nsecs_t timestamp);
|
||||||
void endResync();
|
void endResync();
|
||||||
|
|
||||||
// The setPreiod method sets the vsync event model's period to a specific
|
// The setPeriod method sets the vsync event model's period to a specific
|
||||||
// value. This should be used to prime the model when a display is first
|
// value. This should be used to prime the model when a display is first
|
||||||
// turned on. It should NOT be used after that.
|
// turned on. It should NOT be used after that.
|
||||||
void setPeriod(nsecs_t period);
|
void setPeriod(nsecs_t period);
|
||||||
|
@ -102,6 +102,12 @@ public:
|
||||||
// DispSync object.
|
// DispSync object.
|
||||||
status_t removeEventListener(const sp<Callback>& callback);
|
status_t removeEventListener(const sp<Callback>& callback);
|
||||||
|
|
||||||
|
// computeNextRefresh computes when the next refresh is expected to begin.
|
||||||
|
// The periodOffset value can be used to move forward or backward; an
|
||||||
|
// offset of zero is the next refresh, -1 is the previous refresh, 1 is
|
||||||
|
// the refresh after next. etc.
|
||||||
|
nsecs_t computeNextRefresh(int periodOffset) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void updateModelLocked();
|
void updateModelLocked();
|
||||||
|
|
|
@ -821,7 +821,7 @@ status_t HWComposer::setOutputBuffer(int32_t id, const sp<Fence>& acquireFence,
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<Fence> HWComposer::getLastRetireFence(int32_t id) {
|
sp<Fence> HWComposer::getLastRetireFence(int32_t id) const {
|
||||||
if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
|
if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
|
||||||
return Fence::NO_FENCE;
|
return Fence::NO_FENCE;
|
||||||
return mDisplayData[id].lastRetireFence;
|
return mDisplayData[id].lastRetireFence;
|
||||||
|
|
|
@ -142,7 +142,7 @@ public:
|
||||||
// signal when the h/w composer is completely finished with the frame.
|
// signal when the h/w composer is completely finished with the frame.
|
||||||
// For physical displays, it is no longer being displayed. For virtual
|
// For physical displays, it is no longer being displayed. For virtual
|
||||||
// displays, writes to the output buffer are complete.
|
// displays, writes to the output buffer are complete.
|
||||||
sp<Fence> getLastRetireFence(int32_t id);
|
sp<Fence> getLastRetireFence(int32_t id) const;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interface to hardware composer's layers functionality.
|
* Interface to hardware composer's layers functionality.
|
||||||
|
|
|
@ -1095,7 +1095,8 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
|
||||||
|
|
||||||
Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions);
|
Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions);
|
||||||
|
|
||||||
status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r);
|
status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
|
||||||
|
mFlinger->mPrimaryDispSync);
|
||||||
if (updateResult == BufferQueue::PRESENT_LATER) {
|
if (updateResult == BufferQueue::PRESENT_LATER) {
|
||||||
// Producer doesn't want buffer to be displayed yet. Signal a
|
// Producer doesn't want buffer to be displayed yet. Signal a
|
||||||
// layer update so we check again at the next opportunity.
|
// layer update so we check again at the next opportunity.
|
||||||
|
|
|
@ -2371,6 +2371,15 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
|
||||||
result.append(SyncFeatures::getInstance().toString());
|
result.append(SyncFeatures::getInstance().toString());
|
||||||
result.append("\n");
|
result.append("\n");
|
||||||
|
|
||||||
|
colorizer.bold(result);
|
||||||
|
result.append("DispSync configuration: ");
|
||||||
|
colorizer.reset(result);
|
||||||
|
result.appendFormat("app phase %"PRId64" ns, sf phase %"PRId64" ns, "
|
||||||
|
"present offset %d ns (refresh %"PRId64" ns)",
|
||||||
|
vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS,
|
||||||
|
mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY));
|
||||||
|
result.append("\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dump the visible layer list
|
* Dump the visible layer list
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,7 +29,8 @@ namespace android {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter)
|
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
|
||||||
|
const DispSync& dispSync)
|
||||||
{
|
{
|
||||||
ATRACE_CALL();
|
ATRACE_CALL();
|
||||||
ALOGV("updateTexImage");
|
ALOGV("updateTexImage");
|
||||||
|
@ -51,7 +52,7 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter)
|
||||||
// Acquire the next buffer.
|
// Acquire the next buffer.
|
||||||
// In asynchronous mode the list is guaranteed to be one buffer
|
// In asynchronous mode the list is guaranteed to be one buffer
|
||||||
// deep, while in synchronous mode we use the oldest buffer.
|
// deep, while in synchronous mode we use the oldest buffer.
|
||||||
err = acquireBufferLocked(&item, computeExpectedPresent());
|
err = acquireBufferLocked(&item, computeExpectedPresent(dispSync));
|
||||||
if (err != NO_ERROR) {
|
if (err != NO_ERROR) {
|
||||||
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
|
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
|
||||||
err = NO_ERROR;
|
err = NO_ERROR;
|
||||||
|
@ -128,35 +129,41 @@ sp<NativeHandle> SurfaceFlingerConsumer::getSidebandStream() const {
|
||||||
// will be slightly later then the actual-present timing. If we get a
|
// will be slightly later then the actual-present timing. If we get a
|
||||||
// desired-present time that is unintentionally a hair after the next
|
// desired-present time that is unintentionally a hair after the next
|
||||||
// vsync, we'll hold the frame when we really want to display it. We
|
// vsync, we'll hold the frame when we really want to display it. We
|
||||||
// want to use an expected-presentation time that is slightly late to
|
// need to take the offset between actual-present and reported-vsync
|
||||||
// avoid this sort of edge case.
|
// into account.
|
||||||
nsecs_t SurfaceFlingerConsumer::computeExpectedPresent()
|
//
|
||||||
|
// If the system is configured without a DispSync phase offset for the app,
|
||||||
|
// we also want to throw in a bit of padding to avoid edge cases where we
|
||||||
|
// just barely miss. We want to do it here, not in every app. A major
|
||||||
|
// source of trouble is the app's use of the display's ideal refresh time
|
||||||
|
// (via Display.getRefreshRate()), which could be off of the actual refresh
|
||||||
|
// by a few percent, with the error multiplied by the number of frames
|
||||||
|
// between now and when the buffer should be displayed.
|
||||||
|
//
|
||||||
|
// If the refresh reported to the app has a phase offset, we shouldn't need
|
||||||
|
// to tweak anything here.
|
||||||
|
nsecs_t SurfaceFlingerConsumer::computeExpectedPresent(const DispSync& dispSync)
|
||||||
{
|
{
|
||||||
// Don't yet have an easy way to get actual buffer flip time for
|
|
||||||
// the specific display, so use the current time. This is typically
|
|
||||||
// 1.3ms past the vsync event time.
|
|
||||||
const nsecs_t prevVsync = systemTime(CLOCK_MONOTONIC);
|
|
||||||
|
|
||||||
// Given a SurfaceFlinger reference, and information about what display
|
|
||||||
// we're destined for, we could query the HWC for the refresh rate. This
|
|
||||||
// could change over time, e.g. we could switch to 24fps for a movie.
|
|
||||||
// For now, assume 60fps.
|
|
||||||
//const nsecs_t vsyncPeriod =
|
|
||||||
// getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
|
|
||||||
const nsecs_t vsyncPeriod = 16700000;
|
|
||||||
|
|
||||||
// The HWC doesn't currently have a way to report additional latency.
|
// The HWC doesn't currently have a way to report additional latency.
|
||||||
// Assume that whatever we submit now will appear on the next flip,
|
// Assume that whatever we submit now will appear right after the flip.
|
||||||
// i.e. 1 frame of latency w.r.t. the previous flip.
|
// For a smart panel this might be 1. This is expressed in frames,
|
||||||
const uint32_t hwcLatency = 1;
|
// rather than time, because we expect to have a constant frame delay
|
||||||
|
// regardless of the refresh rate.
|
||||||
|
const uint32_t hwcLatency = 0;
|
||||||
|
|
||||||
// A little extra padding to compensate for slack between actual vsync
|
// Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
|
||||||
// time and vsync event receipt. Currently not needed since we're
|
const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
|
||||||
// using "now" instead of a vsync time.
|
|
||||||
const nsecs_t extraPadding = 0;
|
|
||||||
|
|
||||||
// Total it up.
|
// The DispSync time is already adjusted for the difference between
|
||||||
return prevVsync + hwcLatency * vsyncPeriod + extraPadding;
|
// vsync and reported-vsync (PRESENT_TIME_OFFSET_FROM_VSYNC_NS), so
|
||||||
|
// we don't need to factor that in here. Pad a little to avoid
|
||||||
|
// weird effects if apps might be requesting times right on the edge.
|
||||||
|
nsecs_t extraPadding = 0;
|
||||||
|
if (VSYNC_EVENT_PHASE_OFFSET_NS == 0) {
|
||||||
|
extraPadding = 1000000; // 1ms (6% of 60Hz)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextRefresh + extraPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceFlingerConsumer::setContentsChangedListener(
|
void SurfaceFlingerConsumer::setContentsChangedListener(
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#ifndef ANDROID_SURFACEFLINGERCONSUMER_H
|
#ifndef ANDROID_SURFACEFLINGERCONSUMER_H
|
||||||
#define ANDROID_SURFACEFLINGERCONSUMER_H
|
#define ANDROID_SURFACEFLINGERCONSUMER_H
|
||||||
|
|
||||||
|
#include "DispSync.h"
|
||||||
#include <gui/GLConsumer.h>
|
#include <gui/GLConsumer.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
@ -33,7 +34,8 @@ public:
|
||||||
|
|
||||||
SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
|
SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
|
||||||
uint32_t tex)
|
uint32_t tex)
|
||||||
: GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false)
|
: GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false),
|
||||||
|
mTransformToDisplayInverse(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
class BufferRejecter {
|
class BufferRejecter {
|
||||||
|
@ -51,7 +53,7 @@ public:
|
||||||
// reject the newly acquired buffer. Unlike the GLConsumer version,
|
// reject the newly acquired buffer. Unlike the GLConsumer version,
|
||||||
// this does not guarantee that the buffer has been bound to the GL
|
// this does not guarantee that the buffer has been bound to the GL
|
||||||
// texture.
|
// texture.
|
||||||
status_t updateTexImage(BufferRejecter* rejecter);
|
status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync);
|
||||||
|
|
||||||
// See GLConsumer::bindTextureImageLocked().
|
// See GLConsumer::bindTextureImageLocked().
|
||||||
status_t bindTextureImage();
|
status_t bindTextureImage();
|
||||||
|
@ -66,7 +68,7 @@ public:
|
||||||
sp<NativeHandle> getSidebandStream() const;
|
sp<NativeHandle> getSidebandStream() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsecs_t computeExpectedPresent();
|
nsecs_t computeExpectedPresent(const DispSync& dispSync);
|
||||||
|
|
||||||
virtual void onSidebandStreamChanged();
|
virtual void onSidebandStreamChanged();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue