Skip to content

Commit

Permalink
Add ability to disable the raster thread merger (flutter#20800)
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Garcia authored Aug 29, 2020
1 parent 8c5d9f6 commit 1b0c684
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 73 deletions.
44 changes: 35 additions & 9 deletions fml/raster_thread_merger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ RasterThreadMerger::RasterThreadMerger(fml::TaskQueueId platform_queue_id,
: platform_queue_id_(platform_queue_id),
gpu_queue_id_(gpu_queue_id),
task_queues_(fml::MessageLoopTaskQueues::GetInstance()),
lease_term_(kLeaseNotSet) {
lease_term_(kLeaseNotSet),
enabled_(true) {
FML_CHECK(!task_queues_->Owns(platform_queue_id_, gpu_queue_id_));
}

void RasterThreadMerger::MergeWithLease(size_t lease_term) {
std::scoped_lock lock(lease_term_mutex_);
if (TaskQueuesAreSame()) {
return;
}

if (!IsEnabledUnSafe()) {
return;
}
FML_DCHECK(lease_term > 0) << "lease_term should be positive.";
std::scoped_lock lock(lease_term_mutex_);
if (!IsMergedUnSafe()) {
bool success = task_queues_->Merge(platform_queue_id_, gpu_queue_id_);
FML_CHECK(success) << "Unable to merge the raster and platform threads.";
Expand All @@ -36,11 +39,13 @@ void RasterThreadMerger::MergeWithLease(size_t lease_term) {
}

void RasterThreadMerger::UnMergeNow() {
std::scoped_lock lock(lease_term_mutex_);
if (TaskQueuesAreSame()) {
return;
}

std::scoped_lock lock(lease_term_mutex_);
if (!IsEnabledUnSafe()) {
return;
}
lease_term_ = 0;
bool success = task_queues_->Unmerge(platform_queue_id_);
FML_CHECK(success) << "Unable to un-merge the raster and platform threads.";
Expand All @@ -50,7 +55,7 @@ bool RasterThreadMerger::IsOnPlatformThread() const {
return MessageLoop::GetCurrentTaskQueueId() == platform_queue_id_;
}

bool RasterThreadMerger::IsOnRasterizingThread() {
bool RasterThreadMerger::IsOnRasterizingThread() const {
if (IsMergedUnSafe()) {
return IsOnPlatformThread();
} else {
Expand All @@ -75,11 +80,30 @@ bool RasterThreadMerger::IsMerged() {
return IsMergedUnSafe();
}

bool RasterThreadMerger::IsMergedUnSafe() {
void RasterThreadMerger::Enable() {
std::scoped_lock lock(lease_term_mutex_);
enabled_ = true;
}

void RasterThreadMerger::Disable() {
std::scoped_lock lock(lease_term_mutex_);
enabled_ = false;
}

bool RasterThreadMerger::IsEnabled() {
std::scoped_lock lock(lease_term_mutex_);
return IsEnabledUnSafe();
}

bool RasterThreadMerger::IsEnabledUnSafe() const {
return enabled_;
}

bool RasterThreadMerger::IsMergedUnSafe() const {
return lease_term_ > 0 || TaskQueuesAreSame();
}

bool RasterThreadMerger::TaskQueuesAreSame() {
bool RasterThreadMerger::TaskQueuesAreSame() const {
return platform_queue_id_ == gpu_queue_id_;
}

Expand All @@ -100,7 +124,9 @@ RasterThreadStatus RasterThreadMerger::DecrementLease() {
if (!IsMergedUnSafe()) {
return RasterThreadStatus::kRemainsUnmerged;
}

if (!IsEnabledUnSafe()) {
return RasterThreadStatus::kRemainsMerged;
}
FML_DCHECK(lease_term_ > 0)
<< "lease_term should always be positive when merged.";
lease_term_--;
Expand Down
22 changes: 19 additions & 3 deletions fml/raster_thread_merger.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,23 @@ class RasterThreadMerger
// Returns true if the current thread owns rasterizing.
// When the threads are merged, platform thread owns rasterizing.
// When un-merged, raster thread owns rasterizing.
bool IsOnRasterizingThread();
bool IsOnRasterizingThread() const;

// Returns true if the current thread is the platform thread.
bool IsOnPlatformThread() const;

// Enables the thread merger.
void Enable();

// Disables the thread merger. Once disabled, any call to
// |MergeWithLease| or |UnMergeNow| results in a noop.
void Disable();

// Whether the thread merger is enabled. By default, the thread merger is
// enabled. If false, calls to |MergeWithLease| or |UnMergeNow| results in a
// noop.
bool IsEnabled();

private:
static const int kLeaseNotSet;
fml::TaskQueueId platform_queue_id_;
Expand All @@ -81,11 +93,15 @@ class RasterThreadMerger
std::atomic_int lease_term_;
std::condition_variable merged_condition_;
std::mutex lease_term_mutex_;
bool enabled_;

bool IsMergedUnSafe() const;

bool IsEnabledUnSafe() const;

bool IsMergedUnSafe();
// The platform_queue_id and gpu_queue_id are exactly the same.
// We consider the threads are always merged and cannot be unmerged.
bool TaskQueuesAreSame();
bool TaskQueuesAreSame() const;

FML_FRIEND_REF_COUNTED_THREAD_SAFE(RasterThreadMerger);
FML_FRIEND_MAKE_REF_COUNTED(RasterThreadMerger);
Expand Down
161 changes: 161 additions & 0 deletions fml/raster_thread_merger_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,167 @@ TEST(RasterThreadMerger, HandleTaskQueuesAreTheSame) {
thread1.join();
}

TEST(RasterThreadMerger, Enable) {
fml::MessageLoop* loop1 = nullptr;
fml::AutoResetWaitableEvent latch1;
fml::AutoResetWaitableEvent term1;
std::thread thread1([&loop1, &latch1, &term1]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop1 = &fml::MessageLoop::GetCurrent();
latch1.Signal();
term1.Wait();
});

fml::MessageLoop* loop2 = nullptr;
fml::AutoResetWaitableEvent latch2;
fml::AutoResetWaitableEvent term2;
std::thread thread2([&loop2, &latch2, &term2]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop2 = &fml::MessageLoop::GetCurrent();
latch2.Signal();
term2.Wait();
});

latch1.Wait();
latch2.Wait();

fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
const auto raster_thread_merger_ =
fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);

raster_thread_merger_->Disable();
raster_thread_merger_->MergeWithLease(1);
ASSERT_FALSE(raster_thread_merger_->IsMerged());

raster_thread_merger_->Enable();
ASSERT_FALSE(raster_thread_merger_->IsMerged());

raster_thread_merger_->MergeWithLease(1);
ASSERT_TRUE(raster_thread_merger_->IsMerged());

raster_thread_merger_->DecrementLease();
ASSERT_FALSE(raster_thread_merger_->IsMerged());

term1.Signal();
term2.Signal();
thread1.join();
thread2.join();
}

TEST(RasterThreadMerger, Disable) {
fml::MessageLoop* loop1 = nullptr;
fml::AutoResetWaitableEvent latch1;
fml::AutoResetWaitableEvent term1;
std::thread thread1([&loop1, &latch1, &term1]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop1 = &fml::MessageLoop::GetCurrent();
latch1.Signal();
term1.Wait();
});

fml::MessageLoop* loop2 = nullptr;
fml::AutoResetWaitableEvent latch2;
fml::AutoResetWaitableEvent term2;
std::thread thread2([&loop2, &latch2, &term2]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop2 = &fml::MessageLoop::GetCurrent();
latch2.Signal();
term2.Wait();
});

latch1.Wait();
latch2.Wait();

fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
const auto raster_thread_merger_ =
fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);

raster_thread_merger_->Disable();
ASSERT_FALSE(raster_thread_merger_->IsMerged());

raster_thread_merger_->MergeWithLease(1);
ASSERT_FALSE(raster_thread_merger_->IsMerged());

raster_thread_merger_->Enable();
raster_thread_merger_->MergeWithLease(1);
ASSERT_TRUE(raster_thread_merger_->IsMerged());

raster_thread_merger_->Disable();
raster_thread_merger_->UnMergeNow();
ASSERT_TRUE(raster_thread_merger_->IsMerged());

{
auto decrement_result = raster_thread_merger_->DecrementLease();
ASSERT_EQ(fml::RasterThreadStatus::kRemainsMerged, decrement_result);
}

ASSERT_TRUE(raster_thread_merger_->IsMerged());

raster_thread_merger_->Enable();
raster_thread_merger_->UnMergeNow();
ASSERT_FALSE(raster_thread_merger_->IsMerged());

raster_thread_merger_->MergeWithLease(1);

ASSERT_TRUE(raster_thread_merger_->IsMerged());

{
auto decrement_result = raster_thread_merger_->DecrementLease();
ASSERT_EQ(fml::RasterThreadStatus::kUnmergedNow, decrement_result);
}

ASSERT_FALSE(raster_thread_merger_->IsMerged());

term1.Signal();
term2.Signal();
thread1.join();
thread2.join();
}

TEST(RasterThreadMerger, IsEnabled) {
fml::MessageLoop* loop1 = nullptr;
fml::AutoResetWaitableEvent latch1;
fml::AutoResetWaitableEvent term1;
std::thread thread1([&loop1, &latch1, &term1]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop1 = &fml::MessageLoop::GetCurrent();
latch1.Signal();
term1.Wait();
});

fml::MessageLoop* loop2 = nullptr;
fml::AutoResetWaitableEvent latch2;
fml::AutoResetWaitableEvent term2;
std::thread thread2([&loop2, &latch2, &term2]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop2 = &fml::MessageLoop::GetCurrent();
latch2.Signal();
term2.Wait();
});

latch1.Wait();
latch2.Wait();

fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
const auto raster_thread_merger_ =
fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
ASSERT_TRUE(raster_thread_merger_->IsEnabled());

raster_thread_merger_->Disable();
ASSERT_FALSE(raster_thread_merger_->IsEnabled());

raster_thread_merger_->Enable();
ASSERT_TRUE(raster_thread_merger_->IsEnabled());

term1.Signal();
term2.Signal();
thread1.join();
thread2.join();
}

TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskMergesThreads) {
fml::MessageLoop* loop_platform = nullptr;
fml::AutoResetWaitableEvent latch1;
Expand Down
31 changes: 15 additions & 16 deletions shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
// TODO(sanjayc77): https://github.com/flutter/flutter/issues/53179. Add
// support for raster thread merger for Fuchsia.
if (surface_->GetExternalViewEmbedder() &&
surface_->GetExternalViewEmbedder()->SupportsDynamicThreadMerging()) {
surface_->GetExternalViewEmbedder()->SupportsDynamicThreadMerging() &&
!raster_thread_merger_) {
const auto platform_id =
delegate_.GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId();
const auto gpu_id =
Expand All @@ -99,10 +100,23 @@ void Rasterizer::Teardown() {

if (raster_thread_merger_.get() != nullptr &&
raster_thread_merger_.get()->IsMerged()) {
FML_DCHECK(raster_thread_merger_->IsEnabled());
raster_thread_merger_->UnMergeNow();
}
}

void Rasterizer::EnableThreadMergerIfNeeded() {
if (raster_thread_merger_) {
raster_thread_merger_->Enable();
}
}

void Rasterizer::DisableThreadMergerIfNeeded() {
if (raster_thread_merger_) {
raster_thread_merger_->Disable();
}
}

void Rasterizer::NotifyLowMemoryWarning() const {
if (!surface_) {
FML_DLOG(INFO)
Expand Down Expand Up @@ -668,21 +682,6 @@ std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
return std::nullopt;
}

void Rasterizer::EnsureThreadsAreMerged() {
if (surface_ == nullptr || raster_thread_merger_.get() == nullptr) {
return;
}
const size_t ThreadMergeLeaseTermDefault = 10;
fml::TaskRunner::RunNowOrPostTask(
delegate_.GetTaskRunners().GetRasterTaskRunner(),
[weak_this = weak_factory_.GetWeakPtr(),
thread_merger = raster_thread_merger_]() {
thread_merger->MergeWithLease(ThreadMergeLeaseTermDefault);
});
raster_thread_merger_->WaitUntilMerged();
FML_DCHECK(raster_thread_merger_->IsMerged());
}

Rasterizer::Screenshot::Screenshot() {}

Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data, SkISize p_size)
Expand Down
27 changes: 20 additions & 7 deletions shell/common/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,15 +392,28 @@ class Rasterizer final : public SnapshotDelegate {
std::optional<size_t> GetResourceCacheMaxBytes() const;

//----------------------------------------------------------------------------
/// @brief Makes sure the raster task runner and the platform task runner
/// are merged.
/// @brief Enables the thread merger if the external view embedder
/// supports dynamic thread merging.
///
/// @attention If raster and platform task runners are not the same or not
/// merged, this method will try to merge the task runners,
/// blocking the current thread until the 2 task runners are
/// merged.
/// @attention This method is thread-safe. When the thread merger is enabled,
/// the raster task queue can run in the platform thread at any
/// time.
///
void EnsureThreadsAreMerged();
/// @see `ExternalViewEmbedder`
///
void EnableThreadMergerIfNeeded();

//----------------------------------------------------------------------------
/// @brief Disables the thread merger if the external view embedder
/// supports dynamic thread merging.
///
/// @attention This method is thread-safe. When the thread merger is
/// disabled, the raster task queue will continue to run in the
/// same thread until |EnableThreadMergerIfNeeded| is called.
///
/// @see `ExternalViewEmbedder`
///
void DisableThreadMergerIfNeeded();

private:
Delegate& delegate_;
Expand Down
Loading

0 comments on commit 1b0c684

Please sign in to comment.