1 // Copyright 2019 The Chromium 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 #include "components/viz/test/test_gpu_service_holder.h"
6 
7 #include <utility>
8 
9 #include "base/at_exit.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/command_line.h"
13 #include "base/macros.h"
14 #include "base/memory/singleton.h"
15 #include "base/no_destructor.h"
16 #include "base/synchronization/lock.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/test/task_environment.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "components/viz/service/gl/gpu_service_impl.h"
21 #include "gpu/command_buffer/service/service_utils.h"
22 #include "gpu/config/gpu_feature_info.h"
23 #include "gpu/config/gpu_info.h"
24 #include "gpu/config/gpu_info_collector.h"
25 #include "gpu/config/gpu_preferences.h"
26 #include "gpu/config/gpu_util.h"
27 #include "gpu/ipc/service/gpu_watchdog_thread.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "ui/base/ui_base_features.h"
30 #include "ui/gl/init/gl_factory.h"
31 
32 #if BUILDFLAG(ENABLE_VULKAN)
33 #include "gpu/vulkan/init/vulkan_factory.h"
34 #include "gpu/vulkan/vulkan_implementation.h"
35 #endif
36 
37 #if defined(USE_OZONE)
38 #include "ui/ozone/public/ozone_platform.h"
39 #endif
40 
41 namespace viz {
42 
43 namespace {
44 
GetLock()45 base::Lock& GetLock() {
46   static base::NoDestructor<base::Lock> lock;
47   return *lock;
48 }
49 
50 // We expect GetLock() to be acquired before accessing these variables.
51 TestGpuServiceHolder* g_holder = nullptr;
52 bool g_should_register_listener = true;
53 bool g_registered_listener = false;
54 
55 class InstanceResetter
56     : public testing::EmptyTestEventListener,
57       public base::test::TaskEnvironment::DestructionObserver {
58  public:
InstanceResetter()59   InstanceResetter() {
60     base::test::TaskEnvironment::AddDestructionObserver(this);
61   }
62 
~InstanceResetter()63   ~InstanceResetter() override {
64     base::test::TaskEnvironment::RemoveDestructionObserver(this);
65   }
66 
67   // testing::EmptyTestEventListener:
OnTestEnd(const testing::TestInfo & test_info)68   void OnTestEnd(const testing::TestInfo& test_info) override {
69     {
70       base::AutoLock locked(GetLock());
71       // Make sure the TestGpuServiceHolder instance is not re-created after
72       // WillDestroyCurrentTaskEnvironment().
73       // Otherwise we'll end up with GPU tasks weirdly running in a different
74       // context after the test.
75       DCHECK(!(reset_by_task_env && g_holder))
76           << "TestGpuServiceHolder was re-created after "
77              "base::test::TaskEnvironment was destroyed.";
78     }
79     reset_by_task_env = false;
80     TestGpuServiceHolder::ResetInstance();
81   }
82 
83   // base::test::TaskEnvironment::DestructionObserver:
WillDestroyCurrentTaskEnvironment()84   void WillDestroyCurrentTaskEnvironment() override {
85     reset_by_task_env = true;
86     TestGpuServiceHolder::ResetInstance();
87   }
88 
89  private:
90   bool reset_by_task_env = false;
91 
92   DISALLOW_COPY_AND_ASSIGN(InstanceResetter);
93 };
94 
95 }  // namespace
96 
97 // static
GetInstance()98 TestGpuServiceHolder* TestGpuServiceHolder::GetInstance() {
99   base::AutoLock locked(GetLock());
100 
101   // Make sure the global TestGpuServiceHolder is delete after each test. The
102   // listener will always be registered with gtest even if gtest isn't
103   // otherwised used. This should do nothing in the non-gtest case.
104   if (!g_registered_listener && g_should_register_listener) {
105     g_registered_listener = true;
106     testing::TestEventListeners& listeners =
107         testing::UnitTest::GetInstance()->listeners();
108     // |listeners| assumes ownership of InstanceResetter.
109     listeners.Append(new InstanceResetter);
110   }
111 
112   // Make sure the global TestGpuServiceHolder is deleted at process exit.
113   static bool registered_cleanup = false;
114   if (!registered_cleanup) {
115     registered_cleanup = true;
116     base::AtExitManager::RegisterTask(
117         base::BindOnce(&TestGpuServiceHolder::ResetInstance));
118   }
119 
120   if (!g_holder) {
121     g_holder = new TestGpuServiceHolder(gpu::gles2::ParseGpuPreferences(
122         base::CommandLine::ForCurrentProcess()));
123   }
124   return g_holder;
125 }
126 
127 // static
ResetInstance()128 void TestGpuServiceHolder::ResetInstance() {
129   base::AutoLock locked(GetLock());
130   if (g_holder) {
131     delete g_holder;
132     g_holder = nullptr;
133   }
134 }
135 
136 // static
DoNotResetOnTestExit()137 void TestGpuServiceHolder::DoNotResetOnTestExit() {
138   base::AutoLock locked(GetLock());
139 
140   // This must be called before GetInstance() is ever called.
141   DCHECK(!g_registered_listener);
142   g_should_register_listener = false;
143 }
144 
TestGpuServiceHolder(const gpu::GpuPreferences & gpu_preferences)145 TestGpuServiceHolder::TestGpuServiceHolder(
146     const gpu::GpuPreferences& gpu_preferences)
147     : gpu_thread_("GPUMainThread"), io_thread_("GPUIOThread") {
148   base::Thread::Options gpu_thread_options;
149 #if defined(USE_OZONE)
150   if (features::IsUsingOzonePlatform()) {
151     gpu_thread_options.message_pump_type = ui::OzonePlatform::GetInstance()
152                                                ->GetPlatformProperties()
153                                                .message_pump_type_for_gpu;
154   }
155 #endif
156 
157   CHECK(gpu_thread_.StartWithOptions(gpu_thread_options));
158   CHECK(io_thread_.Start());
159 
160   base::WaitableEvent completion;
161   gpu_thread_.task_runner()->PostTask(
162       FROM_HERE,
163       base::BindOnce(&TestGpuServiceHolder::InitializeOnGpuThread,
164                      base::Unretained(this), gpu_preferences, &completion));
165   completion.Wait();
166 }
167 
~TestGpuServiceHolder()168 TestGpuServiceHolder::~TestGpuServiceHolder() {
169   // Ensure members created on GPU thread are destroyed there too.
170   gpu_thread_.task_runner()->PostTask(
171       FROM_HERE, base::BindOnce(&TestGpuServiceHolder::DeleteOnGpuThread,
172                                 base::Unretained(this)));
173   gpu_thread_.Stop();
174   io_thread_.Stop();
175 }
176 
177 scoped_refptr<gpu::SharedContextState>
GetSharedContextState()178 TestGpuServiceHolder::GetSharedContextState() {
179   return gpu_service_->GetContextState();
180 }
181 
GetShareGroup()182 scoped_refptr<gl::GLShareGroup> TestGpuServiceHolder::GetShareGroup() {
183   return gpu_service_->share_group();
184 }
185 
ScheduleGpuTask(base::OnceClosure callback)186 void TestGpuServiceHolder::ScheduleGpuTask(base::OnceClosure callback) {
187   DCHECK(gpu_task_sequence_);
188   gpu_task_sequence_->ScheduleTask(std::move(callback), {});
189 }
190 
InitializeOnGpuThread(const gpu::GpuPreferences & gpu_preferences,base::WaitableEvent * completion)191 void TestGpuServiceHolder::InitializeOnGpuThread(
192     const gpu::GpuPreferences& gpu_preferences,
193     base::WaitableEvent* completion) {
194   DCHECK(gpu_thread_.task_runner()->BelongsToCurrentThread());
195 
196   if (gpu_preferences.use_vulkan != gpu::VulkanImplementationName::kNone) {
197 #if BUILDFLAG(ENABLE_VULKAN)
198     bool use_swiftshader = gpu_preferences.use_vulkan ==
199                            gpu::VulkanImplementationName::kSwiftshader;
200     bool is_non_ozone_x11 = false;
201 #if defined(USE_X11)
202     is_non_ozone_x11 = !features::IsUsingOzonePlatform();
203 #endif  // defined(USE_X11)
204 
205     if (!is_non_ozone_x11) {
206       // TODO(samans): Support Swiftshader on more platforms.
207       // https://crbug.com/963988
208       LOG_IF(ERROR, use_swiftshader) << "Unable to use Vulkan Swiftshader on "
209                                         "this platform. Falling back to "
210                                         "GPU.";
211       use_swiftshader = false;
212     }
213 
214     vulkan_implementation_ = gpu::CreateVulkanImplementation(use_swiftshader);
215     if (!vulkan_implementation_ ||
216         !vulkan_implementation_->InitializeVulkanInstance(
217             !gpu_preferences.disable_vulkan_surface)) {
218       LOG(FATAL) << "Failed to create and initialize Vulkan implementation.";
219     }
220 #else
221     NOTREACHED();
222 #endif
223   }
224 
225   // Always enable gpu and oop raster, regardless of platform and blocklist.
226   // The latter instructs GpuChannelManager::GetSharedContextState to create a
227   // GrContext, which is required by SkiaRenderer as well as OOP-R.
228   gpu::GPUInfo gpu_info;
229   gpu::GpuFeatureInfo gpu_feature_info = gpu::ComputeGpuFeatureInfo(
230       gpu_info, gpu_preferences, base::CommandLine::ForCurrentProcess(),
231       /*needs_more_info=*/nullptr);
232   gpu_feature_info.status_values[gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION] =
233       gpu::kGpuFeatureStatusEnabled;
234   gpu_feature_info.status_values[gpu::GPU_FEATURE_TYPE_OOP_RASTERIZATION] =
235       gpu::kGpuFeatureStatusEnabled;
236 
237   // TODO(sgilhuly): Investigate why creating a GPUInfo and GpuFeatureInfo from
238   // the command line causes the test SkiaOutputSurfaceImplTest.SubmitPaint to
239   // fail on Android.
240   gpu_service_ = std::make_unique<GpuServiceImpl>(
241       gpu::GPUInfo(), /*watchdog_thread=*/nullptr, io_thread_.task_runner(),
242       gpu_feature_info, gpu_preferences,
243       /*gpu_info_for_hardware_gpu=*/gpu::GPUInfo(),
244       /*gpu_feature_info_for_hardware_gpu=*/gpu::GpuFeatureInfo(),
245       /*gpu_extra_info=*/gfx::GpuExtraInfo(),
246 #if BUILDFLAG(ENABLE_VULKAN)
247       vulkan_implementation_.get(),
248 #else
249       /*vulkan_implementation=*/nullptr,
250 #endif
251       /*exit_callback=*/base::DoNothing());
252 
253   // Use a disconnected mojo remote for GpuHost, we don't need to receive any
254   // messages.
255   mojo::PendingRemote<mojom::GpuHost> gpu_host_proxy;
256   ignore_result(gpu_host_proxy.InitWithNewPipeAndPassReceiver());
257   gpu_service_->InitializeWithHost(
258       std::move(gpu_host_proxy), gpu::GpuProcessActivityFlags(),
259       gl::init::CreateOffscreenGLSurface(gfx::Size()),
260       /*sync_point_manager=*/nullptr, /*shared_image_manager=*/nullptr,
261       /*shutdown_event=*/nullptr);
262 
263   task_executor_ = std::make_unique<gpu::GpuInProcessThreadService>(
264       this, gpu_thread_.task_runner(), gpu_service_->GetGpuScheduler(),
265       gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
266       gpu_service_->gpu_channel_manager()
267           ->default_offscreen_surface()
268           ->GetFormat(),
269       gpu_service_->gpu_feature_info(),
270       gpu_service_->gpu_channel_manager()->gpu_preferences(),
271       gpu_service_->shared_image_manager(),
272       gpu_service_->gpu_channel_manager()->program_cache());
273 
274   // TODO(weiliangc): Since SkiaOutputSurface should not depend on command
275   // buffer, the |gpu_task_sequence_| should be coming from
276   // SkiaOutputSurfaceDependency. SkiaOutputSurfaceDependency cannot be
277   // initialized here because the it will not have correct client thread set up
278   // when unit tests are running in parallel.
279   gpu_task_sequence_ = task_executor_->CreateSequence();
280 
281   completion->Signal();
282 }
283 
DeleteOnGpuThread()284 void TestGpuServiceHolder::DeleteOnGpuThread() {
285   task_executor_.reset();
286   gpu_task_sequence_.reset();
287   gpu_service_.reset();
288 }
289 
290 }  // namespace viz
291