diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 04bb29a1f19e4..453259cd67279 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -23,18 +23,19 @@ namespace flutter { class LayerTree; +// The result status of CompositorContext::ScopedFrame::Raster. enum class RasterStatus { // Frame has been successfully rasterized. kSuccess, - // Frame is submitted twice. This is only used on Android when - // switching the background surface to FlutterImageView. + // Frame has been submited, but must be submitted again. This is only used + // on Android when switching the background surface to FlutterImageView. // // On Android, the first frame doesn't make the image available // to the ImageReader right away. The second frame does. // // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 kResubmit, - // Frame is dropped and a new frame with the same layer tree is + // Frame has be dropped and a new frame with the same layer tree must be // attempted. // // This is currently used to wait for the thread merger to merge @@ -44,18 +45,6 @@ enum class RasterStatus { // with separate threads for rasterization and platform tasks, // potentially leading to different performance characteristics. kSkipAndRetry, - // Frame has been successfully rasterized, but there are additional items in - // the pipeline waiting to be consumed. This is currently - // only used when thread configuration change occurs. - kEnqueuePipeline, - // Failed to rasterize the frame. - kFailed, - // Layer tree was discarded due to LayerTreeDiscardCallback or inability to - // access the GPU. - kDiscarded, - // Drawing was yielded to allow the correct thread to draw as a result of the - // RasterThreadMerger. - kYielded, }; class FrameDamage { diff --git a/flow/frame_timings.cc b/flow/frame_timings.cc index eed35fd4c755a..339374d77b837 100644 --- a/flow/frame_timings.cc +++ b/flow/frame_timings.cc @@ -254,4 +254,8 @@ const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const { return frame_number_trace_arg_val_.c_str(); } +void FrameTimingsRecorder::AssertInState(State state) const { + FML_DCHECK(state_ == state); +} + } // namespace flutter diff --git a/flow/frame_timings.h b/flow/frame_timings.h index e477a80b8f955..c81bcc3362298 100644 --- a/flow/frame_timings.h +++ b/flow/frame_timings.h @@ -116,6 +116,13 @@ class FrameTimingsRecorder { /// Returns the recorded time from when `RecordRasterEnd` is called. FrameTiming GetRecordedTime() const; + /// Asserts in unopt builds that the recorder is current at the specified + /// state. + /// + /// Instead of adding a `GetState` method and asserting on the result, this + /// method prevents other logic from relying on the state. + void AssertInState(State state) const; + private: FML_FRIEND_TEST(FrameTimingsRecorderTest, ThrowWhenRecordBuildBeforeVsync); FML_FRIEND_TEST(FrameTimingsRecorderTest, diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 5f573da2099a0..af162d58e0c4f 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -94,6 +94,27 @@ class LayerTree { FML_DISALLOW_COPY_AND_ASSIGN(LayerTree); }; +// The information to draw a layer tree to a specified view. +struct LayerTreeTask { + public: + LayerTreeTask(int64_t view_id, + std::unique_ptr layer_tree, + float device_pixel_ratio) + : view_id(view_id), + layer_tree(std::move(layer_tree)), + device_pixel_ratio(device_pixel_ratio) {} + + /// The target view to draw to. + int64_t view_id; + /// The target layer tree to be drawn. + std::unique_ptr layer_tree; + /// The pixel ratio of the target view. + float device_pixel_ratio; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(LayerTreeTask); +}; + } // namespace flutter #endif // FLUTTER_FLOW_LAYERS_LAYER_TREE_H_ diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index a036d4723cb34..bc3de031f4411 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -23,7 +23,7 @@ class RuntimeDelegate { public: virtual std::string DefaultRouteName() = 0; - virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; + virtual void ScheduleFrame(bool regenerate_layer_trees = true) = 0; virtual void Render(std::unique_ptr layer_tree, float device_pixel_ratio) = 0; diff --git a/shell/common/animator.cc b/shell/common/animator.cc index b0d8f62a22033..3dd925cee6213 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -4,6 +4,7 @@ #include "flutter/shell/common/animator.h" +#include "flutter/common/constants.h" #include "flutter/flow/frame_timings.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" @@ -28,12 +29,12 @@ Animator::Animator(Delegate& delegate, task_runners_(task_runners), waiter_(std::move(waiter)), #if SHELL_ENABLE_METAL - layer_tree_pipeline_(std::make_shared(2)), + layer_tree_pipeline_(std::make_shared(2)), #else // SHELL_ENABLE_METAL // TODO(dnfield): We should remove this logic and set the pipeline depth // back to 2 in this case. See // https://github.com/flutter/engine/pull/9132 for discussion. - layer_tree_pipeline_(std::make_shared( + layer_tree_pipeline_(std::make_shared( task_runners.GetPlatformTaskRunner() == task_runners.GetRasterTaskRunner() ? 1 @@ -84,7 +85,7 @@ void Animator::BeginFrame( } frame_scheduled_ = false; - regenerate_layer_tree_ = false; + regenerate_layer_trees_ = false; pending_frame_semaphore_.Signal(); if (!producer_continuation_) { @@ -143,7 +144,6 @@ void Animator::BeginFrame( void Animator::Render(std::unique_ptr layer_tree, float device_pixel_ratio) { has_rendered_ = true; - last_layer_tree_size_ = layer_tree->frame_size(); if (!frame_timings_recorder_) { // Framework can directly call render with a built scene. @@ -161,12 +161,16 @@ void Animator::Render(std::unique_ptr layer_tree, delegate_.OnAnimatorUpdateLatestFrameTargetTime( frame_timings_recorder_->GetVsyncTargetTime()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), std::move(frame_timings_recorder_), - device_pixel_ratio); + // TODO(dkwingsmt): Currently only supports a single window. + // See https://github.com/flutter/flutter/issues/135530, item 2. + int64_t view_id = kFlutterImplicitViewId; + std::vector> layer_trees_tasks; + layer_trees_tasks.push_back(std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio)); // Commit the pending continuation. PipelineProduceResult result = - producer_continuation_.Complete(std::move(layer_tree_item)); + producer_continuation_.Complete(std::make_unique( + std::move(layer_trees_tasks), std::move(frame_timings_recorder_))); if (!result.success) { FML_DLOG(INFO) << "No pending continuation to commit"; @@ -188,15 +192,15 @@ const std::weak_ptr Animator::GetVsyncWaiter() const { return weak; } -bool Animator::CanReuseLastLayerTree() { - return !regenerate_layer_tree_; +bool Animator::CanReuseLastLayerTrees() { + return !regenerate_layer_trees_; } -void Animator::DrawLastLayerTree( +void Animator::DrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { // This method is very cheap, but this makes it explicitly clear in trace // files. - TRACE_EVENT0("flutter", "Animator::DrawLastLayerTree"); + TRACE_EVENT0("flutter", "Animator::DrawLastLayerTrees"); pending_frame_semaphore_.Signal(); // In this case BeginFrame doesn't get called, we need to @@ -206,18 +210,18 @@ void Animator::DrawLastLayerTree( const auto now = fml::TimePoint::Now(); frame_timings_recorder->RecordBuildStart(now); frame_timings_recorder->RecordBuildEnd(now); - delegate_.OnAnimatorDrawLastLayerTree(std::move(frame_timings_recorder)); + delegate_.OnAnimatorDrawLastLayerTrees(std::move(frame_timings_recorder)); } -void Animator::RequestFrame(bool regenerate_layer_tree) { - if (regenerate_layer_tree) { +void Animator::RequestFrame(bool regenerate_layer_trees) { + if (regenerate_layer_trees) { // This event will be closed by BeginFrame. BeginFrame will only be called - // if regenerating the layer tree. If a frame has been requested to update + // if regenerating the layer trees. If a frame has been requested to update // an external texture, this will be false and no BeginFrame call will // happen. TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_request_number_); - regenerate_layer_tree_ = true; + regenerate_layer_trees_ = true; } if (!pending_frame_semaphore_.TryWait()) { @@ -248,8 +252,8 @@ void Animator::AwaitVSync() { [self = weak_factory_.GetWeakPtr()]( std::unique_ptr frame_timings_recorder) { if (self) { - if (self->CanReuseLastLayerTree()) { - self->DrawLastLayerTree(std::move(frame_timings_recorder)); + if (self->CanReuseLastLayerTrees()) { + self->DrawLastLayerTrees(std::move(frame_timings_recorder)); } else { self->BeginFrame(std::move(frame_timings_recorder)); } diff --git a/shell/common/animator.h b/shell/common/animator.h index 4e6295957bdcc..8a92705ede9e6 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -39,10 +39,9 @@ class Animator final { virtual void OnAnimatorUpdateLatestFrameTargetTime( fml::TimePoint frame_target_time) = 0; - virtual void OnAnimatorDraw( - std::shared_ptr pipeline) = 0; + virtual void OnAnimatorDraw(std::shared_ptr pipeline) = 0; - virtual void OnAnimatorDrawLastLayerTree( + virtual void OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) = 0; }; @@ -52,7 +51,7 @@ class Animator final { ~Animator(); - void RequestFrame(bool regenerate_layer_tree = true); + void RequestFrame(bool regenerate_layer_trees = true); void Render(std::unique_ptr layer_tree, float device_pixel_ratio); @@ -85,9 +84,9 @@ class Animator final { private: void BeginFrame(std::unique_ptr frame_timings_recorder); - bool CanReuseLastLayerTree(); + bool CanReuseLastLayerTrees(); - void DrawLastLayerTree( + void DrawLastLayerTrees( std::unique_ptr frame_timings_recorder); void AwaitVSync(); @@ -102,12 +101,11 @@ class Animator final { std::unique_ptr frame_timings_recorder_; uint64_t frame_request_number_ = 1; fml::TimeDelta dart_frame_deadline_; - std::shared_ptr layer_tree_pipeline_; + std::shared_ptr layer_tree_pipeline_; fml::Semaphore pending_frame_semaphore_; - LayerTreePipeline::ProducerContinuation producer_continuation_; - bool regenerate_layer_tree_ = false; + FramePipeline::ProducerContinuation producer_continuation_; + bool regenerate_layer_trees_ = false; bool frame_scheduled_ = false; - SkISize last_layer_tree_size_ = {0, 0}; std::deque trace_flow_ids_; bool has_rendered_ = false; diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 459c77b0ec672..9190659fc6f55 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -41,10 +41,10 @@ class FakeAnimatorDelegate : public Animator::Delegate { MOCK_METHOD(void, OnAnimatorDraw, - (std::shared_ptr pipeline), + (std::shared_ptr pipeline), (override)); - void OnAnimatorDrawLastLayerTree( + void OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) override {} bool notify_idle_called_ = false; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 920122c9a23dc..ea0cb43d9be18 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -455,8 +455,8 @@ std::string Engine::DefaultRouteName() { return "/"; } -void Engine::ScheduleFrame(bool regenerate_layer_tree) { - animator_->RequestFrame(regenerate_layer_tree); +void Engine::ScheduleFrame(bool regenerate_layer_trees) { + animator_->RequestFrame(regenerate_layer_trees); } void Engine::Render(std::unique_ptr layer_tree, diff --git a/shell/common/engine.h b/shell/common/engine.h index f7d888de425f2..a60d8b81f8ed4 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -29,7 +29,6 @@ #include "flutter/shell/common/display_manager.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/pointer_data_dispatcher.h" -#include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell_io_manager.h" @@ -828,7 +827,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { void SetAccessibilityFeatures(int32_t flags); // |RuntimeDelegate| - void ScheduleFrame(bool regenerate_layer_tree) override; + void ScheduleFrame(bool regenerate_layer_trees) override; /// Schedule a frame with the default parameter of regenerating the layer /// tree. diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index c76ccbedbcd49..72401eec40cd5 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -253,20 +253,6 @@ class Pipeline { FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; -struct LayerTreeItem { - LayerTreeItem(std::unique_ptr layer_tree, - std::unique_ptr frame_timings_recorder, - float device_pixel_ratio) - : layer_tree(std::move(layer_tree)), - frame_timings_recorder(std::move(frame_timings_recorder)), - device_pixel_ratio(device_pixel_ratio) {} - std::unique_ptr layer_tree; - std::unique_ptr frame_timings_recorder; - float device_pixel_ratio; -}; - -using LayerTreePipeline = Pipeline; - } // namespace flutter #endif // FLUTTER_SHELL_COMMON_PIPELINE_H_ diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 9a7e6e16552b0..7ab349d353f37 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -42,7 +42,8 @@ static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000); Rasterizer::Rasterizer(Delegate& delegate, MakeGpuImageBehavior gpu_image_behavior) - : delegate_(delegate), + : is_torn_down_(false), + delegate_(delegate), gpu_image_behavior_(gpu_image_behavior), compositor_context_(std::make_unique(*this)), user_override_resource_cache_bytes_(false), @@ -108,6 +109,7 @@ void Rasterizer::TeardownExternalViewEmbedder() { } void Rasterizer::Teardown() { + is_torn_down_ = true; if (surface_) { auto context_switch = surface_->MakeRenderContextCurrent(); if (context_switch->GetResult()) { @@ -119,7 +121,7 @@ void Rasterizer::Teardown() { surface_.reset(); } - last_layer_tree_.reset(); + view_records_.clear(); if (raster_thread_merger_.get() != nullptr && raster_thread_merger_.get()->IsMerged()) { @@ -129,6 +131,20 @@ void Rasterizer::Teardown() { } } +bool Rasterizer::IsTornDown() { + return is_torn_down_; +} + +std::optional Rasterizer::GetLastDrawStatus( + int64_t view_id) { + auto found = view_records_.find(view_id); + if (found != view_records_.end()) { + return found->second.last_draw_status; + } else { + return std::optional(); + } +} + void Rasterizer::EnableThreadMergerIfNeeded() { if (raster_thread_merger_) { raster_thread_merger_->Enable(); @@ -161,11 +177,7 @@ void Rasterizer::NotifyLowMemoryWarning() const { } void Rasterizer::CollectView(int64_t view_id) { - // TODO(dkwingsmt): When Rasterizer supports multi-view, this method should - // correctly clear the view corresponding to the ID. - if (view_id == kFlutterImplicitViewId) { - last_layer_tree_.reset(); - } + view_records_.erase(view_id); } std::shared_ptr Rasterizer::GetTextureRegistry() { @@ -176,74 +188,79 @@ GrDirectContext* Rasterizer::GetGrContext() { return surface_ ? surface_->GetContext() : nullptr; } -flutter::LayerTree* Rasterizer::GetLastLayerTree() { - return last_layer_tree_.get(); +flutter::LayerTree* Rasterizer::GetLastLayerTree(int64_t view_id) { + auto found = view_records_.find(view_id); + if (found == view_records_.end()) { + return nullptr; + } + auto& last_task = found->second.last_successful_task; + if (last_task == nullptr) { + return nullptr; + } + return last_task->layer_tree.get(); } -void Rasterizer::DrawLastLayerTree( +void Rasterizer::DrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { - if (!last_layer_tree_ || !surface_) { + if (!surface_) { + return; + } + std::vector> tasks; + for (auto& [view_id, view_record] : view_records_) { + if (view_record.last_successful_task) { + tasks.push_back(std::move(view_record.last_successful_task)); + } + } + if (tasks.empty()) { return; } - RasterStatus raster_status = DrawToSurface( - *frame_timings_recorder, *last_layer_tree_, last_device_pixel_ratio_); + + DoDrawResult result = + DrawToSurfaces(*frame_timings_recorder, std::move(tasks)); // EndFrame should perform cleanups for the external_view_embedder. if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) { - bool should_resubmit_frame = ShouldResubmitFrame(raster_status); + bool should_resubmit_frame = ShouldResubmitFrame(result); external_view_embedder_->SetUsedThisFrame(false); external_view_embedder_->EndFrame(should_resubmit_frame, raster_thread_merger_); } } -RasterStatus Rasterizer::Draw( - const std::shared_ptr& pipeline) { +DrawStatus Rasterizer::Draw(const std::shared_ptr& pipeline) { TRACE_EVENT0("flutter", "GPURasterizer::Draw"); if (raster_thread_merger_ && !raster_thread_merger_->IsOnRasterizingThread()) { // we yield and let this frame be serviced on the right thread. - return RasterStatus::kYielded; + return DrawStatus::kYielded; } FML_DCHECK(delegate_.GetTaskRunners() .GetRasterTaskRunner() ->RunsTasksOnCurrentThread()); DoDrawResult draw_result; - LayerTreePipeline::Consumer consumer = - [&draw_result, this, - &delegate = delegate_](std::unique_ptr item) { - // TODO(dkwingsmt): Use a proper view ID when Rasterizer supports - // multi-view. - int64_t view_id = kFlutterImplicitViewId; - std::unique_ptr layer_tree = std::move(item->layer_tree); - std::unique_ptr frame_timings_recorder = - std::move(item->frame_timings_recorder); - float device_pixel_ratio = item->device_pixel_ratio; - if (delegate.ShouldDiscardLayerTree(view_id, *layer_tree.get())) { - draw_result.raster_status = RasterStatus::kDiscarded; - } else { - draw_result = DoDraw(std::move(frame_timings_recorder), - std::move(layer_tree), device_pixel_ratio); - } - }; + FramePipeline::Consumer consumer = [&draw_result, + this](std::unique_ptr item) { + draw_result = DoDraw(std::move(item->frame_timings_recorder), + std::move(item->layer_tree_tasks)); + }; PipelineConsumeResult consume_result = pipeline->Consume(consumer); if (consume_result == PipelineConsumeResult::NoneAvailable) { - return RasterStatus::kFailed; + return DrawStatus::kPipelineEmpty; } // if the raster status is to resubmit the frame, we push the frame to the // front of the queue and also change the consume status to more available. - bool should_resubmit_frame = ShouldResubmitFrame(draw_result.raster_status); + bool should_resubmit_frame = ShouldResubmitFrame(draw_result); if (should_resubmit_frame) { auto front_continuation = pipeline->ProduceIfEmpty(); - PipelineProduceResult pipeline_result = front_continuation.Complete( - std::move(draw_result.resubmitted_layer_tree_item)); + PipelineProduceResult pipeline_result = + front_continuation.Complete(std::move(draw_result.resubmitted_item)); if (pipeline_result.success) { consume_result = PipelineConsumeResult::MoreAvailable; } - } else if (draw_result.raster_status == RasterStatus::kEnqueuePipeline) { + } else if (draw_result.status == DoDrawStatus::kEnqueuePipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } @@ -270,12 +287,29 @@ RasterStatus Rasterizer::Draw( break; } - return draw_result.raster_status; + return ToDrawStatus(draw_result.status); +} + +bool Rasterizer::ShouldResubmitFrame(const DoDrawResult& result) { + if (result.resubmitted_item) { + FML_CHECK(!result.resubmitted_item->layer_tree_tasks.empty()); + return true; + } + return false; } -bool Rasterizer::ShouldResubmitFrame(const RasterStatus& raster_status) { - return raster_status == RasterStatus::kResubmit || - raster_status == RasterStatus::kSkipAndRetry; +DrawStatus Rasterizer::ToDrawStatus(DoDrawStatus status) { + switch (status) { + case DoDrawStatus::kEnqueuePipeline: + return DrawStatus::kDone; + case DoDrawStatus::kNotSetUp: + return DrawStatus::kNotSetUp; + case DoDrawStatus::kGpuUnavailable: + return DrawStatus::kGpuUnavailable; + case DoDrawStatus::kDone: + return DrawStatus::kDone; + } + FML_UNREACHABLE(); } namespace { @@ -393,42 +427,31 @@ fml::Milliseconds Rasterizer::GetFrameBudget() const { Rasterizer::DoDrawResult Rasterizer::DoDraw( std::unique_ptr frame_timings_recorder, - std::unique_ptr layer_tree, - float device_pixel_ratio) { + std::vector> tasks) { TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter", "Rasterizer::DoDraw", /*flow_id_count=*/0, /*flow_ids=*/nullptr); FML_DCHECK(delegate_.GetTaskRunners() .GetRasterTaskRunner() ->RunsTasksOnCurrentThread()); + frame_timings_recorder->AssertInState(FrameTimingsRecorder::State::kBuildEnd); - if (!layer_tree || !surface_) { - return DoDrawResult{ - .raster_status = RasterStatus::kFailed, - }; + if (tasks.empty()) { + return DoDrawResult{DoDrawStatus::kDone}; + } + if (!surface_) { + return DoDrawResult{DoDrawStatus::kNotSetUp}; } PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); persistent_cache->ResetStoredNewShaders(); - RasterStatus raster_status = - DrawToSurface(*frame_timings_recorder, *layer_tree, device_pixel_ratio); - if (raster_status == RasterStatus::kSuccess) { - last_layer_tree_ = std::move(layer_tree); - last_device_pixel_ratio_ = device_pixel_ratio; - } else if (ShouldResubmitFrame(raster_status)) { - return DoDrawResult{ - .raster_status = raster_status, - .resubmitted_layer_tree_item = std::make_unique( - std::move(layer_tree), - frame_timings_recorder->CloneUntil( - FrameTimingsRecorder::State::kBuildEnd), - device_pixel_ratio), - }; - } else if (raster_status == RasterStatus::kDiscarded) { - return DoDrawResult{ - .raster_status = raster_status, - }; + DoDrawResult result = + DrawToSurfaces(*frame_timings_recorder, std::move(tasks)); + + FML_DCHECK(result.status != DoDrawStatus::kEnqueuePipeline); + if (result.status == DoDrawStatus::kGpuUnavailable) { + return DoDrawResult{DoDrawStatus::kGpuUnavailable}; } if (persistent_cache->IsDumpingSkp() && @@ -497,73 +520,162 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( if (raster_thread_merger_->DecrementLease() == fml::RasterThreadStatus::kUnmergedNow) { return DoDrawResult{ - .raster_status = RasterStatus::kEnqueuePipeline, + .status = DoDrawStatus::kEnqueuePipeline, + .resubmitted_item = std::move(result.resubmitted_item), }; } } - return DoDrawResult{ - .raster_status = raster_status, - }; + return result; } -RasterStatus Rasterizer::DrawToSurface( +Rasterizer::DoDrawResult Rasterizer::DrawToSurfaces( FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio) { - TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface"); + std::vector> tasks) { + TRACE_EVENT0("flutter", "Rasterizer::DrawToSurfaces"); FML_DCHECK(surface_); + frame_timings_recorder.AssertInState(FrameTimingsRecorder::State::kBuildEnd); - RasterStatus raster_status; + DoDrawResult result{ + .status = DoDrawStatus::kDone, + }; if (surface_->AllowsDrawingWhenGpuDisabled()) { - raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree, - device_pixel_ratio); + result.resubmitted_item = + DrawToSurfacesUnsafe(frame_timings_recorder, std::move(tasks)); } else { delegate_.GetIsGpuDisabledSyncSwitch()->Execute( fml::SyncSwitch::Handlers() - .SetIfTrue([&] { raster_status = RasterStatus::kDiscarded; }) + .SetIfTrue([&] { + result.status = DoDrawStatus::kGpuUnavailable; + frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); + frame_timings_recorder.RecordRasterEnd(); + }) .SetIfFalse([&] { - raster_status = DrawToSurfaceUnsafe( - frame_timings_recorder, layer_tree, device_pixel_ratio); + result.resubmitted_item = DrawToSurfacesUnsafe( + frame_timings_recorder, std::move(tasks)); })); } + frame_timings_recorder.AssertInState(FrameTimingsRecorder::State::kRasterEnd); - return raster_status; + return result; } -/// Unsafe because it assumes we have access to the GPU which isn't the case -/// when iOS is backgrounded, for example. -/// \see Rasterizer::DrawToSurface -RasterStatus Rasterizer::DrawToSurfaceUnsafe( +std::unique_ptr Rasterizer::DrawToSurfacesUnsafe( FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio) { - FML_DCHECK(surface_); + std::vector> tasks) { + // TODO(dkwingsmt): The rasterizer only supports rendering a single view + // and that view must be the implicit view. Properly support multi-view + // in the future. + // See https://github.com/flutter/flutter/issues/135530, item 2 & 4. + FML_CHECK(tasks.size() == 1u) << "Unexpected size of " << tasks.size(); + FML_DCHECK(tasks.front()->view_id == kFlutterImplicitViewId); compositor_context_->ui_time().SetLapTime( frame_timings_recorder.GetBuildDuration()); - DlCanvas* embedder_root_canvas = nullptr; + // First traverse: Filter out discarded trees + auto task_iter = tasks.begin(); + while (task_iter != tasks.end()) { + LayerTreeTask& task = **task_iter; + if (delegate_.ShouldDiscardLayerTree(task.view_id, *task.layer_tree)) { + EnsureViewRecord(task.view_id).last_draw_status = + DrawSurfaceStatus::kDiscarded; + task_iter = tasks.erase(task_iter); + } else { + ++task_iter; + } + } + if (tasks.empty()) { + frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); + frame_timings_recorder.RecordRasterEnd(); + return nullptr; + } + if (external_view_embedder_) { FML_DCHECK(!external_view_embedder_->GetUsedThisFrame()); external_view_embedder_->SetUsedThisFrame(true); external_view_embedder_->BeginFrame( - layer_tree.frame_size(), surface_->GetContext(), device_pixel_ratio, - raster_thread_merger_); - embedder_root_canvas = external_view_embedder_->GetRootCanvas(); + // TODO(dkwingsmt): Add all views here. + // See https://github.com/flutter/flutter/issues/135530, item 4. + tasks.front()->layer_tree->frame_size(), surface_->GetContext(), + tasks.front()->device_pixel_ratio, raster_thread_merger_); + } + + std::optional presentation_time = std::nullopt; + // TODO (https://github.com/flutter/flutter/issues/105596): this can be in + // the past and might need to get snapped to future as this frame could + // have been resubmitted. `presentation_time` on SubmitInfo is not set + // in this case. + { + const auto vsync_target_time = frame_timings_recorder.GetVsyncTargetTime(); + if (vsync_target_time > fml::TimePoint::Now()) { + presentation_time = vsync_target_time; + } } frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); + // Second traverse: draw all layer trees. + std::vector> resubmitted_tasks; + for (std::unique_ptr& task : tasks) { + int64_t view_id = task->view_id; + std::unique_ptr layer_tree = std::move(task->layer_tree); + float device_pixel_ratio = task->device_pixel_ratio; + + DrawSurfaceStatus status = DrawToSurfaceUnsafe( + view_id, *layer_tree, device_pixel_ratio, presentation_time); + FML_DCHECK(status != DrawSurfaceStatus::kDiscarded); + + auto& view_record = EnsureViewRecord(task->view_id); + view_record.last_draw_status = status; + if (status == DrawSurfaceStatus::kSuccess) { + view_record.last_successful_task = std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio); + } else if (status == DrawSurfaceStatus::kRetry) { + resubmitted_tasks.push_back(std::make_unique( + view_id, std::move(layer_tree), device_pixel_ratio)); + } + } + // TODO(dkwingsmt): Pass in raster cache(s) for all views. + // See https://github.com/flutter/flutter/issues/135530, item 4. + frame_timings_recorder.RecordRasterEnd(&compositor_context_->raster_cache()); + FireNextFrameCallbackIfPresent(); + + if (surface_->GetContext()) { + surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); + } + + if (resubmitted_tasks.empty()) { + return nullptr; + } else { + return std::make_unique( + std::move(resubmitted_tasks), + frame_timings_recorder.CloneUntil( + FrameTimingsRecorder::State::kBuildEnd)); + } +} + +/// \see Rasterizer::DrawToSurfaces +DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe( + int64_t view_id, + flutter::LayerTree& layer_tree, + float device_pixel_ratio, + std::optional presentation_time) { + FML_DCHECK(surface_); + + DlCanvas* embedder_root_canvas = nullptr; + if (external_view_embedder_) { + // TODO(dkwingsmt): Add view ID here. + embedder_root_canvas = external_view_embedder_->GetRootCanvas(); + } + // On Android, the external view embedder deletes surfaces in `BeginFrame`. // // Deleting a surface also clears the GL context. Therefore, acquire the // frame after calling `BeginFrame` as this operation resets the GL context. auto frame = surface_->AcquireFrame(layer_tree.frame_size()); if (frame == nullptr) { - frame_timings_recorder.RecordRasterEnd( - &compositor_context_->raster_cache()); - return RasterStatus::kFailed; + return DrawSurfaceStatus::kFailed; } // If the external view embedder has specified an optional root surface, the @@ -604,7 +716,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( damage = std::make_unique(); auto existing_damage = frame->framebuffer_info().existing_damage; if (existing_damage.has_value() && !force_full_repaint) { - damage->SetPreviousLayerTree(last_layer_tree_.get()); + damage->SetPreviousLayerTree(GetLastLayerTree(view_id)); damage->AddAdditionalDamage(existing_damage.value()); damage->SetClipAlignment( frame->framebuffer_info().horizontal_clip_alignment, @@ -618,25 +730,17 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( ignore_raster_cache = false; } - RasterStatus raster_status = + RasterStatus frame_status = compositor_frame->Raster(layer_tree, // layer tree ignore_raster_cache, // ignore raster cache damage.get() // frame damage ); - if (raster_status == RasterStatus::kFailed || - raster_status == RasterStatus::kSkipAndRetry) { - return raster_status; + if (frame_status == RasterStatus::kSkipAndRetry) { + return DrawSurfaceStatus::kRetry; } SurfaceFrame::SubmitInfo submit_info; - // TODO (https://github.com/flutter/flutter/issues/105596): this can be in - // the past and might need to get snapped to future as this frame could - // have been resubmitted. `presentation_time` on `submit_info` is not set - // in this case. - const auto presentation_time = frame_timings_recorder.GetVsyncTargetTime(); - if (presentation_time > fml::TimePoint::Now()) { - submit_info.presentation_time = presentation_time; - } + submit_info.presentation_time = presentation_time; if (damage) { submit_info.frame_damage = damage->GetFrameDamage(); submit_info.buffer_damage = damage->GetBufferDamage(); @@ -655,22 +759,23 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // Do not update raster cache metrics for kResubmit because that status // indicates that the frame was not actually painted. - if (raster_status != RasterStatus::kResubmit) { + if (frame_status != RasterStatus::kResubmit) { compositor_context_->raster_cache().EndFrame(); } - frame_timings_recorder.RecordRasterEnd( - &compositor_context_->raster_cache()); - FireNextFrameCallbackIfPresent(); - - if (surface_->GetContext()) { - surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); + if (frame_status == RasterStatus::kResubmit) { + return DrawSurfaceStatus::kRetry; + } else { + FML_CHECK(frame_status == RasterStatus::kSuccess); + return DrawSurfaceStatus::kSuccess; } - - return raster_status; } - return RasterStatus::kFailed; + return DrawSurfaceStatus::kFailed; +} + +Rasterizer::ViewRecord& Rasterizer::EnsureViewRecord(int64_t view_id) { + return view_records_[view_id]; } static sk_sp ScreenshotLayerTreeAsPicture( @@ -760,7 +865,11 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { - auto* layer_tree = GetLastLayerTree(); + // TODO(dkwingsmt): Support screenshotting all last layer trees + // when the shell protocol supports multi-views. + // https://github.com/flutter/flutter/issues/135534 + // https://github.com/flutter/flutter/issues/135535 + auto* layer_tree = GetLastLayerTree(kFlutterImplicitViewId); if (layer_tree == nullptr) { FML_LOG(ERROR) << "Last layer tree was null when screenshotting."; return {}; diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index a54dd677a2fdb..e5acbaf8357a9 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -7,6 +7,7 @@ #include #include +#include #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" @@ -48,6 +49,54 @@ class AiksContext; namespace flutter { +// The result status of Rasterizer::Draw. This is only used for unit tests. +enum class DrawStatus { + // The drawing was done without any specified status. + kDone, + // Failed to rasterize the frame because the Rasterizer is not set up. + kNotSetUp, + // Nothing was done, because the call was not on the raster thread. Yielded to + // let this frame be serviced on the right thread. + kYielded, + // Nothing was done, because the pipeline was empty. + kPipelineEmpty, + // Nothing was done, because the GPU was unavailable. + kGpuUnavailable, +}; + +// The result status of drawing to a view. This is only used for unit tests. +enum class DrawSurfaceStatus { + // The layer tree was successfully rasterized. + kSuccess, + // The layer tree must be submitted again. + // + // This can occur on Android when switching the background surface to + // FlutterImageView. On Android, the first frame doesn't make the image + // available to the ImageReader right away. The second frame does. + // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 + // + // This can also occur when the frame is dropped to wait for the thread + // merger to merge the raster and platform threads. + kRetry, + // Failed to rasterize the frame. + kFailed, + // Layer tree was discarded because its size does not match the view size. + // This typically occurs during resizing. + kDiscarded, +}; + +// The information to draw to all views of a frame. +struct FrameItem { + FrameItem(std::vector> tasks, + std::unique_ptr frame_timings_recorder) + : layer_tree_tasks(std::move(tasks)), + frame_timings_recorder(std::move(frame_timings_recorder)) {} + std::vector> layer_tree_tasks; + std::unique_ptr frame_timings_recorder; +}; + +using FramePipeline = Pipeline; + //------------------------------------------------------------------------------ /// The rasterizer is a component owned by the shell that resides on the raster /// task runner. Each shell owns exactly one instance of a rasterizer. The @@ -180,6 +229,7 @@ class Rasterizer final : public SnapshotDelegate, /// collects associated resources. No more rendering may occur /// till the next call to `Rasterizer::Setup` with a new render /// surface. Calling a teardown without a setup is user error. + /// Calling this method multiple times is safe. /// void Teardown(); @@ -211,45 +261,46 @@ class Rasterizer final : public SnapshotDelegate, //---------------------------------------------------------------------------- /// @brief Deallocate the resources for displaying a view. /// - /// This method should be called when a view is removed. + /// This method must be called when a view is removed. /// /// The rasterizer don't need views to be registered. Last-frame /// states for views are recorded when layer trees are rasterized - /// to the view and used during `Rasterizer::DrawLastLayerTree`. + /// to the view and used during `Rasterizer::DrawLastLayerTrees`. /// /// @param[in] view_id The ID of the view. /// void CollectView(int64_t view_id); //---------------------------------------------------------------------------- - /// @brief Sometimes, it may be necessary to render the same frame again - /// without having to wait for the framework to build a whole new - /// layer tree describing the same contents. One such case is when - /// external textures (video or camera streams for example) are - /// updated in an otherwise static layer tree. To support this use - /// case, the rasterizer holds onto the last rendered layer tree. + /// @brief Returns the last successfully drawn layer tree for the given + /// view, or nullptr if there isn't any. This is useful during + /// `DrawLastLayerTrees` and computing frame damage. /// /// @bug https://github.com/flutter/flutter/issues/33939 /// /// @return A pointer to the last layer or `nullptr` if this rasterizer - /// has never rendered a frame. + /// has never rendered a frame to the given view. /// - flutter::LayerTree* GetLastLayerTree(); + flutter::LayerTree* GetLastLayerTree(int64_t view_id); //---------------------------------------------------------------------------- - /// @brief Draws a last layer tree to the render surface. This may seem - /// entirely redundant at first glance. After all, on surface loss - /// and re-acquisition, the framework generates a new layer tree. - /// Otherwise, why render the same contents to the screen again? - /// This is used as an optimization in cases where there are - /// external textures (video or camera streams for example) in - /// referenced in the layer tree. These textures may be updated at - /// a cadence different from that of the Flutter application. - /// Flutter can re-render the layer tree with just the updated - /// textures instead of waiting for the framework to do the work - /// to generate the layer tree describing the same contents. - /// - void DrawLastLayerTree( + /// @brief Draws the last layer trees with their last configuration. This + /// may seem entirely redundant at first glance. After all, on + /// surface loss and re-acquisition, the framework generates a new + /// layer tree. Otherwise, why render the same contents to the + /// screen again? This is used as an optimization in cases where + /// there are external textures (video or camera streams for + /// example) in referenced in the layer tree. These textures may + /// be updated at a cadence different from that of the Flutter + /// application. Flutter can re-render the layer tree with just + /// the updated textures instead of waiting for the framework to + /// do the work to generate the layer tree describing the same + /// contents. + /// + /// Calling this method clears all last layer trees + /// (GetLastLayerTree). + /// + void DrawLastLayerTrees( std::unique_ptr frame_timings_recorder); // |SnapshotDelegate| @@ -286,7 +337,7 @@ class Rasterizer final : public SnapshotDelegate, /// @param[in] pipeline The layer tree pipeline to take the next layer tree /// to render from. /// - RasterStatus Draw(const std::shared_ptr& pipeline); + DrawStatus Draw(const std::shared_ptr& pipeline); //---------------------------------------------------------------------------- /// @brief The type of the screenshot to obtain of the previously @@ -511,18 +562,54 @@ class Rasterizer final : public SnapshotDelegate, /// void DisableThreadMergerIfNeeded(); + //---------------------------------------------------------------------------- + /// @brief Returns whether TearDown has been called. + /// + /// This method is used only in unit tests. + /// + bool IsTornDown(); + + //---------------------------------------------------------------------------- + /// @brief Returns the last status of drawing the specific view. + /// + /// This method is used only in unit tests. + /// + std::optional GetLastDrawStatus(int64_t view_id); + private: - // The result of `DoDraw`. - // - // Normally `DoDraw` returns simply a raster status. However, sometimes we - // need to attempt to rasterize the layer tree again. This happens when - // layer_tree has not successfully rasterized due to changes in the thread - // configuration, in which case the resubmitted task will be inserted to the - // front of the pipeline. + // The result status of DoDraw, DrawToSurfaces, and DrawToSurfacesUnsafe. + enum class DoDrawStatus { + // The drawing was done without any specified status. + kDone, + // Frame has been successfully rasterized, but there are additional items + // in the pipeline waiting to be consumed. This is currently only used when + // thread configuration change occurs. + kEnqueuePipeline, + // Failed to rasterize the frame because the Rasterizer is not set up. + kNotSetUp, + // Nothing was done, because GPU was unavailable. + kGpuUnavailable, + }; + + // The result of DoDraw. struct DoDrawResult { - RasterStatus raster_status = RasterStatus::kFailed; + // The overall status of the drawing process. + // + // The status of drawing a specific view is available at GetLastDrawStatus. + DoDrawStatus status = DoDrawStatus::kDone; + + // The frame item that needs to be submitted again. + // + // See RasterStatus::kResubmit and kSkipAndRetry for when it happens. + // + // If `resubmitted_item` is not null, its `tasks` is guaranteed to be + // non-empty. + std::unique_ptr resubmitted_item; + }; - std::unique_ptr resubmitted_layer_tree_item; + struct ViewRecord { + std::unique_ptr last_successful_task; + std::optional last_draw_status; }; // |SnapshotDelegate| @@ -580,32 +667,58 @@ class Rasterizer final : public SnapshotDelegate, GrDirectContext* surface_context, bool compressed); + // This method starts with the frame timing recorder at build end. This + // method might push it to raster end and get the recorded time, or abort in + // the middle and not get the recorded time. DoDrawResult DoDraw( std::unique_ptr frame_timings_recorder, - std::unique_ptr layer_tree, - float device_pixel_ratio); + std::vector> tasks); - RasterStatus DrawToSurface(FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio); + // This method pushes the frame timing recorder from build end to raster end. + DoDrawResult DrawToSurfaces( + FrameTimingsRecorder& frame_timings_recorder, + std::vector> tasks); + + // Draws the specified layer trees to views, assuming we have access to the + // GPU. + // + // If any layer trees need resubmitting, this method returns the frame item to + // be resubmitted. Otherwise, it returns nullptr. + // + // Unsafe because it assumes we have access to the GPU which isn't the case + // when iOS is backgrounded, for example. + // + // This method pushes the frame timing recorder from build end to raster end. + std::unique_ptr DrawToSurfacesUnsafe( + FrameTimingsRecorder& frame_timings_recorder, + std::vector> tasks); + + // Draws the layer tree to the specified view, assuming we have access to the + // GPU. + // + // This method is not affiliated with the frame timing recorder, but must be + // included between the RasterStart and RasterEnd. + DrawSurfaceStatus DrawToSurfaceUnsafe( + int64_t view_id, + flutter::LayerTree& layer_tree, + float device_pixel_ratio, + std::optional presentation_time); - RasterStatus DrawToSurfaceUnsafe(FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree, - float device_pixel_ratio); + ViewRecord& EnsureViewRecord(int64_t view_id); void FireNextFrameCallbackIfPresent(); - static bool ShouldResubmitFrame(const RasterStatus& raster_status); + static bool ShouldResubmitFrame(const DoDrawResult& result); + static DrawStatus ToDrawStatus(DoDrawStatus status); + bool is_torn_down_; Delegate& delegate_; MakeGpuImageBehavior gpu_image_behavior_; std::weak_ptr impeller_context_; std::unique_ptr surface_; std::unique_ptr snapshot_surface_producer_; std::unique_ptr compositor_context_; - // This is the last successfully rasterized layer tree. - std::unique_ptr last_layer_tree_; - float last_device_pixel_ratio_; + std::unordered_map view_records_; fml::closure next_frame_callback_; bool user_override_resource_cache_bytes_; std::optional max_cache_bytes_; diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 545df3b6d3854..1e21d0d8f1ecb 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -32,6 +32,17 @@ namespace flutter { namespace { constexpr float kDevicePixelRatio = 2.0f; +constexpr int64_t kImplicitViewId = 0; + +std::vector> SingleLayerTreeList( + int64_t view_id, + std::unique_ptr layer_tree, + float pixel_ratio) { + std::vector> tasks; + tasks.push_back(std::make_unique( + view_id, std::move(layer_tree), pixel_ratio)); + return tasks; +} class MockDelegate : public Rasterizer::Delegate { public: @@ -152,7 +163,7 @@ TEST(RasterizerTest, drawEmptyPipeline) { rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); rasterizer->Draw(pipeline); latch.Signal(); @@ -214,13 +225,14 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -281,12 +293,13 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -353,11 +366,13 @@ TEST( rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -406,7 +421,7 @@ TEST(RasterizerTest, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); - // Prepare two frames for Draw() and DrawLastLayerTree(). + // Prepare two frames for Draw() and DrawLastLayerTrees(). EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame1)))) .WillOnce(Return(ByMove(std::move(surface_frame2)))); @@ -427,11 +442,13 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -441,9 +458,9 @@ TEST(RasterizerTest, ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); rasterizer->Draw(pipeline); - // The DrawLastLayerTree() will respectively call BeginFrame(), SubmitFrame() + // The DrawLastLayerTrees() will respectively call BeginFrame(), SubmitFrame() // and EndFrame() one more time, totally 2 times. - rasterizer->DrawLastLayerTree(CreateFinishedBuildRecorder()); + rasterizer->DrawLastLayerTrees(CreateFinishedBuildRecorder()); } TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { @@ -476,12 +493,13 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -507,6 +525,10 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings)); EXPECT_CALL(delegate, GetTaskRunners()) .WillRepeatedly(ReturnRef(task_runners)); + auto is_gpu_disabled_sync_switch = + std::make_shared(false); + ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) + .WillByDefault(Return(is_gpu_disabled_sync_switch)); auto rasterizer = std::make_unique(delegate); auto surface = std::make_unique>(); @@ -532,19 +554,22 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); // Always discard the layer tree. ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(true)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kDiscarded); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); + EXPECT_EQ(rasterizer->GetLastDrawStatus(kImplicitViewId), + DrawSurfaceStatus::kDiscarded); latch.Signal(); }); latch.Wait(); @@ -585,10 +610,10 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kFailed); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kPipelineEmpty); latch.Signal(); }); latch.Wait(); @@ -635,12 +660,13 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -693,18 +719,19 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kSuccess); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); latch.Signal(); }); latch.Wait(); @@ -751,18 +778,19 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kSuccess); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); latch.Signal(); }); latch.Wait(); @@ -808,18 +836,19 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kDiscarded); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kGpuUnavailable); latch.Signal(); }); latch.Wait(); @@ -864,18 +893,21 @@ TEST( rasterizer->Setup(std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); ON_CALL(delegate, ShouldDiscardLayerTree).WillByDefault(Return(false)); - RasterStatus status = rasterizer->Draw(pipeline); - EXPECT_EQ(status, RasterStatus::kFailed); + DrawStatus status = rasterizer->Draw(pipeline); + EXPECT_EQ(status, DrawStatus::kDone); + EXPECT_EQ(rasterizer->GetLastDrawStatus(kImplicitViewId), + DrawSurfaceStatus::kFailed); latch.Signal(); }); latch.Wait(); @@ -942,13 +974,14 @@ TEST(RasterizerTest, thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i]), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -1113,13 +1146,14 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i]), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -1196,12 +1230,13 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); - auto pipeline = std::make_shared(/*depth=*/10); + auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(first_timestamp), - kDevicePixelRatio); + auto layer_tree_item = std::make_unique( + SingleLayerTreeList(kImplicitViewId, std::move(layer_tree), + kDevicePixelRatio), + CreateFinishedBuildRecorder(first_timestamp)); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 82db9dea4ff64..44e5d2a65e110 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1220,16 +1220,16 @@ void Shell::OnAnimatorUpdateLatestFrameTargetTime( } // |Animator::Delegate| -void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { +void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { FML_DCHECK(is_set_up_); task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( [&waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), - weak_pipeline = std::weak_ptr(pipeline)]() mutable { + weak_pipeline = std::weak_ptr(pipeline)]() mutable { if (rasterizer) { - std::shared_ptr pipeline = weak_pipeline.lock(); + std::shared_ptr pipeline = weak_pipeline.lock(); if (pipeline) { rasterizer->Draw(pipeline); } @@ -1243,7 +1243,7 @@ void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { } // |Animator::Delegate| -void Shell::OnAnimatorDrawLastLayerTree( +void Shell::OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { FML_DCHECK(is_set_up_); @@ -1251,7 +1251,7 @@ void Shell::OnAnimatorDrawLastLayerTree( [rasterizer = rasterizer_->GetWeakPtr(), frame_timings_recorder = std::move(frame_timings_recorder)]() mutable { if (rasterizer) { - rasterizer->DrawLastLayerTree(std::move(frame_timings_recorder)); + rasterizer->DrawLastLayerTrees(std::move(frame_timings_recorder)); } }); @@ -1971,7 +1971,8 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( // TODO(dkwingsmt): This method only handles view #0, including the snapshot // and the frame size. We need to adapt this method to multi-view. // https://github.com/flutter/flutter/issues/131892 - if (auto last_layer_tree = rasterizer_->GetLastLayerTree()) { + int64_t view_id = kFlutterImplicitViewId; + if (auto last_layer_tree = rasterizer_->GetLastLayerTree(view_id)) { auto& allocator = response->GetAllocator(); response->SetObject(); response->AddMember("type", "RenderFrameWithRasterStats", allocator); @@ -1986,7 +1987,7 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( frame_timings_recorder->RecordBuildEnd(now); last_layer_tree->enable_leaf_layer_tracing(true); - rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder)); + rasterizer_->DrawLastLayerTrees(std::move(frame_timings_recorder)); last_layer_tree->enable_leaf_layer_tracing(false); rapidjson::Value snapshots; @@ -2002,7 +2003,7 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( response->AddMember("snapshots", snapshots, allocator); - const auto& frame_size = ExpectedFrameSize(kFlutterImplicitViewId); + const auto& frame_size = ExpectedFrameSize(view_id); response->AddMember("frame_width", frame_size.width(), allocator); response->AddMember("frame_height", frame_size.height(), allocator); diff --git a/shell/common/shell.h b/shell/common/shell.h index d3bb7f0bd620c..133fecc4d0f43 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -650,10 +650,10 @@ class Shell final : public PlatformView::Delegate, fml::TimePoint frame_target_time) override; // |Animator::Delegate| - void OnAnimatorDraw(std::shared_ptr pipeline) override; + void OnAnimatorDraw(std::shared_ptr pipeline) override; // |Animator::Delegate| - void OnAnimatorDrawLastLayerTree( + void OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) override; // |Engine::Delegate| diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2371fa9113aa7..f899d5c5ede97 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -310,30 +310,26 @@ static bool ValidateShell(Shell* shell) { return true; } -static bool RasterizerHasLayerTree(Shell* shell) { +static bool RasterizerIsTornDown(Shell* shell) { fml::AutoResetWaitableEvent latch; - bool has_layer_tree = false; + bool is_torn_down = false; fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetRasterTaskRunner(), - [shell, &latch, &has_layer_tree]() { - has_layer_tree = shell->GetRasterizer()->GetLastLayerTree() != nullptr; + [shell, &latch, &is_torn_down]() { + is_torn_down = shell->GetRasterizer()->IsTornDown(); latch.Signal(); }); latch.Wait(); - return has_layer_tree; + return is_torn_down; } static void ValidateDestroyPlatformView(Shell* shell) { ASSERT_TRUE(shell != nullptr); ASSERT_TRUE(shell->IsSetup()); - // To validate destroy platform view, we must ensure the rasterizer has a - // layer tree before the platform view is destroyed. - ASSERT_TRUE(RasterizerHasLayerTree(shell)); - + ASSERT_FALSE(RasterizerIsTornDown(shell)); ShellTest::PlatformViewNotifyDestroyed(shell); - // Validate the layer tree is destroyed - ASSERT_FALSE(RasterizerHasLayerTree(shell)); + ASSERT_TRUE(RasterizerIsTornDown(shell)); } static std::string CreateFlagsString(std::vector& flags) {