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
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index acc2775..c3e1a76 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -34,6 +34,7 @@
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
+#include <gui/BufferItem.h>
#include <gui/Surface.h>
#include "clz.h"
@@ -159,11 +160,26 @@
}
}
-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);
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() {
if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
// mSidebandStreamChanged was false
@@ -1014,6 +1030,14 @@
// 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() {
mRefreshPending = false;
return mQueuedFrames > 0 || mSidebandStreamChanged;
@@ -1203,6 +1227,12 @@
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
// have more frames pending.
if (android_atomic_dec(&mQueuedFrames) > 1) {