1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #define FML_USED_ON_EMBEDDER
6
7 #include <atomic>
8 #include <thread>
9
10 #include "flutter/fml/gpu_thread_merger.h"
11 #include "flutter/fml/message_loop.h"
12 #include "flutter/fml/synchronization/count_down_latch.h"
13 #include "flutter/fml/synchronization/waitable_event.h"
14 #include "flutter/fml/task_runner.h"
15 #include "gtest/gtest.h"
16
TEST(GpuThreadMerger,RemainMergedTillLeaseExpires)17 TEST(GpuThreadMerger, RemainMergedTillLeaseExpires) {
18 fml::MessageLoop* loop1 = nullptr;
19 fml::AutoResetWaitableEvent latch1;
20 fml::AutoResetWaitableEvent term1;
21 std::thread thread1([&loop1, &latch1, &term1]() {
22 fml::MessageLoop::EnsureInitializedForCurrentThread();
23 loop1 = &fml::MessageLoop::GetCurrent();
24 latch1.Signal();
25 term1.Wait();
26 });
27
28 fml::MessageLoop* loop2 = nullptr;
29 fml::AutoResetWaitableEvent latch2;
30 fml::AutoResetWaitableEvent term2;
31 std::thread thread2([&loop2, &latch2, &term2]() {
32 fml::MessageLoop::EnsureInitializedForCurrentThread();
33 loop2 = &fml::MessageLoop::GetCurrent();
34 latch2.Signal();
35 term2.Wait();
36 });
37
38 latch1.Wait();
39 latch2.Wait();
40
41 fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
42 fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
43 const auto gpu_thread_merger_ =
44 fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
45 const int kNumFramesMerged = 5;
46
47 ASSERT_FALSE(gpu_thread_merger_->IsMerged());
48
49 gpu_thread_merger_->MergeWithLease(kNumFramesMerged);
50
51 for (int i = 0; i < kNumFramesMerged; i++) {
52 ASSERT_TRUE(gpu_thread_merger_->IsMerged());
53 gpu_thread_merger_->DecrementLease();
54 }
55
56 ASSERT_FALSE(gpu_thread_merger_->IsMerged());
57
58 term1.Signal();
59 term2.Signal();
60 thread1.join();
61 thread2.join();
62 }
63
TEST(GpuThreadMerger,IsNotOnRasterizingThread)64 TEST(GpuThreadMerger, IsNotOnRasterizingThread) {
65 fml::MessageLoop* loop1 = nullptr;
66 fml::AutoResetWaitableEvent latch1;
67 std::thread thread1([&loop1, &latch1]() {
68 fml::MessageLoop::EnsureInitializedForCurrentThread();
69 loop1 = &fml::MessageLoop::GetCurrent();
70 loop1->GetTaskRunner()->PostTask([&]() { latch1.Signal(); });
71 loop1->Run();
72 });
73
74 fml::MessageLoop* loop2 = nullptr;
75 fml::AutoResetWaitableEvent latch2;
76 std::thread thread2([&loop2, &latch2]() {
77 fml::MessageLoop::EnsureInitializedForCurrentThread();
78 loop2 = &fml::MessageLoop::GetCurrent();
79 loop2->GetTaskRunner()->PostTask([&]() { latch2.Signal(); });
80 loop2->Run();
81 });
82
83 latch1.Wait();
84 latch2.Wait();
85
86 fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
87 fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
88 const auto gpu_thread_merger_ =
89 fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
90
91 fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2);
92
93 loop1->GetTaskRunner()->PostTask([&]() {
94 ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread());
95 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
96 pre_merge.CountDown();
97 });
98
99 loop2->GetTaskRunner()->PostTask([&]() {
100 ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
101 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
102 pre_merge.CountDown();
103 });
104
105 pre_merge.Wait();
106
107 gpu_thread_merger_->MergeWithLease(1);
108
109 loop1->GetTaskRunner()->PostTask([&]() {
110 ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
111 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
112 post_merge.CountDown();
113 });
114
115 loop2->GetTaskRunner()->PostTask([&]() {
116 // this will be false since this is going to be run
117 // on loop1 really.
118 ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
119 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
120 post_merge.CountDown();
121 });
122
123 post_merge.Wait();
124
125 gpu_thread_merger_->DecrementLease();
126
127 loop1->GetTaskRunner()->PostTask([&]() {
128 ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread());
129 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
130 post_unmerge.CountDown();
131 });
132
133 loop2->GetTaskRunner()->PostTask([&]() {
134 ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
135 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
136 post_unmerge.CountDown();
137 });
138
139 post_unmerge.Wait();
140
141 loop1->GetTaskRunner()->PostTask([&]() { loop1->Terminate(); });
142
143 loop2->GetTaskRunner()->PostTask([&]() { loop2->Terminate(); });
144
145 thread1.join();
146 thread2.join();
147 }
148
TEST(GpuThreadMerger,LeaseExtension)149 TEST(GpuThreadMerger, LeaseExtension) {
150 fml::MessageLoop* loop1 = nullptr;
151 fml::AutoResetWaitableEvent latch1;
152 fml::AutoResetWaitableEvent term1;
153 std::thread thread1([&loop1, &latch1, &term1]() {
154 fml::MessageLoop::EnsureInitializedForCurrentThread();
155 loop1 = &fml::MessageLoop::GetCurrent();
156 latch1.Signal();
157 term1.Wait();
158 });
159
160 fml::MessageLoop* loop2 = nullptr;
161 fml::AutoResetWaitableEvent latch2;
162 fml::AutoResetWaitableEvent term2;
163 std::thread thread2([&loop2, &latch2, &term2]() {
164 fml::MessageLoop::EnsureInitializedForCurrentThread();
165 loop2 = &fml::MessageLoop::GetCurrent();
166 latch2.Signal();
167 term2.Wait();
168 });
169
170 latch1.Wait();
171 latch2.Wait();
172
173 fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
174 fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
175 const auto gpu_thread_merger_ =
176 fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
177 const int kNumFramesMerged = 5;
178
179 ASSERT_FALSE(gpu_thread_merger_->IsMerged());
180
181 gpu_thread_merger_->MergeWithLease(kNumFramesMerged);
182
183 // let there be one more turn till the leases expire.
184 for (int i = 0; i < kNumFramesMerged - 1; i++) {
185 ASSERT_TRUE(gpu_thread_merger_->IsMerged());
186 gpu_thread_merger_->DecrementLease();
187 }
188
189 // extend the lease once.
190 gpu_thread_merger_->ExtendLeaseTo(kNumFramesMerged);
191
192 // we will NOT last for 1 extra turn, we just set it.
193 for (int i = 0; i < kNumFramesMerged; i++) {
194 ASSERT_TRUE(gpu_thread_merger_->IsMerged());
195 gpu_thread_merger_->DecrementLease();
196 }
197
198 ASSERT_FALSE(gpu_thread_merger_->IsMerged());
199
200 term1.Signal();
201 term2.Signal();
202 thread1.join();
203 thread2.join();
204 }
205