diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1418af878d6d9..dc374fe6e5d77 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -454,6 +454,7 @@ FILE: ../../../flutter/shell/common/persistent_cache.cc FILE: ../../../flutter/shell/common/persistent_cache.h FILE: ../../../flutter/shell/common/pipeline.cc FILE: ../../../flutter/shell/common/pipeline.h +FILE: ../../../flutter/shell/common/pipeline_unittests.cc FILE: ../../../flutter/shell/common/platform_view.cc FILE: ../../../flutter/shell/common/platform_view.h FILE: ../../../flutter/shell/common/rasterizer.cc diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index a64821580df12..e8375080feae3 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -152,6 +152,7 @@ test_fixtures("shell_unittests_fixtures") { shell_host_executable("shell_unittests") { sources = [ + "pipeline_unittests.cc", "shell_test.cc", "shell_test.h", "shell_unittests.cc", diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index 1cf5877353e8a..d54962814a7e4 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -10,9 +10,9 @@ #include "flutter/fml/synchronization/semaphore.h" #include "flutter/fml/trace_event.h" +#include #include #include -#include namespace flutter { @@ -88,7 +88,8 @@ class Pipeline : public fml::RefCountedThreadSafe> { FML_DISALLOW_COPY_AND_ASSIGN(ProducerContinuation); }; - explicit Pipeline(uint32_t depth) : empty_(depth), available_(0) {} + explicit Pipeline(uint32_t depth) + : depth_(depth), empty_(depth), available_(0) {} ~Pipeline() = default; @@ -105,6 +106,20 @@ class Pipeline : public fml::RefCountedThreadSafe> { GetNextPipelineTraceID()}; // trace id } + // Pushes task to the front of the pipeline. + // + // If we exceed the depth completing this continuation, we drop the + // last frame to preserve the depth of the pipeline. + // + // Note: Use |Pipeline::Produce| where possible. This should only be + // used to en-queue high-priority resources. + ProducerContinuation ProduceToFront() { + return ProducerContinuation{ + std::bind(&Pipeline::ProducerCommitFront, this, std::placeholders::_1, + std::placeholders::_2), // continuation + GetNextPipelineTraceID()}; // trace id + } + using Consumer = std::function; FML_WARN_UNUSED_RESULT @@ -124,7 +139,7 @@ class Pipeline : public fml::RefCountedThreadSafe> { { std::scoped_lock lock(queue_mutex_); std::tie(resource, trace_id) = std::move(queue_.front()); - queue_.pop(); + queue_.pop_front(); items_count = queue_.size(); } @@ -143,15 +158,29 @@ class Pipeline : public fml::RefCountedThreadSafe> { } private: + uint32_t depth_; fml::Semaphore empty_; fml::Semaphore available_; std::mutex queue_mutex_; - std::queue> queue_; + std::deque> queue_; void ProducerCommit(ResourcePtr resource, size_t trace_id) { { std::scoped_lock lock(queue_mutex_); - queue_.emplace(std::move(resource), trace_id); + queue_.emplace_back(std::move(resource), trace_id); + } + + // Ensure the queue mutex is not held as that would be a pessimization. + available_.Signal(); + } + + void ProducerCommitFront(ResourcePtr resource, size_t trace_id) { + { + std::scoped_lock lock(queue_mutex_); + queue_.emplace_front(std::move(resource), trace_id); + while (queue_.size() > depth_) { + queue_.pop_back(); + } } // Ensure the queue mutex is not held as that would be a pessimization. diff --git a/shell/common/pipeline_unittests.cc b/shell/common/pipeline_unittests.cc new file mode 100644 index 0000000000000..ada908953b45a --- /dev/null +++ b/shell/common/pipeline_unittests.cc @@ -0,0 +1,135 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define FML_USED_ON_EMBEDDER + +#include +#include +#include + +#include "flutter/shell/common/pipeline.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +using IntPipeline = Pipeline; +using Continuation = IntPipeline::ProducerContinuation; + +TEST(PipelineTest, ConsumeOneVal) { + fml::RefPtr pipeline = fml::MakeRefCounted(2); + + Continuation continuation = pipeline->Produce(); + + const int test_val = 1; + continuation.Complete(std::make_unique(test_val)); + + PipelineConsumeResult consume_result = pipeline->Consume( + [&test_val](std::unique_ptr v) { ASSERT_EQ(*v, test_val); }); + + ASSERT_EQ(consume_result, PipelineConsumeResult::Done); +} + +TEST(PipelineTest, ContinuationCanOnlyBeUsedOnce) { + fml::RefPtr pipeline = fml::MakeRefCounted(2); + + Continuation continuation = pipeline->Produce(); + + const int test_val = 1; + continuation.Complete(std::make_unique(test_val)); + + PipelineConsumeResult consume_result_1 = pipeline->Consume( + [&test_val](std::unique_ptr v) { ASSERT_EQ(*v, test_val); }); + + continuation.Complete(std::make_unique(test_val)); + ASSERT_EQ(consume_result_1, PipelineConsumeResult::Done); + + PipelineConsumeResult consume_result_2 = + pipeline->Consume([](std::unique_ptr v) { FAIL(); }); + + continuation.Complete(std::make_unique(test_val)); + ASSERT_EQ(consume_result_2, PipelineConsumeResult::NoneAvailable); +} + +TEST(PipelineTest, PushingMoreThanDepthCompletesFirstSubmission) { + const int depth = 1; + fml::RefPtr pipeline = fml::MakeRefCounted(depth); + + Continuation continuation_1 = pipeline->Produce(); + Continuation continuation_2 = pipeline->Produce(); + + const int test_val_1 = 1, test_val_2 = 2; + continuation_1.Complete(std::make_unique(test_val_1)); + continuation_2.Complete(std::make_unique(test_val_2)); + + PipelineConsumeResult consume_result_1 = pipeline->Consume( + [&test_val_1](std::unique_ptr v) { ASSERT_EQ(*v, test_val_1); }); + + ASSERT_EQ(consume_result_1, PipelineConsumeResult::Done); +} + +TEST(PipelineTest, PushingMultiProcessesInOrder) { + const int depth = 2; + fml::RefPtr pipeline = fml::MakeRefCounted(depth); + + Continuation continuation_1 = pipeline->Produce(); + Continuation continuation_2 = pipeline->Produce(); + + const int test_val_1 = 1, test_val_2 = 2; + continuation_1.Complete(std::make_unique(test_val_1)); + continuation_2.Complete(std::make_unique(test_val_2)); + + PipelineConsumeResult consume_result_1 = pipeline->Consume( + [&test_val_1](std::unique_ptr v) { ASSERT_EQ(*v, test_val_1); }); + ASSERT_EQ(consume_result_1, PipelineConsumeResult::MoreAvailable); + + PipelineConsumeResult consume_result_2 = pipeline->Consume( + [&test_val_2](std::unique_ptr v) { ASSERT_EQ(*v, test_val_2); }); + ASSERT_EQ(consume_result_2, PipelineConsumeResult::Done); +} + +TEST(PipelineTest, PushingToFrontOverridesOrder) { + const int depth = 2; + fml::RefPtr pipeline = fml::MakeRefCounted(depth); + + Continuation continuation_1 = pipeline->Produce(); + Continuation continuation_2 = pipeline->ProduceToFront(); + + const int test_val_1 = 1, test_val_2 = 2; + continuation_1.Complete(std::make_unique(test_val_1)); + continuation_2.Complete(std::make_unique(test_val_2)); + + PipelineConsumeResult consume_result_1 = pipeline->Consume( + [&test_val_2](std::unique_ptr v) { ASSERT_EQ(*v, test_val_2); }); + ASSERT_EQ(consume_result_1, PipelineConsumeResult::MoreAvailable); + + PipelineConsumeResult consume_result_2 = pipeline->Consume( + [&test_val_1](std::unique_ptr v) { ASSERT_EQ(*v, test_val_1); }); + ASSERT_EQ(consume_result_2, PipelineConsumeResult::Done); +} + +TEST(PipelineTest, PushingToFrontDropsLastResource) { + const int depth = 2; + fml::RefPtr pipeline = fml::MakeRefCounted(depth); + + Continuation continuation_1 = pipeline->Produce(); + Continuation continuation_2 = pipeline->Produce(); + Continuation continuation_3 = pipeline->ProduceToFront(); + + const int test_val_1 = 1, test_val_2 = 2, test_val_3 = 3; + continuation_1.Complete(std::make_unique(test_val_1)); + continuation_2.Complete(std::make_unique(test_val_2)); + continuation_3.Complete(std::make_unique(test_val_3)); + + PipelineConsumeResult consume_result_1 = pipeline->Consume( + [&test_val_3](std::unique_ptr v) { ASSERT_EQ(*v, test_val_3); }); + ASSERT_EQ(consume_result_1, PipelineConsumeResult::MoreAvailable); + + PipelineConsumeResult consume_result_2 = pipeline->Consume( + [&test_val_1](std::unique_ptr v) { ASSERT_EQ(*v, test_val_1); }); + ASSERT_EQ(consume_result_2, PipelineConsumeResult::Done); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index c7047cfc22901..b8f35edf28f6a 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -173,6 +173,8 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, } void Rasterizer::DoDraw(std::unique_ptr layer_tree) { + FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + if (!layer_tree || !surface_) { return; }