diff --git a/engine/src/flutter/compositor/layer_host.cc b/engine/src/flutter/compositor/layer_host.cc index a1991770c3..9a635e6d4a 100644 --- a/engine/src/flutter/compositor/layer_host.cc +++ b/engine/src/flutter/compositor/layer_host.cc @@ -15,6 +15,7 @@ namespace sky { LayerHost::LayerHost(LayerHostClient* client) : client_(client), + state_(kIdle), surface_holder_(this, client->GetShell()), gl_context_(mojo::GLContext::Create(client->GetShell())), ganesh_context_(gl_context_), @@ -29,6 +30,7 @@ LayerHost::~LayerHost() { void LayerHost::SetNeedsAnimate() { scheduler_.SetNeedsFrame(); + state_ = kWaitingForBeginFrame; } void LayerHost::SetRootLayer(scoped_refptr layer) { @@ -39,6 +41,9 @@ void LayerHost::SetRootLayer(scoped_refptr layer) { void LayerHost::OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) { client_->OnSurfaceIdAvailable(surface_id.Pass()); + + if (state_ == kWaitingForSurfaceToUploadFrame) + Upload(root_layer_.get()); } void LayerHost::ReturnResources( @@ -48,6 +53,8 @@ void LayerHost::ReturnResources( void LayerHost::BeginFrame(base::TimeTicks frame_time, base::TimeTicks deadline) { + DCHECK_EQ(state_, kWaitingForBeginFrame); + state_ = kProducingFrame; client_->BeginFrame(frame_time); { @@ -57,9 +64,30 @@ void LayerHost::BeginFrame(base::TimeTicks frame_time, } Upload(root_layer_.get()); + + if (state_ == kProducingFrame) + state_ = kIdle; } void LayerHost::Upload(Layer* layer) { + if (!surface_holder_.IsReadyForFrame()) { + if (state_ == kProducingFrame) { + // Currently we use a timer to drive the BeginFrame cycle, which means we + // can produce frames before the surfaces service is ready to receive + // frames from us. In that situation, we wait for surfaces before + // uploading the frame. The upload will actually happen when the surface + // id is available (i.e., in OnSurfaceIdAvailable). If SetNeedsAnimate is + // called before then, we'll go back into the kWaitingForBeginFrame state + // and defer to the timer again. + // + // We can avoid this complexity if we use feedback from the surfaces + // service to drive the BeginFrame cycle. In that approach, we wouldn't + // get here before we've attached to a surface. + state_ = kWaitingForSurfaceToUploadFrame; + } + return; + } + gfx::Size size = layer->size(); surface_holder_.SetSize(size); diff --git a/engine/src/flutter/compositor/layer_host.h b/engine/src/flutter/compositor/layer_host.h index 197ff9a9e8..17b8ae58d2 100644 --- a/engine/src/flutter/compositor/layer_host.h +++ b/engine/src/flutter/compositor/layer_host.h @@ -41,6 +41,13 @@ class LayerHost : public SurfaceHolder::Client, public Scheduler::Client { void SetRootLayer(scoped_refptr layer); private: + enum State { + kIdle, + kWaitingForBeginFrame, + kProducingFrame, + kWaitingForSurfaceToUploadFrame, + }; + // SurfaceHolder::Client void OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) override; void ReturnResources( @@ -53,6 +60,7 @@ class LayerHost : public SurfaceHolder::Client, public Scheduler::Client { void Upload(Layer* layer); LayerHostClient* client_; + State state_; SurfaceHolder surface_holder_; base::WeakPtr gl_context_; mojo::GaneshContext ganesh_context_; diff --git a/engine/src/flutter/compositor/surface_holder.cc b/engine/src/flutter/compositor/surface_holder.cc index 5288efa517..de9662209b 100644 --- a/engine/src/flutter/compositor/surface_holder.cc +++ b/engine/src/flutter/compositor/surface_holder.cc @@ -28,10 +28,14 @@ SurfaceHolder::SurfaceHolder(Client* client, mojo::Shell* shell) } SurfaceHolder::~SurfaceHolder() { - if (surface_id_) + if (surface_ && surface_id_) surface_->DestroySurface(surface_id_.Clone()); } +bool SurfaceHolder::IsReadyForFrame() const { + return surface_; +} + void SurfaceHolder::SubmitFrame(mojo::FramePtr frame) { surface_->SubmitFrame(surface_id_.Clone(), frame.Pass()); } diff --git a/engine/src/flutter/compositor/surface_holder.h b/engine/src/flutter/compositor/surface_holder.h index 3ff3703cac..59a0fdb1df 100644 --- a/engine/src/flutter/compositor/surface_holder.h +++ b/engine/src/flutter/compositor/surface_holder.h @@ -34,6 +34,8 @@ class SurfaceHolder : public mojo::SurfaceClient { explicit SurfaceHolder(Client* client, mojo::Shell* shell); ~SurfaceHolder() override; + bool IsReadyForFrame() const; + void SetSize(const gfx::Size& size); void SubmitFrame(mojo::FramePtr frame);