flutter_flutter/fml/gpu_thread_merger_unittests.cc
Kaushik Iska 971a639151
Allow for dynamic thread merging on IOS for embedded view mutations (#9819)
After pre-roll we know if there have been any mutations made to the IOS embedded UIViews. If there are any mutations and the thread configuration is such chat the mutations will be committed on an illegal thread (GPU thread), we merge the threads and keep them merged until the lease expires. The lease is currently set to expire after 10 frames of no mutations. If there are any mutations in the interim we extend the lease.

TaskRunnerMerger will ultimately be responsible for enforcing the correct thread configurations.

This configuration will be inactive even after this change since still use the same thread when we create the iOS engine. That is slated to change in the coming PRs.
2019-08-12 12:32:38 -07:00

205 lines
6.0 KiB
C++

// 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 <atomic>
#include <thread>
#include "flutter/fml/gpu_thread_merger.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/synchronization/count_down_latch.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/task_runner.h"
#include "gtest/gtest.h"
TEST(GpuThreadMerger, RemainMergedTillLeaseExpires) {
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 gpu_thread_merger_ =
fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
const int kNumFramesMerged = 5;
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
gpu_thread_merger_->MergeWithLease(kNumFramesMerged);
for (int i = 0; i < kNumFramesMerged; i++) {
ASSERT_TRUE(gpu_thread_merger_->IsMerged());
gpu_thread_merger_->DecrementLease();
}
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
term1.Signal();
term2.Signal();
thread1.join();
thread2.join();
}
TEST(GpuThreadMerger, IsNotOnRasterizingThread) {
fml::MessageLoop* loop1 = nullptr;
fml::AutoResetWaitableEvent latch1;
std::thread thread1([&loop1, &latch1]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop1 = &fml::MessageLoop::GetCurrent();
loop1->GetTaskRunner()->PostTask([&]() { latch1.Signal(); });
loop1->Run();
});
fml::MessageLoop* loop2 = nullptr;
fml::AutoResetWaitableEvent latch2;
std::thread thread2([&loop2, &latch2]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop2 = &fml::MessageLoop::GetCurrent();
loop2->GetTaskRunner()->PostTask([&]() { latch2.Signal(); });
loop2->Run();
});
latch1.Wait();
latch2.Wait();
fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
const auto gpu_thread_merger_ =
fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2);
loop1->GetTaskRunner()->PostTask([&]() {
ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
pre_merge.CountDown();
});
loop2->GetTaskRunner()->PostTask([&]() {
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
pre_merge.CountDown();
});
pre_merge.Wait();
gpu_thread_merger_->MergeWithLease(1);
loop1->GetTaskRunner()->PostTask([&]() {
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
post_merge.CountDown();
});
loop2->GetTaskRunner()->PostTask([&]() {
// this will be false since this is going to be run
// on loop1 really.
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
post_merge.CountDown();
});
post_merge.Wait();
gpu_thread_merger_->DecrementLease();
loop1->GetTaskRunner()->PostTask([&]() {
ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
post_unmerge.CountDown();
});
loop2->GetTaskRunner()->PostTask([&]() {
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
post_unmerge.CountDown();
});
post_unmerge.Wait();
loop1->GetTaskRunner()->PostTask([&]() { loop1->Terminate(); });
loop2->GetTaskRunner()->PostTask([&]() { loop2->Terminate(); });
thread1.join();
thread2.join();
}
TEST(GpuThreadMerger, LeaseExtension) {
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 gpu_thread_merger_ =
fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
const int kNumFramesMerged = 5;
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
gpu_thread_merger_->MergeWithLease(kNumFramesMerged);
// let there be one more turn till the leases expire.
for (int i = 0; i < kNumFramesMerged - 1; i++) {
ASSERT_TRUE(gpu_thread_merger_->IsMerged());
gpu_thread_merger_->DecrementLease();
}
// extend the lease once.
gpu_thread_merger_->ExtendLeaseTo(kNumFramesMerged);
// we will NOT last for 1 extra turn, we just set it.
for (int i = 0; i < kNumFramesMerged; i++) {
ASSERT_TRUE(gpu_thread_merger_->IsMerged());
gpu_thread_merger_->DecrementLease();
}
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
term1.Signal();
term2.Signal();
thread1.join();
thread2.join();
}