Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions nativelib/src/main/cpp/native_render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,6 @@ void NativeRender::ResetPresentationStatsLocked() {
precisePhaseShiftCount_ = 0;
preciseRebufferCount_ = 0;
preciseResyncCount_ = 0;
preciseQueueFullCount_ = 0;
preciseApiFailureCount_ = 0;
preciseMaxTargetLeadNs_ = 0;
presentationDiagnostics_.Reset();
Expand Down Expand Up @@ -577,7 +576,6 @@ NativeRender::FrameSubmitResult NativeRender::SubmitFrame(const DecodedFrame& fr
plan.event == PresentationEvent::DUPLICATE_PTS) {
preciseResyncCount_++;
}
if (plan.event == PresentationEvent::QUEUE_FULL) preciseQueueFullCount_++;
if (plan.action == PresentationAction::SCHEDULE) {
preciseMaxTargetLeadNs_ = std::max(
preciseMaxTargetLeadNs_, plan.targetTimeNs - decodedAtNs);
Expand All @@ -591,7 +589,7 @@ NativeRender::FrameSubmitResult NativeRender::SubmitFrame(const DecodedFrame& fr
const PresentationTimingStats& timing =
presentationDiagnostics_.GetStats();
OH_LOG_INFO(LOG_APP,
"Host-paced stats: frames=%{public}lld, scheduled=%{public}lld, dropped=%{public}lld, late=%{public}lld, catchUp=%{public}lld, phaseShift=%{public}lld, rebuffer=%{public}lld, resync=%{public}lld, queueFull=%{public}lld, apiFailure=%{public}lld, lead=%{public}lldus, maxLead=%{public}lldus, sameSlot=%{public}lld, slotRegression=%{public}lld, targetRegression=%{public}lld, maxRegression=%{public}lldus, maxQueueSlots=%{public}lld, vsyncPeriod=%{public}lldus, vsyncAge=%{public}lldus, vsyncSampleFailure=%{public}lld",
"Host-paced stats: frames=%{public}lld, scheduled=%{public}lld, dropped=%{public}lld, late=%{public}lld, catchUp=%{public}lld, phaseShift=%{public}lld, rebuffer=%{public}lld, resync=%{public}lld, apiFailure=%{public}lld, lead=%{public}lldus, maxLead=%{public}lldus, sameSlot=%{public}lld, slotRegression=%{public}lld, targetRegression=%{public}lld, maxRegression=%{public}lldus, maxQueueSlots=%{public}lld, vsyncPeriod=%{public}lldus, vsyncAge=%{public}lldus, vsyncSampleFailure=%{public}lld",
static_cast<long long>(totalFrames),
static_cast<long long>(preciseScheduledCount_),
static_cast<long long>(preciseDroppedCount_),
Expand All @@ -600,7 +598,6 @@ NativeRender::FrameSubmitResult NativeRender::SubmitFrame(const DecodedFrame& fr
static_cast<long long>(precisePhaseShiftCount_),
static_cast<long long>(preciseRebufferCount_),
static_cast<long long>(preciseResyncCount_),
static_cast<long long>(preciseQueueFullCount_),
static_cast<long long>(preciseApiFailureCount_),
static_cast<long long>(ptsScheduler_.GetInitialLeadNs() / 1000),
static_cast<long long>(preciseMaxTargetLeadNs_ / 1000),
Expand Down
1 change: 0 additions & 1 deletion nativelib/src/main/cpp/native_render.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ class NativeRender {
int64_t precisePhaseShiftCount_ = 0;
int64_t preciseRebufferCount_ = 0;
int64_t preciseResyncCount_ = 0;
int64_t preciseQueueFullCount_ = 0;
int64_t preciseApiFailureCount_ = 0;
int64_t preciseMaxTargetLeadNs_ = 0;

Expand Down
40 changes: 0 additions & 40 deletions nativelib/src/main/cpp/presentation_scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ constexpr double kNtscTripleBurstFps = 119.88;
constexpr int64_t kDriftDeadbandNs = 2 * kNanosecondsPerMillisecond;
constexpr int64_t kMaxDriftCorrectionPerFrameNs = 20 * kNanosecondsPerMicrosecond;

PresentationEvent ReanchorEventForRetry(PresentationEvent event) {
if (event == PresentationEvent::DISCONTINUITY ||
event == PresentationEvent::REBUFFER ||
event == PresentationEvent::DUPLICATE_PTS) {
return event;
}
return PresentationEvent::CATCH_UP;
}

int64_t FloorDiv(int64_t value, int64_t divisor) {
int64_t quotient = value / divisor;
if (value % divisor < 0) {
Expand Down Expand Up @@ -95,8 +86,6 @@ void PtsPresentationScheduler::Reset() {
driftErrorEmaNs_ = 0;
phaseShiftDebtNs_ = 0;
lastScheduledTargetNs_ = 0;
reanchorRetryPending_ = false;
pendingReanchorEvent_ = PresentationEvent::CATCH_UP;
}

PresentationPlan PtsPresentationScheduler::AnchorFrame(
Expand All @@ -108,8 +97,6 @@ PresentationPlan PtsPresentationScheduler::AnchorFrame(
lastPtsUs_ = ptsUs;
driftErrorEmaNs_ = 0;
phaseShiftDebtNs_ = 0;
reanchorRetryPending_ = false;
pendingReanchorEvent_ = PresentationEvent::CATCH_UP;

return ScheduleTarget(
anchorTargetNs_, decodedAtNs, event, latenessNs, slotClock);
Expand All @@ -134,21 +121,6 @@ PresentationPlan PtsPresentationScheduler::ScheduleTarget(
}
}

// Start the existing cadence budget at the first slot that still has the
// submit margin. Being close to a VSync must not reduce burst capacity.
const int64_t firstEligibleSlotNs = CeilToSlot(
requiredTargetNs, slotClock.anchorNs, slotClock.periodNs);
const int64_t maxScheduledSlotNs = firstEligibleSlotNs +
GetAdditionalQueueSlots(slotClock.periodNs) * slotClock.periodNs;
if (scheduledSlotNs > maxScheduledSlotNs) {
reanchorRetryPending_ = true;
pendingReanchorEvent_ = ReanchorEventForRetry(event);
PresentationPlan plan;
plan.event = PresentationEvent::QUEUE_FULL;
plan.latenessNs = latenessNs;
return plan;
}

lastScheduledTargetNs_ = scheduledSlotNs;

PresentationPlan plan;
Expand Down Expand Up @@ -184,13 +156,6 @@ bool PtsPresentationScheduler::IsPresentationQueueEmpty(
return lastOccupiedSlotNs <= slotClock.currentSlotNs;
}

int64_t PtsPresentationScheduler::GetAdditionalQueueSlots(
int64_t vsyncPeriodNs) const {
const int64_t cadenceBudgetNs = std::max<int64_t>(
0, maxFutureLeadNs_ - initialLeadNs_);
return cadenceBudgetNs / vsyncPeriodNs;
}

void PtsPresentationScheduler::ApplySlowDriftCorrection(
int64_t decodedAtNs, int64_t& targetTimeNs) {
int64_t leadErrorNs = targetTimeNs - decodedAtNs - initialLeadNs_;
Expand Down Expand Up @@ -229,11 +194,6 @@ PresentationPlan PtsPresentationScheduler::PlanFrame(
const SlotClock slotClock = ResolveSlotClock(decodedAtNs, vsyncTiming);
const bool queueEmpty = IsPresentationQueueEmpty(slotClock);

if (reanchorRetryPending_) {
return AnchorFrame(
ptsUs, decodedAtNs, pendingReanchorEvent_, slotClock);
}

if (!initialized_) {
return AnchorFrame(
ptsUs, decodedAtNs, PresentationEvent::INITIAL_ANCHOR, slotClock);
Expand Down
4 changes: 0 additions & 4 deletions nativelib/src/main/cpp/presentation_scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ enum class PresentationEvent {
REBUFFER,
DUPLICATE_PTS,
INVALID_PTS,
QUEUE_FULL,
};

struct PresentationVsyncTiming {
Expand Down Expand Up @@ -74,7 +73,6 @@ class PtsPresentationScheduler {
int64_t decodedAtNs,
const PresentationVsyncTiming& vsyncTiming) const;
bool IsPresentationQueueEmpty(const SlotClock& slotClock) const;
int64_t GetAdditionalQueueSlots(int64_t vsyncPeriodNs) const;

int64_t frameIntervalNs_ = 16666667LL;
int64_t initialLeadNs_ = 2000000LL;
Expand All @@ -89,8 +87,6 @@ class PtsPresentationScheduler {
int64_t driftErrorEmaNs_ = 0;
int64_t phaseShiftDebtNs_ = 0;
int64_t lastScheduledTargetNs_ = 0;
bool reanchorRetryPending_ = false;
PresentationEvent pendingReanchorEvent_ = PresentationEvent::CATCH_UP;
};

#endif // PRESENTATION_SCHEDULER_H
99 changes: 55 additions & 44 deletions nativelib/src/test/cpp/presentation_scheduler_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ void TestSustained120FpsPeriodicJitterDoesNotDrop() {
assert(phaseShiftCount >= 20);
}

void Test120FpsBurstUsesThreeUniqueSlots() {
void Test120FpsBurstPreservesAllFrames() {
PtsPresentationScheduler scheduler;
scheduler.Configure(120.0);
const PresentationVsyncTiming timing = Timing(120.0);
Expand All @@ -116,44 +116,55 @@ void Test120FpsBurstUsesThreeUniqueSlots() {
const PresentationPlan second = scheduler.PlanFrame(8334, decodedAtNs, timing);
const PresentationPlan third = scheduler.PlanFrame(16667, decodedAtNs, timing);
const PresentationPlan fourth = scheduler.PlanFrame(25000, decodedAtNs, timing);
const PresentationPlan fifth = scheduler.PlanFrame(33333, decodedAtNs, timing);

AssertOnVsyncSlot(first, timing);
AssertOnVsyncSlot(second, timing);
AssertOnVsyncSlot(third, timing);
AssertOnVsyncSlot(fourth, timing);
AssertOnVsyncSlot(fifth, timing);
assert(second.targetTimeNs - first.targetTimeNs == timing.periodNs);
assert(third.targetTimeNs - second.targetTimeNs == timing.periodNs);
assert(fourth.action == PresentationAction::DROP);
assert(fourth.event == PresentationEvent::QUEUE_FULL);
assert(fourth.targetTimeNs - third.targetTimeNs == timing.periodNs);
assert(fifth.targetTimeNs - fourth.targetTimeNs == timing.periodNs);
}

void TestQueueFullRetriesAtNextAvailableSlot() {
void TestRepeated120FpsBurstsDrainBetweenCallbacks() {
PtsPresentationScheduler scheduler;
scheduler.Configure(120.0);
const PresentationVsyncTiming timing = Timing(120.0);
const int64_t burstTimeNs = kStartNs + kMs;

const PresentationPlan first = scheduler.PlanFrame(0, burstTimeNs, timing);
scheduler.PlanFrame(8334, burstTimeNs, timing);
const PresentationPlan third = scheduler.PlanFrame(16667, burstTimeNs, timing);
const PresentationPlan full = scheduler.PlanFrame(25000, burstTimeNs, timing);
const PresentationPlan stillFull = scheduler.PlanFrame(
33333, burstTimeNs, timing);
const PresentationPlan recovered = scheduler.PlanFrame(
41667, first.targetTimeNs + kMs, timing);
const PresentationPlan next = scheduler.PlanFrame(
50000, first.targetTimeNs + timing.periodNs + kMs, timing);

assert(full.event == PresentationEvent::QUEUE_FULL);
assert(stillFull.action == PresentationAction::DROP);
assert(stillFull.event == PresentationEvent::QUEUE_FULL);
assert(recovered.action == PresentationAction::SCHEDULE);
assert(recovered.event == PresentationEvent::CATCH_UP);
assert(recovered.targetTimeNs == third.targetTimeNs + timing.periodNs);
assert(next.action == PresentationAction::SCHEDULE);
assert(next.targetTimeNs == recovered.targetTimeNs + timing.periodNs);
const int64_t firstDecodedAtNs = kStartNs + kMs;
constexpr double kPtsIntervalUs = 1000000.0 / 120.0;
constexpr int kBurstSize = 5;

PresentationPlan previous;
for (int burst = 0; burst < 120; ++burst) {
const int firstFrame = burst * kBurstSize;
const int64_t decodedAtNs =
firstDecodedAtNs + firstFrame * timing.periodNs;
for (int offset = 0; offset < kBurstSize; ++offset) {
const int frame = firstFrame + offset;
const int64_t ptsUs = static_cast<int64_t>(
std::llround(frame * kPtsIntervalUs));
const PresentationPlan current = scheduler.PlanFrame(
ptsUs, decodedAtNs, timing);

AssertOnVsyncSlot(current, timing);
if (frame > 0) {
assert(current.targetTimeNs ==
previous.targetTimeNs + timing.periodNs);
}
previous = current;
}
}

const int64_t lastDecodedAtNs = firstDecodedAtNs +
(120 - 1) * kBurstSize * timing.periodNs;
assert(previous.targetTimeNs - lastDecodedAtNs <=
kBurstSize * timing.periodNs);
}

void Test90FpsPairBurstUsesTwoSlots() {
void Test90FpsBurstPreservesAllFrames() {
PtsPresentationScheduler scheduler;
scheduler.Configure(90.0);
const PresentationVsyncTiming timing = Timing(90.0);
Expand All @@ -165,12 +176,12 @@ void Test90FpsPairBurstUsesTwoSlots() {

AssertOnVsyncSlot(first, timing);
AssertOnVsyncSlot(second, timing);
AssertOnVsyncSlot(third, timing);
assert(second.targetTimeNs - first.targetTimeNs == timing.periodNs);
assert(third.action == PresentationAction::DROP);
assert(third.event == PresentationEvent::QUEUE_FULL);
assert(third.targetTimeNs - second.targetTimeNs == timing.periodNs);
}

void Test60FpsDoesNotGrowPresentationQueue() {
void Test60FpsBurstPreservesAllFrames() {
PtsPresentationScheduler scheduler;
scheduler.Configure(60.0);
const PresentationVsyncTiming timing = Timing(60.0);
Expand All @@ -182,11 +193,10 @@ void Test60FpsDoesNotGrowPresentationQueue() {
33333, first.targetTimeNs + kMs, timing);

AssertOnVsyncSlot(first, timing);
assert(burst.action == PresentationAction::DROP);
assert(burst.event == PresentationEvent::QUEUE_FULL);
assert(recovered.action == PresentationAction::SCHEDULE);
assert(recovered.event == PresentationEvent::CATCH_UP);
assert(recovered.targetTimeNs == first.targetTimeNs + timing.periodNs);
AssertOnVsyncSlot(burst, timing);
AssertOnVsyncSlot(recovered, timing);
assert(burst.targetTimeNs == first.targetTimeNs + timing.periodNs);
assert(recovered.targetTimeNs == burst.targetTimeNs + timing.periodNs);
}

void TestSmallLateFrameShiftsWholeTimeline() {
Expand Down Expand Up @@ -238,22 +248,23 @@ void TestDiscontinuityUsesNextAvailableSlot() {
assert(reanchored.targetTimeNs == first.targetTimeNs + timing.periodNs);
}

void TestDuplicatePtsRecoversAfterQueuedSlot() {
void TestDuplicatePtsReanchorsWithoutDropping() {
PtsPresentationScheduler scheduler;
scheduler.Configure(60.0);
const PresentationVsyncTiming timing = Timing(60.0);
const int64_t decodedAtNs = kStartNs + kMs;

const PresentationPlan first = scheduler.PlanFrame(1000, decodedAtNs, timing);
const PresentationPlan full = scheduler.PlanFrame(1000, decodedAtNs, timing);
const PresentationPlan duplicate = scheduler.PlanFrame(1000, decodedAtNs, timing);
const PresentationPlan reanchored = scheduler.PlanFrame(
1000, first.targetTimeNs + kMs, timing);

assert(full.action == PresentationAction::DROP);
assert(full.event == PresentationEvent::QUEUE_FULL);
assert(duplicate.action == PresentationAction::SCHEDULE);
assert(duplicate.event == PresentationEvent::DUPLICATE_PTS);
assert(reanchored.action == PresentationAction::SCHEDULE);
assert(reanchored.event == PresentationEvent::DUPLICATE_PTS);
assert(reanchored.targetTimeNs == first.targetTimeNs + timing.periodNs);
assert(duplicate.targetTimeNs == first.targetTimeNs + timing.periodNs);
assert(reanchored.targetTimeNs == duplicate.targetTimeNs + timing.periodNs);
}

void TestNtscCadenceSkipsARealSlotWithoutRegression() {
Expand Down Expand Up @@ -318,14 +329,14 @@ int main() {
TestRefreshRateTierBudgets();
TestTransient120FpsJitterUsesTemporaryPhaseShift();
TestSustained120FpsPeriodicJitterDoesNotDrop();
Test120FpsBurstUsesThreeUniqueSlots();
TestQueueFullRetriesAtNextAvailableSlot();
Test90FpsPairBurstUsesTwoSlots();
Test60FpsDoesNotGrowPresentationQueue();
Test120FpsBurstPreservesAllFrames();
TestRepeated120FpsBurstsDrainBetweenCallbacks();
Test90FpsBurstPreservesAllFrames();
Test60FpsBurstPreservesAllFrames();
TestSmallLateFrameShiftsWholeTimeline();
TestSevereLateFrameReanchorsImmediately();
TestDiscontinuityUsesNextAvailableSlot();
TestDuplicatePtsRecoversAfterQueuedSlot();
TestDuplicatePtsReanchorsWithoutDropping();
TestNtscCadenceSkipsARealSlotWithoutRegression();
TestObservedVsyncPhaseCannotReuseFallbackSlot();
TestInvalidPtsDropsWithoutScheduling();
Expand Down
Loading