SurfaceFlinger: Do less work when using PTS

Currently, SurfaceFlinger is very dumb about how it handles buffer
updates at less than 60fps. If there is a new frame pending, but its
timestamp says not to present it until later SurfaceFlinger will wake
up every vsync until it is time to present it. Even worse, if
SurfaceFlinger has woken up but nothing has changed, it still goes
through the entire composition process.

This change (mostly) fixes that inefficiency. SurfaceFlinger will
still wake up every refresh period while there is a new frame
pending, but if there is no work to do, it will almost immediately go
back to sleep.

Bug: 18111837
Change-Id: I7825bacd37f40bf26edcc6a5e0f051dce45291fb
This commit is contained in:
Dan Stoza 2014-11-07 16:00:59 -08:00
parent 7d9fdd1e0d
commit 6b9454d1fe
5 changed files with 90 additions and 24 deletions

View File

@ -34,6 +34,7 @@
#include <ui/GraphicBuffer.h> #include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h> #include <ui/PixelFormat.h>
#include <gui/BufferItem.h>
#include <gui/Surface.h> #include <gui/Surface.h>
#include "clz.h" #include "clz.h"
@ -159,11 +160,26 @@ void Layer::onLayerDisplayed(const sp<const DisplayDevice>& /* hw */,
} }
} }
void Layer::onFrameAvailable(const BufferItem& /* item */) { void Layer::onFrameAvailable(const BufferItem& item) {
// Add this buffer from our internal queue tracker
{ // Autolock scope
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.push_back(item);
}
android_atomic_inc(&mQueuedFrames); android_atomic_inc(&mQueuedFrames);
mFlinger->signalLayerUpdate(); mFlinger->signalLayerUpdate();
} }
void Layer::onFrameReplaced(const BufferItem& item) {
Mutex::Autolock lock(mQueueItemLock);
if (mQueueItems.empty()) {
ALOGE("Can't replace a frame on an empty queue");
return;
}
mQueueItems.editItemAt(0) = item;
}
void Layer::onSidebandStreamChanged() { void Layer::onSidebandStreamChanged() {
if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) { if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
// mSidebandStreamChanged was false // mSidebandStreamChanged was false
@ -1014,6 +1030,14 @@ bool Layer::setLayerStack(uint32_t layerStack) {
// pageflip handling... // pageflip handling...
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool Layer::shouldPresentNow(const DispSync& dispSync) const {
Mutex::Autolock lock(mQueueItemLock);
nsecs_t expectedPresent =
mSurfaceFlingerConsumer->computeExpectedPresent(dispSync);
return mQueueItems.empty() ?
false : mQueueItems[0].mTimestamp < expectedPresent;
}
bool Layer::onPreComposition() { bool Layer::onPreComposition() {
mRefreshPending = false; mRefreshPending = false;
return mQueuedFrames > 0 || mSidebandStreamChanged; return mQueuedFrames > 0 || mSidebandStreamChanged;
@ -1203,6 +1227,12 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
return outDirtyRegion; return outDirtyRegion;
} }
// Remove this buffer from our internal queue tracker
{ // Autolock scope
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.removeAt(0);
}
// Decrement the queued-frames count. Signal another event if we // Decrement the queued-frames count. Signal another event if we
// have more frames pending. // have more frames pending.
if (android_atomic_dec(&mQueuedFrames) > 1) { if (android_atomic_dec(&mQueuedFrames) > 1) {

View File

@ -210,6 +210,8 @@ public:
void onLayerDisplayed(const sp<const DisplayDevice>& hw, void onLayerDisplayed(const sp<const DisplayDevice>& hw,
HWComposer::HWCLayerInterface* layer); HWComposer::HWCLayerInterface* layer);
bool shouldPresentNow(const DispSync& dispSync) const;
/* /*
* called before composition. * called before composition.
* returns true if the layer has pending updates. * returns true if the layer has pending updates.
@ -331,6 +333,7 @@ protected:
private: private:
// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener // Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
virtual void onFrameAvailable(const BufferItem& item); virtual void onFrameAvailable(const BufferItem& item);
virtual void onFrameReplaced(const BufferItem& item);
virtual void onSidebandStreamChanged(); virtual void onSidebandStreamChanged();
void commitTransaction(); void commitTransaction();
@ -404,6 +407,10 @@ private:
// This layer can be a cursor on some displays. // This layer can be a cursor on some displays.
bool mPotentialCursor; bool mPotentialCursor;
// Local copy of the queued contents of the incoming BufferQueue
mutable Mutex mQueueItemLock;
Vector<BufferItem> mQueueItems;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -829,30 +829,39 @@ void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
void SurfaceFlinger::onMessageReceived(int32_t what) { void SurfaceFlinger::onMessageReceived(int32_t what) {
ATRACE_CALL(); ATRACE_CALL();
switch (what) { switch (what) {
case MessageQueue::TRANSACTION: case MessageQueue::TRANSACTION: {
handleMessageTransaction(); handleMessageTransaction();
break; break;
case MessageQueue::INVALIDATE: }
handleMessageTransaction(); case MessageQueue::INVALIDATE: {
handleMessageInvalidate(); bool refreshNeeded = handleMessageTransaction();
signalRefresh(); refreshNeeded |= handleMessageInvalidate();
break; if (refreshNeeded) {
case MessageQueue::REFRESH: // Signal a refresh if a transaction modified the window state or if
handleMessageRefresh(); // a new buffer was latched
break; signalRefresh();
}
break;
}
case MessageQueue::REFRESH: {
handleMessageRefresh();
break;
}
} }
} }
void SurfaceFlinger::handleMessageTransaction() { bool SurfaceFlinger::handleMessageTransaction() {
uint32_t transactionFlags = peekTransactionFlags(eTransactionMask); uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
if (transactionFlags) { if (transactionFlags) {
handleTransaction(transactionFlags); handleTransaction(transactionFlags);
return true;
} }
return false;
} }
void SurfaceFlinger::handleMessageInvalidate() { bool SurfaceFlinger::handleMessageInvalidate() {
ATRACE_CALL(); ATRACE_CALL();
handlePageFlip(); return handlePageFlip();
} }
void SurfaceFlinger::handleMessageRefresh() { void SurfaceFlinger::handleMessageRefresh() {
@ -1685,12 +1694,13 @@ void SurfaceFlinger::invalidateLayerStack(uint32_t layerStack,
} }
} }
void SurfaceFlinger::handlePageFlip() bool SurfaceFlinger::handlePageFlip()
{ {
Region dirtyRegion; Region dirtyRegion;
bool visibleRegions = false; bool visibleRegions = false;
const LayerVector& layers(mDrawingState.layersSortedByZ); const LayerVector& layers(mDrawingState.layersSortedByZ);
bool frameQueued = false;
// Store the set of layers that need updates. This set must not change as // Store the set of layers that need updates. This set must not change as
// buffers are being latched, as this could result in a deadlock. // buffers are being latched, as this could result in a deadlock.
@ -1704,8 +1714,12 @@ void SurfaceFlinger::handlePageFlip()
Vector<Layer*> layersWithQueuedFrames; Vector<Layer*> layersWithQueuedFrames;
for (size_t i = 0, count = layers.size(); i<count ; i++) { for (size_t i = 0, count = layers.size(); i<count ; i++) {
const sp<Layer>& layer(layers[i]); const sp<Layer>& layer(layers[i]);
if (layer->hasQueuedFrame()) if (layer->hasQueuedFrame()) {
layersWithQueuedFrames.push_back(layer.get()); frameQueued = true;
if (layer->shouldPresentNow(mPrimaryDispSync)) {
layersWithQueuedFrames.push_back(layer.get());
}
}
} }
for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) { for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
Layer* layer = layersWithQueuedFrames[i]; Layer* layer = layersWithQueuedFrames[i];
@ -1715,6 +1729,16 @@ void SurfaceFlinger::handlePageFlip()
} }
mVisibleRegionsDirty |= visibleRegions; mVisibleRegionsDirty |= visibleRegions;
// If we will need to wake up at some time in the future to deal with a
// queued frame that shouldn't be displayed during this vsync period, wake
// up during the next vsync period to check again.
if (frameQueued && layersWithQueuedFrames.empty()) {
signalLayerUpdate();
}
// Only continue with the refresh if there is actually new work to do
return !layersWithQueuedFrames.empty();
} }
void SurfaceFlinger::invalidateHwcGeometry() void SurfaceFlinger::invalidateHwcGeometry()

View File

@ -247,8 +247,12 @@ private:
// called on the main thread in response to setPowerMode() // called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode); void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode);
void handleMessageTransaction(); // Returns whether the transaction actually modified any state
void handleMessageInvalidate(); bool handleMessageTransaction();
// Returns whether a new buffer has been latched (see handlePageFlip())
bool handleMessageInvalidate();
void handleMessageRefresh(); void handleMessageRefresh();
void handleTransaction(uint32_t transactionFlags); void handleTransaction(uint32_t transactionFlags);
@ -256,10 +260,11 @@ private:
void updateCursorAsync(); void updateCursorAsync();
/* handlePageFilp: this is were we latch a new buffer /* handlePageFlip - latch a new buffer if available and compute the dirty
* if available and compute the dirty region. * region. Returns whether a new buffer has been latched, i.e., whether it
* is necessary to perform a refresh during this vsync.
*/ */
void handlePageFlip(); bool handlePageFlip();
/* ------------------------------------------------------------------------ /* ------------------------------------------------------------------------
* Transactions * Transactions

View File

@ -67,9 +67,9 @@ public:
sp<NativeHandle> getSidebandStream() const; sp<NativeHandle> getSidebandStream() const;
private:
nsecs_t computeExpectedPresent(const DispSync& dispSync); nsecs_t computeExpectedPresent(const DispSync& dispSync);
private:
virtual void onSidebandStreamChanged(); virtual void onSidebandStreamChanged();
wp<ContentsChangedListener> mContentsChangedListener; wp<ContentsChangedListener> mContentsChangedListener;