blob: d5679a0160a86798177344481a2e058cbda13e39 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/common/animator.h"
#include "flutter/common/threads.h"
#include "flutter/fml/trace_event.h"
#include "lib/fxl/time/stopwatch.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
namespace shell {
Animator::Animator(fml::WeakPtr<Rasterizer> rasterizer,
VsyncWaiter* waiter,
Engine* engine)
: rasterizer_(rasterizer),
waiter_(waiter),
engine_(engine),
last_begin_frame_time_(),
dart_frame_deadline_(0),
layer_tree_pipeline_(fxl::MakeRefCounted<LayerTreePipeline>(2)),
pending_frame_semaphore_(1),
frame_number_(1),
paused_(false),
regenerate_layer_tree_(false),
frame_scheduled_(false),
dimension_change_pending_(false),
weak_factory_(this) {}
Animator::~Animator() = default;
void Animator::Stop() {
paused_ = true;
}
void Animator::Start() {
if (!paused_) {
return;
}
paused_ = false;
RequestFrame();
}
// Indicate that screen dimensions will be changing in order to force rendering
// of an updated frame even if the animator is currently paused.
void Animator::SetDimensionChangePending() {
dimension_change_pending_ = true;
}
// This Parity is used by the timeline component to correctly align
// GPU Workloads events with their respective Framework Workload.
const char* Animator::FrameParity() {
return (frame_number_ % 2) ? "even" : "odd";
}
static int64_t FxlToDartOrEarlier(fxl::TimePoint time) {
int64_t dart_now = Dart_TimelineGetMicros();
fxl::TimePoint fxl_now = fxl::TimePoint::Now();
return (time - fxl_now).ToMicroseconds() + dart_now;
}
void Animator::BeginFrame(fxl::TimePoint frame_start_time,
fxl::TimePoint frame_target_time) {
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);
frame_scheduled_ = false;
regenerate_layer_tree_ = false;
pending_frame_semaphore_.Signal();
if (!producer_continuation_) {
// We may already have a valid pipeline continuation in case a previous
// begin frame did not result in an Animation::Render. Simply reuse that
// instead of asking the pipeline for a fresh continuation.
producer_continuation_ = layer_tree_pipeline_->Produce();
if (!producer_continuation_) {
// If we still don't have valid continuation, the pipeline is currently
// full because the consumer is being too slow. Try again at the next
// frame interval.
TRACE_EVENT_INSTANT0("flutter", "ConsumerSlowDefer");
RequestFrame();
return;
}
}
// We have acquired a valid continuation from the pipeline and are ready
// to service potential frame.
FXL_DCHECK(producer_continuation_);
last_begin_frame_time_ = frame_start_time;
dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
{
TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
FrameParity());
engine_->BeginFrame(last_begin_frame_time_);
}
if (!frame_scheduled_) {
// We don't have another frame pending, so we're waiting on user input
// or I/O. Allow the Dart VM 100 ms.
engine_->NotifyIdle(dart_frame_deadline_ + 100000);
}
}
void Animator::Render(std::unique_ptr<flow::LayerTree> layer_tree) {
if (dimension_change_pending_ &&
layer_tree->frame_size() != last_layer_tree_size_) {
dimension_change_pending_ = false;
}
last_layer_tree_size_ = layer_tree->frame_size();
if (layer_tree) {
// Note the frame time for instrumentation.
layer_tree->set_construction_time(fxl::TimePoint::Now() -
last_begin_frame_time_);
}
// Commit the pending continuation.
producer_continuation_.Complete(std::move(layer_tree));
blink::Threads::Gpu()->PostTask([
rasterizer = rasterizer_, pipeline = layer_tree_pipeline_,
frame_id = FrameParity()
]() {
if (!rasterizer.get())
return;
TRACE_EVENT2("flutter", "GPU Workload", "mode", "basic", "frame", frame_id);
rasterizer->Draw(pipeline);
});
}
bool Animator::CanReuseLastLayerTree() {
return !regenerate_layer_tree_;
}
void Animator::DrawLastLayerTree() {
pending_frame_semaphore_.Signal();
blink::Threads::Gpu()->PostTask([rasterizer = rasterizer_]() {
if (rasterizer.get())
rasterizer->DrawLastLayerTree();
});
}
void Animator::RequestFrame(bool regenerate_layer_tree) {
if (regenerate_layer_tree) {
regenerate_layer_tree_ = true;
}
if (paused_ && !dimension_change_pending_) {
return;
}
if (!pending_frame_semaphore_.TryWait()) {
// Multiple calls to Animator::RequestFrame will still result in a
// single request to the VsyncWaiter.
return;
}
// The AwaitVSync is going to call us back at the next VSync. However, we want
// to be reasonably certain that the UI thread is not in the middle of a
// particularly expensive callout. We post the AwaitVSync to run right after
// an idle. This does NOT provide a guarantee that the UI thread has not
// started an expensive operation right after posting this message however.
// To support that, we need edge triggered wakes on VSync.
blink::Threads::UI()->PostTask(
[ self = weak_factory_.GetWeakPtr(), frame_number = frame_number_ ]() {
if (!self.get()) {
return;
}
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending",
frame_number);
self->AwaitVSync();
});
frame_scheduled_ = true;
}
void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync([self = weak_factory_.GetWeakPtr()](
fxl::TimePoint frame_start_time, fxl::TimePoint frame_target_time) {
if (self) {
if (self->CanReuseLastLayerTree()) {
self->DrawLastLayerTree();
} else {
self->BeginFrame(frame_start_time, frame_target_time);
}
}
});
engine_->NotifyIdle(dart_frame_deadline_);
}
} // namespace shell