Skip to content

Commit d885231

Browse files
Melody HsuAndroid (Google) Code Review
authored andcommitted
Merge "Recover from buffer stuffing for canned animations" into main
2 parents 3a362e5 + e524dd9 commit d885231

15 files changed

Lines changed: 192 additions & 10 deletions

libs/gui/BufferStuffing.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
### Buffer Stuffing and Recovery ###
2+
3+
Buffer stuffing happens on the client side when SurfaceFlinger misses a frame, but the client continues producing buffers at the same rate. This could occur anytime when SurfaceFlinger does not meet the expected timeline’s deadline to finish composing a frame. As a result, SurfaceFlinger cannot yet release the buffer for the frame that it missed and the client has one less buffer to render into. The client may then run out of buffers or have to wait for buffer release callbacks, increasing the chances of janking when clients render multiple windows.
4+
5+
Recovery is implemented by first detecting when buffer stuffing occurs and ensuring that the elevated buffer counts in the server are from a relevant SurfaceControl (is a ViewRootImpl). Other SurfaceControl buffer producers such as games, media, and camera have other reasons for expectedly increased buffer counts, which do not need buffer stuffing recovery.
6+
7+
The actual recovery adjusts the animation timeline in the Choreographer so that the client deadlines for subsequent frames are moved forward in time by one frame. This approach adjusts the client buffer production timeline such that SurfaceFlinger does not fall behind when it misses a frame because the client will simply match its frame production rate with SurfaceFlinger. Ordinarily, buffer stuffing is problematic because the client continues producing buffers when SurfaceFlinger is behind. However, if the client delays producing its buffers to match SurfaceFlinger’s rate, the animation has new frame deadlines that can be reasonably met. The animation is effectively paused for one frame longer than originally intended, and continues the remainder of the animation normally.

libs/gui/include/gui/DisplayEventReceiver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class DisplayEventReceiver {
119119
HdcpLevelsChange hdcpLevelsChange;
120120
};
121121
};
122-
static_assert(sizeof(Event) == 216);
122+
static_assert(sizeof(Event) == 224);
123123

124124
public:
125125
/*

libs/gui/include/gui/LayerState.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ struct layer_state_t {
171171
// Sets a property on this layer indicating that its visible region should be considered
172172
// when computing TrustedPresentation Thresholds.
173173
eCanOccludePresentation = 0x1000,
174+
// Indicates that the SurfaceControl should recover from buffer stuffing when
175+
// possible. This is the case when the SurfaceControl is the root SurfaceControl
176+
// owned by ViewRootImpl.
177+
eRecoverableFromBufferStuffing = 0x2000,
174178
};
175179

176180
enum {

libs/gui/include/gui/VsyncEventData.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ struct VsyncEventData {
3636
// Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity.
3737
uint32_t frameTimelinesLength;
3838

39+
// Number of queued buffers to indicate if buffer stuffing mode is detected.
40+
uint32_t numberQueuedBuffers;
41+
3942
struct alignas(8) FrameTimeline {
4043
// The Vsync Id corresponsing to this vsync event. This will be used to
4144
// populate ISurfaceComposer::setFrameTimelineVsync and

libs/gui/tests/DisplayEventStructLayout_test.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@ TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
3636
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8);
3737
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16);
3838
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20);
39-
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24);
40-
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24);
39+
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.numberQueuedBuffers, 24);
40+
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 32);
41+
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 32);
4142
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp,
42-
32);
43+
40);
4344
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
44-
vsyncData.frameTimelines[0].expectedPresentationTime, 40);
45+
vsyncData.frameTimelines[0].expectedPresentationTime, 48);
4546
// Also test the offsets of the last frame timeline. A loop is not used because the non-const
4647
// index cannot be used in static_assert.
47-
const int lastFrameTimelineOffset = /* Start of array */ 24 +
48+
const int lastFrameTimelineOffset = /* Start of array */ 32 +
4849
(VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24;
4950
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
5051
vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId,

services/surfaceflinger/FrameTimeline/FrameTimeline.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <cinttypes>
3030
#include <numeric>
3131
#include <unordered_set>
32+
#include <vector>
3233

3334
#include "../Jank/JankTracker.h"
3435

@@ -1002,6 +1003,11 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
10021003
finalizeCurrentDisplayFrame();
10031004
}
10041005

1006+
const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames()
1007+
const {
1008+
return mPresentFrames;
1009+
}
1010+
10051011
void FrameTimeline::onCommitNotComposited() {
10061012
SFTRACE_CALL();
10071013
std::scoped_lock lock(mMutex);
@@ -1521,6 +1527,7 @@ void FrameTimeline::flushPendingPresentFences() {
15211527
mPendingPresentFences.erase(mPendingPresentFences.begin());
15221528
}
15231529

1530+
mPresentFrames.clear();
15241531
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
15251532
const auto& pendingPresentFence = mPendingPresentFences[i];
15261533
nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
@@ -1533,6 +1540,13 @@ void FrameTimeline::flushPendingPresentFences() {
15331540

15341541
auto& displayFrame = pendingPresentFence.second;
15351542
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
1543+
1544+
// Surface frames have been jank classified and can be provided to caller
1545+
// to detect if buffer stuffing is occurring.
1546+
for (const auto& frame : displayFrame->getSurfaceFrames()) {
1547+
mPresentFrames.push_back(frame);
1548+
}
1549+
15361550
mPreviousPredictionPresentTime =
15371551
displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
15381552
mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);

services/surfaceflinger/FrameTimeline/FrameTimeline.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ class FrameTimeline {
328328
virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
329329
const std::shared_ptr<FenceTime>& gpuFence) = 0;
330330

331+
// Provides surface frames that have already been jank classified in the most recent
332+
// flush of pending present fences. This allows buffer stuffing detection from SF.
333+
virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
334+
const = 0;
335+
331336
// Tells FrameTimeline that a frame was committed but not composited. This is used to flush
332337
// all the associated surface frames.
333338
virtual void onCommitNotComposited() = 0;
@@ -505,6 +510,8 @@ class FrameTimeline : public android::frametimeline::FrameTimeline {
505510
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
506511
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
507512
const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
513+
const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
514+
const override;
508515
void onCommitNotComposited() override;
509516
void parseArgs(const Vector<String16>& args, std::string& result) override;
510517
void setMaxDisplayFrames(uint32_t size) override;
@@ -552,6 +559,9 @@ class FrameTimeline : public android::frametimeline::FrameTimeline {
552559
// display frame, this is a good starting size for the vector so that we can avoid the
553560
// internal vector resizing that happens with push_back.
554561
static constexpr uint32_t kNumSurfaceFramesInitial = 10;
562+
// Presented surface frames that have been jank classified and can
563+
// indicate of potential buffer stuffing.
564+
std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames;
555565
};
556566

557567
} // namespace impl

services/surfaceflinger/Scheduler/EventThread.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,8 @@
4444

4545
#include <common/FlagManager.h>
4646
#include <scheduler/VsyncConfig.h>
47-
#include "DisplayHardware/DisplayMode.h"
4847
#include "FrameTimeline.h"
4948
#include "VSyncDispatch.h"
50-
#include "VSyncTracker.h"
5149

5250
#include "EventThread.h"
5351

@@ -482,6 +480,14 @@ void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t conne
482480
mCondition.notify_all();
483481
}
484482

483+
// Merge lists of buffer stuffed Uids
484+
void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
485+
std::lock_guard<std::mutex> lock(mMutex);
486+
for (auto& [uid, count] : bufferStuffedUids) {
487+
mBufferStuffedUids.emplace_or_replace(uid, count);
488+
}
489+
}
490+
485491
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
486492
DisplayEventConsumers consumers;
487493

@@ -721,6 +727,10 @@ void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs
721727

722728
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
723729
const DisplayEventConsumers& consumers) {
730+
// List of Uids that have been sent vsync data with queued buffer count.
731+
// Used to keep track of which Uids can be removed from the map of
732+
// buffer stuffed clients.
733+
ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
724734
for (const auto& consumer : consumers) {
725735
DisplayEventReceiver::Event copy = event;
726736
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
@@ -730,6 +740,13 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
730740
event.vsync.vsyncData.preferredExpectedPresentationTime(),
731741
event.vsync.vsyncData.preferredDeadlineTimestamp());
732742
}
743+
auto it = mBufferStuffedUids.find(consumer->mOwnerUid);
744+
if (it != mBufferStuffedUids.end()) {
745+
copy.vsync.vsyncData.numberQueuedBuffers = it->second;
746+
uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid);
747+
} else {
748+
copy.vsync.vsyncData.numberQueuedBuffers = 0;
749+
}
733750
switch (consumer->postEvent(copy)) {
734751
case NO_ERROR:
735752
break;
@@ -745,6 +762,12 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
745762
removeDisplayEventConnectionLocked(consumer);
746763
}
747764
}
765+
// The clients that have already received the queued buffer count
766+
// can be removed from the buffer stuffed Uid list to avoid
767+
// being sent duplicate messages.
768+
for (auto uid : uidsPostedQueuedBuffers) {
769+
mBufferStuffedUids.erase(uid);
770+
}
748771
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
749772
FlagManager::getInstance().vrr_config()) {
750773
mLastCommittedVsyncTime =

services/surfaceflinger/Scheduler/EventThread.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
#include <thread>
3333
#include <vector>
3434

35-
#include "DisplayHardware/DisplayMode.h"
3635
#include "TracedOrdinal.h"
3736
#include "VSyncDispatch.h"
3837
#include "VsyncSchedule.h"
@@ -55,6 +54,7 @@ using gui::VsyncEventData;
5554
// ---------------------------------------------------------------------------
5655

5756
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
57+
using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
5858

5959
enum class VSyncRequest {
6060
None = -2,
@@ -136,6 +136,10 @@ class EventThread {
136136

137137
virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
138138
int32_t maxLevel) = 0;
139+
140+
// An elevated number of queued buffers in the server is detected. This propagates a
141+
// flag to Choreographer indicating that buffer stuffing recovery should begin.
142+
virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
139143
};
140144

141145
struct IEventThreadCallback {
@@ -188,6 +192,8 @@ class EventThread : public android::EventThread {
188192
void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
189193
int32_t maxLevel) override;
190194

195+
void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override;
196+
191197
private:
192198
friend EventThreadTest;
193199

@@ -228,6 +234,10 @@ class EventThread : public android::EventThread {
228234
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
229235
frametimeline::TokenManager* const mTokenManager;
230236

237+
// All consumers that need to recover from buffer stuffing and the number
238+
// of their queued buffers.
239+
BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex);
240+
231241
IEventThreadCallback& mCallback;
232242

233243
std::thread mThread;

services/surfaceflinger/Scheduler/Scheduler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,11 @@ bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
951951
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
952952
}
953953

954+
void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
955+
if (!mRenderEventThread) return;
956+
mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids));
957+
}
958+
954959
void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
955960
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
956961
{

0 commit comments

Comments
 (0)