mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Do not call Animator::Delegate::OnAnimatorNotifyIdle until at least one frame has been rendered. (flutter/engine#29015)
This commit is contained in:
parent
b107c3700e
commit
074f1d0842
@ -147,7 +147,7 @@ void Animator::BeginFrame(
|
||||
delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
|
||||
}
|
||||
|
||||
if (!frame_scheduled_) {
|
||||
if (!frame_scheduled_ && has_rendered_) {
|
||||
// Under certain workloads (such as our parent view resizing us, which is
|
||||
// communicated to us by repeat viewport metrics events), we won't
|
||||
// actually have a frame scheduled yet, despite the fact that we *will* be
|
||||
@ -177,6 +177,7 @@ void Animator::BeginFrame(
|
||||
}
|
||||
|
||||
void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
|
||||
has_rendered_ = true;
|
||||
if (dimension_change_pending_ &&
|
||||
layer_tree->frame_size() != last_layer_tree_size_) {
|
||||
dimension_change_pending_ = false;
|
||||
@ -268,9 +269,10 @@ void Animator::AwaitVSync() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
delegate_.OnAnimatorNotifyIdle(
|
||||
dart_frame_deadline_.ToEpochDelta().ToMicroseconds());
|
||||
if (has_rendered_) {
|
||||
delegate_.OnAnimatorNotifyIdle(
|
||||
dart_frame_deadline_.ToEpochDelta().ToMicroseconds());
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::ScheduleSecondaryVsyncCallback(uintptr_t id,
|
||||
|
||||
@ -117,6 +117,7 @@ class Animator final {
|
||||
bool dimension_change_pending_ = false;
|
||||
SkISize last_layer_tree_size_ = {0, 0};
|
||||
std::deque<uint64_t> trace_flow_ids_;
|
||||
bool has_rendered_ = false;
|
||||
|
||||
fml::WeakPtrFactory<Animator> weak_factory_;
|
||||
|
||||
|
||||
@ -18,6 +18,25 @@
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
class FakeAnimatorDelegate : public Animator::Delegate {
|
||||
public:
|
||||
void OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
|
||||
uint64_t frame_number) override {}
|
||||
|
||||
void OnAnimatorNotifyIdle(int64_t deadline) override {
|
||||
notify_idle_called_ = true;
|
||||
}
|
||||
|
||||
void OnAnimatorDraw(
|
||||
std::shared_ptr<Pipeline<flutter::LayerTree>> pipeline,
|
||||
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override {}
|
||||
|
||||
void OnAnimatorDrawLastLayerTree(
|
||||
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override {}
|
||||
|
||||
bool notify_idle_called_ = false;
|
||||
};
|
||||
|
||||
TEST_F(ShellTest, VSyncTargetTime) {
|
||||
// Add native callbacks to listen for window.onBeginFrame
|
||||
int64_t target_time;
|
||||
@ -103,7 +122,8 @@ TEST_F(ShellTest, AnimatorStartsPaused) {
|
||||
auto settings = CreateSettingsForFixture();
|
||||
TaskRunners task_runners = GetTaskRunnersForFixture();
|
||||
|
||||
auto shell = CreateShell(std::move(settings), task_runners);
|
||||
auto shell = CreateShell(std::move(settings), task_runners,
|
||||
/* simulate_vsync=*/true);
|
||||
ASSERT_TRUE(DartVMRef::IsInstanceRunning());
|
||||
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
@ -120,5 +140,88 @@ TEST_F(ShellTest, AnimatorStartsPaused) {
|
||||
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
|
||||
}
|
||||
|
||||
TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) {
|
||||
FakeAnimatorDelegate delegate;
|
||||
TaskRunners task_runners = {
|
||||
"test",
|
||||
CreateNewThread(), // platform
|
||||
CreateNewThread(), // raster
|
||||
CreateNewThread(), // ui
|
||||
CreateNewThread() // io
|
||||
};
|
||||
|
||||
auto clock = std::make_shared<ShellTestVsyncClock>();
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
std::shared_ptr<Animator> animator;
|
||||
|
||||
auto flush_vsync_task = [&] {
|
||||
fml::AutoResetWaitableEvent ui_latch;
|
||||
task_runners.GetUITaskRunner()->PostTask([&] { ui_latch.Signal(); });
|
||||
do {
|
||||
clock->SimulateVSync();
|
||||
} while (ui_latch.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1)));
|
||||
latch.Signal();
|
||||
};
|
||||
|
||||
// Create the animator on the UI task runner.
|
||||
task_runners.GetUITaskRunner()->PostTask([&] {
|
||||
auto vsync_waiter = static_cast<std::unique_ptr<VsyncWaiter>>(
|
||||
std::make_unique<ShellTestVsyncWaiter>(task_runners, clock));
|
||||
animator = std::make_unique<Animator>(delegate, task_runners,
|
||||
std::move(vsync_waiter));
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
|
||||
// Validate it has not notified idle and start it. This will request a frame.
|
||||
task_runners.GetUITaskRunner()->PostTask([&] {
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
animator->Start();
|
||||
// Immediately request a frame saying it can reuse the last layer tree to
|
||||
// avoid more calls to BeginFrame by the animator.
|
||||
animator->RequestFrame(false);
|
||||
task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
|
||||
});
|
||||
latch.Wait();
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
|
||||
// Validate it has not notified idle and try to render.
|
||||
task_runners.GetUITaskRunner()->PostDelayedTask(
|
||||
[&] {
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
auto layer_tree =
|
||||
std::make_unique<LayerTree>(SkISize::Make(600, 800), 1.0);
|
||||
animator->Render(std::move(layer_tree));
|
||||
task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
|
||||
},
|
||||
// See kNotifyIdleTaskWaitTime in animator.cc.
|
||||
fml::TimeDelta::FromMilliseconds(60));
|
||||
latch.Wait();
|
||||
|
||||
// Still hasn't notified idle because there has been no frame request.
|
||||
task_runners.GetUITaskRunner()->PostTask([&] {
|
||||
ASSERT_FALSE(delegate.notify_idle_called_);
|
||||
// False to avoid getting cals to BeginFrame that will request more frames
|
||||
// before we are ready.
|
||||
animator->RequestFrame(false);
|
||||
task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
|
||||
});
|
||||
latch.Wait();
|
||||
|
||||
// Now it should notify idle. Make sure it is destroyed on the UI thread.
|
||||
ASSERT_TRUE(delegate.notify_idle_called_);
|
||||
|
||||
// Stop and do one more flush so we can safely clean up on the UI thread.
|
||||
animator->Stop();
|
||||
task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
|
||||
latch.Wait();
|
||||
|
||||
task_runners.GetUITaskRunner()->PostTask([&] {
|
||||
animator.reset();
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user